var CONTEXT_NAME;

if(document.domain.indexOf("dev") >= 0) {
	CONTEXT_NAME = "/wps/PA_s7reyjl";
	setCookie("j_context_name", CONTEXT_NAME);
}
else {
	CONTEXT_NAME = "/wps/PA_1_Q7JFDFH20G66902PJT4SHT24V6";
	setCookie("j_context_name", CONTEXT_NAME);
}

function getContextName(){
    // TODO cookie를 안쓸 경우엔 무조건 값이 "" 이 되어 버림.
    if (CONTEXT_NAME == null){
    	CONTEXT_NAME = getCookie("j_context_name");
    }
    return CONTEXT_NAME;
}

// 월별 마지막 날짜를 윤년이 아닌해와 윤년인 해로 구분하여 배열로 작성
var leap = new Array();
leap[0] = new Array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
leap[1] = new Array(31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);

// 주어진 연이 윤년인지 아닌지를 판단한다.
function isLeap(year) {
    return (( year % 4 == 0 ) && ( year % 100  != 0 ) || ( year % 400 == 0 )) 
                 ? 1 : 0;
}

// 날짜를 나타내는 객체
function Datetime(y, m, d) {
    this.year = y;
    this.month = m;
    this.day = d;

    this.compare = function(dt) {
        return parseInt(this.toString()) - parseInt(dt.toString());
    };

    this.toString = function() {
    	this.validateLength();
        return (this.year + "-" + this.month + "-"  + this.day);
    };

    this.validateLength = function() {
        this.year = ("0000" + this.year).substring((new String(this.year)).length);
        this.month = ("00" + this.month).substring((new String(this.month)).length);
        this.day = ("00" + this.day).substring((new String(this.day)).length);
    };

    this.clone = function() {
        return new Datetime(this.year, this.month, this.day);
    };
}

var ol = new Array();


// 현재 날짜 또는 사용자가 선택할 날짜


// 페이지가 로드될 때 달력을 구성한다.
// ui:date2 는 <input type="text" /><a><img/></a><span class="dateSpan"/> 구조의 HTML을 뿌린다.
// elt는 <span class="dateSpan"/> 엘리먼트
function DateObj(elt) {
	var today;
	var element;
	var inputElt;
	
	this.element = elt;
	this.inputElt = elt.previousSibling.previousSibling; // <input> 엘리먼트
	
	ol[elt.id] = this;

    this.run = function(daystring){
        this.today = new Datetime(daystring.substring(0,4), 
                                  daystring.substring(4,6), 
                                  daystring.substring(6,8));
        this.drawCalendar(this.today);
	    this.setYear(this.today);
	    this.setMonth(this.today);
	    this.setDay(this.today);
	}

	// 년도를 변경한다.
	this.changeYear = function(number) {
	    this.today.year = parseInt(this.today.year) + number;
	    this.setYear(this.today);
	    this.drawCalendar(this.today);
	    this.setDay(this.today);
	}

	// 월을 변경한다.
	this.changeMonth = function() {
	    this.today.month = this.firstSelectEltByName("month").value;
	    this.setMonth(this.today);
	    this.drawCalendar(this.today);
	    this.setDay(this.today);
	}

	// 날짜를 변경하다.
	this.changeDay = function(evt) {
	    evt = (evt) ? evt : window.event;
	    var td = (evt.target) ? evt.target : evt.srcElement;
	    
	    // <font> 요소에 대한 처리
	    if (td.id == null || td.id == "") {
	        td = td.parentNode;
	    }
	
	    var tdIndex = td.id.substring(2 + this.element.id.length);
	
	    var firstDay = new Date(this.today.year, this.today.month - 1, 1);
	
	    if (this.today.day != (tdIndex - firstDay.getDay())) {
	        this.resetDay(this.today);
	        this.today.day = parseInt(tdIndex) - firstDay.getDay();
	        this.setDay(this.today);

	        this.inputElt.value = this.today.toString();
	    }
	}
	
	// 달력을 구성한다.
	this.drawCalendar = function(date) {
	    var firstDay = new Date(date.year, date.month - 1, 1);
	    var td;
	    for (var i = 1; i <= 42; i++) {
	        td = document.getElementById("c_" + this.element.id + i);
	        td.innerHTML = ' ';
	        td.style.backgroundColor = '#FFFFFF';
	    }
	
	    var maxMonth = leap[isLeap(date.year)][date.month - 1];
	    date.day = Math.min(date.day, maxMonth);
	    for (var i = 0; i < maxMonth; i++) {
	    
	        td = document.getElementById("c_" + this.element.id + (i + 1 + firstDay.getDay()));
	    
			td.innerHTML = (i + 1);
	        td.style.cursor = 'pointer';
	    }
	}
	
	// 선택한 년도를 지정한다.
	this.setYear = function(date) {
		var yr = this.firstInputEltByName("year");
	    yr.value = date.year;
	}
	
	// 선택한 월을 지정한다.
	this.setMonth = function(date) {
		var mnth = this.firstSelectEltByName("month");
	    mnth.selectedIndex = date.month - 1;
	}
	
	// 선택한 날짜를 지정한다.
	this.setDay = function(date) {
		var firstDay = new Date(date.year, date.month - 1, 1);
	    var dateIndex = parseInt(date.day) +  parseInt(firstDay.getDay());
	    var td = document.getElementById("c_" + this.element.id + dateIndex);
	}
	
	// 선택했던 날짜의 배경을 초기화한다.
	this.resetDay = function(date) {
	    var firstDay = new Date(date.year, date.month - 1, 1);
	    var dateIndex = parseInt(date.day) + parseInt(firstDay.getDay());
	    var td = document.getElementById("c_" + this.element.id + dateIndex);
	}
	
	// 날짜를 선택한다.
	this.done = function() {
	    this.today.validateLength();
	    this.inputElt.value = this.today.toString();
	    this.hideDate();
	    return;
	}
	
	// 날짜를 초기화하고 끝낸다.
	this.clearDatetime = function() {
		var initial = this.firstInputEltByName("initialValueOfDate").value;
	    this.inputElt.value = new Datetime(initial.substring(0,4), 
                                           initial.substring(4,6), 
                                           initial.substring(6,8));
	    this.hideDate();
	    return false;
	}
	
	this.hideDate = function(){
	    this.element.removeChild(this.element.firstChild);
	}
	
	this.firstInputEltByName = function (name){
		return firstEltByName(this.element, name, "input");
	}
	
	this.firstSelectEltByName = function (name){
		return firstEltByName(this.element, name, "select");
	}
}

function dateDisplay(elt){
	var spanElt = elt.nextSibling; // 바로 뒤의 <div> 태그를 얻음.
	var inputElt = elt.previousSibling;
	var id = spanElt.id;
	
	if (spanElt.innerHTML == "" || spanElt.getElementsByTagName("span")[0].style.display == "none"){
		var params = "id=" + id;
		if (inputElt.value != "") params += "&date=" + inputElt.value.replace(/-/g, "")
		
		new Ajax.Updater(id,
	          getCookie("j_context_name") + "/popup/DateInclude.jsp", 
	          {method: 'get', parameters: params, onComplete: function(request){
		          		var initial = firstEltByName(spanElt, "initialValueOfDate", "input").value; 
					    ol[id] = new DateObj(spanElt);
					    ol[id].run(initial);
					}
	          });
    }else{
        getDateObj(spanElt.id).hideDate();
    }
}

function getDateObj(id){
    return ol[id];
}

function firstEltByName(elt, name, tagName){
	var all = elt.getElementsByTagName(tagName);
	for(var i = 0; i < all.length; i++){
  		if (all[i].name == name) break;
  	}
	if (i < all.length) {
		return all[i];
	}
	return null;
}

var global = new JGlobal();

var messages = new JMessage();

function JGlobal() {
    this.pageNo = 1;    
    this.help
    this.query;
    this.orderIndex;
    this.orderTheadIndex;
    this.orderMode;
    this.orderColumn;
}

function JMessage() {
    this.init;
    this.messages;
    
    this.get = function(id, args) {
                   if (!this.init) {
                       this.messages = new Array();
                       this.messages["JSM-1001"] = "저장하시겠습니까?";
                       this.messages["JSM-1002"] = "아이디";
                       this.messages["JSM-1003"] = "삭제하시겠습니까?";
                       this.messages["JSM-1004"] = "{0} 입력해야 합니다.";
                       this.messages["JSM-1005"] = "{0}의 크기는 {1}이여야 합니다";
                       this.messages["JSM-1006"] = "{0}의 크기는 {1}과 {2} 사이여야 합니다";
                       this.messages["JSM-1007"] = "{0}에는 다음(', ., \") 특수 문자는 입력할 수 없습니다";
                       this.messages["JSM-1008"] = "{0}에는 숫자만을 입력해야 합니다";
                       this.messages["JSM-1009"] = "{0}의 숫자는 {1}이여야 합니다";
                       this.messages["JSM-1010"] = "{0}의 숫자는 {1} {2} 사이여야 합니다";
                       this.messages["JSM-1011"] = "{0} 선택해야 합니다";
                       this.messages["JSM-1012"] = "선택할 수 있는 {0}의 수는 {1} 입니다";
                       this.messages["JSM-1013"] = "선택할 수 있는 {0}의 수는 {1} {2} 사이여야 합니다";
                       this.messages["JSM-1014"] = "{0}의 금액은 {1}이여야 합니다";
                       this.messages["JSM-1015"] = "{0}의 금액는 {1} {2} 사이여야 합니다";
                       this.messages["JSM-1016"] = "{0} 올바르게 입력하여 주십시오";
                       this.messages["JSM-1017"] = "입력하신 {0} 전자우편주소로 유효하지 않습니다. 다시 입력하여 주십시오!";
                       this.messages["JSM-1018"] = "입력하신 {0} 날짜로 유효하지 않습니다. 다시 입력하여 주십시오!";
                       this.messages["JSM-1019"] = "{0}의 날짜는 {1} 이전 이어야 합니다";
                       this.messages["JSM-1020"] = "{0}의 날짜는 {1} 이후 이어야 합니다";
                       this.messages["JSM-1021"] = "선택할 수 있는 {0}의 날짜는 {1} {2} 사이여야 합니다";
                       this.messages["JSM-1022"] = "올바른 파일명이 아닙니다. 파일을 다시 입력해 주시기 바랍니다";
                       this.messages["JSM-1023"] = "{0}에 대한 파일을 첨부해야 합니다";
                       this.messages["JSM-1024"] = "{0}에 대한 첨부 가능한 파일의 확장자는 {1} 입니다";
                       this.messages["JSM-1025"] = "{0}에 대하여 확장자가 {1}인 파일은 첨부가 금지되어 있습니다";
                       this.messages["JSM-1026"] = "입력하신 {0} Crontab 표현식으로 유효하지 않습니다. 다시 입력하여 주십시오!";
                       this.messages["JSM-1027"] = "{0}의  {1} 올바른 속성 표현식이 아닙니다";
                       this.messages["JSM-1028"] = "{0}의 속성  {1} 필요합니다";
                       this.messages["JSM-1029"] = "관련 답글도 함께 삭제됩니다. 삭제하시겠습니까?";
                       this.init = true;
                   }
                   var message = this.messages[id];
                   if (!message) {
                       return id;
                   }
                   if (args) {
                       if (typeof args == "object" && args.length) {
                           for (var i = 0; i < args.length; i++) {
                               var pattern = new RegExp("\\{" + i + "\\}", "g"); 
                               message = message.replace(pattern, args[i]);
                           }
                       } else {
                           message = message.replace(/\{0\}/g, args);
                       }
                   }
                   return message;
               };

    this.alert = function(id, args) {
                     alert(this.get(id, args));
                 }
}

function JAdd() {
    this.params;

    this.param = jcontrol_param;
    
    this.addParam = jcontrol_addParam;

    this.run = function() {
                   var nextPage = jcontrol_getJspName() + "Form.jsp";
                   var query = jcontrol_getQueryString();
                   if (query != "") {
                       nextPage += "?" + query;
                   }
                   if (this.params) {
                       if (query != "") {
                           nextPage += "&" + this.params;
                       } else {
                           nextPage += "?" + this.params;
                       }
                   }
                   window.location.href = encodeURI(nextPage);
               };

}

function JAjax() {
    this.params;
    
    this.addParam = jcontrol_addParam;

    this.mode = function(mode) {
                    return this.addParam("mode", mode);
                };

    this.run = function(element, url, params) {
                   JLoadingMessage.show(element);
                   if (params) {
                       var callback = params.onComplete;
                       params.onComplete = function(originalRequest) {
                                            JLoadingMessage.hide();
                                            if (callback) {
                                                callback(originalRequest);
                                            }
                                        };
                       if (this.params) {
                           params.parameters = this.params;
                       }
                   } else if (this.params) {
                       params = {parameters: this.params, onComplete: JLoadingMessage.hide};
                   }
                   new Ajax.Request(url, params);
               };


    this.update = function(element, url, target, params) {
                      JLoadingMessage.show(element);
                      if (params) {
                          var callback = params.onComplete;
                          params.onComplete = function(originalRequest) {
                                                  JLoadingMessage.hide();
                                                  if (callback) {
                                                      callback(originalRequest);
                                                  }
                                              };
                          if (this.params) {
                              params.parameters = this.params;
                          }
                      } else if (this.params) {
                          params = {parameters: this.params, onComplete: JLoadingMessage.hide};
                      }
                      new Ajax.Updater(target, url, params);
                  };
}

function JBehind() {
    this.formIndex = 0;

    this.form = jcontrol_form;

    this.run = function(mode) {
                   if (window.validate && !validate(mode)) {
                       return;
                   }
                   var form = document.forms[this.formIndex];
                   form.action = form.action + jcontrol_getQuery();
                   jcontrol_addMode(form, mode);
                   form.submit();
               };
}

function JClose() {
    this.run = function() {
                   window.close();
               };
}

//TODO after 함수에 기본적으로 적용될 파라미터는?
//TODO firefox가 modal 창을 IE처럼 지원하지 않아 편법을 썼음. 리팩토링의 여지가 있음. 이와 관련 참고 참고 : http://www.gtalbot.org/BugzillaSection/Bug195867GDR_WindowOpen.html

var jcontrol_tempDatePopupMethod = null;

function JDatePopup() {
    this.after;
    this.popup = function(event, element, after) {
                     var url = getContextName() + "/popup/DatePopup.jsp";
                     var xfield = element.previousSibling;
                     var _width = 340, _height = 340;
                     /* IE */
                     if (window.showModalDialog) {
                         var reIE = new RegExp("MSIE (\\d+\\.\\d+);");
                         reIE.test(navigator.userAgent);
                         var fIEVersion = parseFloat(RegExp["$1"]);
                   		 if (fIEVersion < 7) {
                   		     _width = 375;
                   		     _height = 375;
                     	 }
                         var ws = jcontrol_formatModalWindow(Event.pointerX(event) + window.screenLeft - 105,
                                                    Event.pointerY(event) + window.screenTop + 10,
                                                    _width, _height);
                         var dt = showModalDialog(url, window, ws);
                         if (dt != null) {
                             if (dt.year == "") {
                                 xfield.value = "";
                             } else {
                                 //xfield.value = dt.year + "-" + dt.month + "-" + dt.day;
                                 xfield.value = dt.year + "" + dt.month + "" + dt.day;
                             }
                         }
                         if (after) after();
                     } else {
                         var ws = jcontrol_formatModalWindow(Event.pointerX(event) + window.screenX,
                                                    Event.pointerY(event) + window.screenY,
                                                    _width, _height);
                         url += "?formName=" + xfield.form.getAttribute("name") + "&fieldName=" + xfield.name;
                         var win1 = window.open(url, "date", ws);
                         jcontrol_tempDatePopupMethod = after;
                     }
                 };
}

function addEvent(obj, evType, fn){
try{
 if (obj.addEventListener){
    obj.addEventListener(evType, fn, false);
    return true;
 } else if (obj.attachEvent){
    var r = obj.attachEvent("on"+evType, fn);
    return r;
 } else {
    return false;
 }
 }catch(e){alert("catch");}
}

function JDelete() {
    this.formIndex = 0;
    this.idValue = "id";

    this.form = jcontrol_form;

    this.id = function(id) {
                  this.idValue = id;
                  return this;
              };

    this.run = function() {
                   var form = document.forms[this.formIndex];
                   if (new JCheck(messages.get("JSM-1002"), form[this.idValue]).validate()
                           && confirm(messages.get("JSM-1003"))) {
                       jcontrol_addMode(form, "delete");
                       form.action = form.action + jcontrol_getQuery();
                       form.submit();
                   }
               };
}

function JExcel() {
    this.formIndex = 0;

    this.form = jcontrol_form;

    this.run = function(mode) {
                   if (!mode) {
                       mode = "excel";
                   }
                   var form = document.forms[this.formIndex];
                   var input = jcontrol_addMode(form, mode);
                   form.submit();
                   form.removeChild(input);
               };
}

function JHelp() {
    this.targetValue;

    this.target = function(target) {
                      this.targetValue = target;
                      return this;
                  };

    this.run = function(id) {
                   var target = "help-message";
                   if (this.targetValue) {
                       target = this.targetValue;
                   }
                   if (!$(target)) {
                       alert(target + "를 아이디로 하는 태그가 없습니다.");
                       return;
                   }
                   if (global.help == null || global.help != id) {
                       global.help = id;
                       var onComplete = function() {
                                            $(target).style.display = "block";
                                        }
                       new Ajax.Updater(target,
                                        getContextName() + "/HelpAjax.do?id=" + id,
                                        {asynchronous:true, onComplete:onComplete});
                   } else {
                       if ($(target).style.display == "block") {
                           $(target).style.display = "none";
                           global.help =  null;
                           return;
                       }
                   }
               };
}

function JLink() {
    this.nextPage;
    this.params;

    this.next = function(page) {
                    this.nextPage = page;
                    return this;
                }

    this.param = jcontrol_param;

    this.addParam = jcontrol_addParam;

    this.run = function(id) {
                   if (!this.nextPage) {
                       this.nextPage = jcontrol_getJspName() + "Form.jsp";
                   }
                   var part = "";
                   if (arguments.length == 1) {
                       part += "p_id=" + id;
                   } else {
                       for (var i = 0; i < arguments.length; i++) {
                           part += "&p_id" + (i + 1) + "=" + arguments[i];
                       }
                   }
                   if (this.params) {
                       part += "&" + this.params;
                   }
                   var query = jcontrol_getQueryString();
                   if (query != "") {
                       part += "&" + query;
                   }
                   window.location.href = encodeURI(this.nextPage + "?" + part.replace(/^&/, ""));
               };
}

function JList() {
    this.run = function() {
                   var nextPage = jcontrol_getJspName() + "List.jsp";
                   window.location.href = nextPage + jcontrol_getQuery();
               };
}

function JLoad() {
    this.formIndex = 0;

    this.form = jcontrol_form;

    this.run = function() {
                   var query = window.location.search;
                   query = decodeURI(query);
                   query = query.replace(/^\?(&)*/, "");
                   query = query.split("&");
                   var form = document.forms[this.formIndex];
                   for (var i = 0; i < query.length; i++) {
                       var index = query[i].indexOf("=");
                       var name = query[i].substring(0, index);
                       var value = query[i].substring(index + 1);
                       if (name.substring(0, 2) == "q_") {
                           if (form[name]) {
                               if (form[name].length){ // 배열로 전달되면
                                   for (var j = 0; j < form[name].length; j++) {
                                       if (form[name][j].value == value) {
                                           if (form[name][0].nodeName.toLowerCase() == "option") {
                                               form[name][j].selected = true;
                                           } else {
                                               form[name][j].checked = true;
                                           }
                                       }
                                   }
                               } else { // 배열이 아닌 input이면
                                   form[name].value = value;
                               }
                           } // if (form[name])
                       } else {
                           if (name == "orderIndex") {
                               global.orderIndex = value;
                           } else if (name == "orderTheadIndex") {
                               global.orderTheadIndex = value;
                           } else if (name == "orderBy") {
                               global.orderColumn = value;
                           } else if (name == "orderMode") {
                               global.orderMode = value;
                           } else if (name == "pageNo") {
                               global.pageNo = value;
                           }
                       }
                   }
                   jcontrol_sortToggle();
               };
}

var JLoadingMessage = {
    show : function(target) {
               var id = "loading-message";
               if (!$(id)) {
                   var div = document.createElement('div');
                   div.id = id;
                   document.body.appendChild(div);
                   Element.hide(id);
                   new Insertion.Bottom(id, "<img src='" + getContextName() + "/ext/image/loading.gif'/>");
                   $(id).style.position = "absolute";
               }
               var dim = Element.getDimensions(id);
               var targetDim = Element.getDimensions(target);
               var offsetTop, offsetLeft;
               offsetTop = (targetDim.height - dim.height) / 2;
               offsetLeft = (targetDim.width - dim.width) / 2;

               Position.clone(target,id,
                              {setLeft: true,
                               setTop: true,
                               setWidth: false,
                               setHeight: false,
                               offsetTop: offsetTop,
                               offsetLeft: offsetLeft
                              });
               Element.show(id);
           },

    hide: function() {
              var id = "loading-message";
              if ($(id)) {
                  Element.hide(id);
                  jcontrol_sortToggle();
              }
          }
};

function JNavigation() {
    this.params;

    this.run = function(pageNo, params) {
                   global.pageNo = pageNo;
                   if (params && params.pageNo) {
                       document.forms[0][params.pageNo].value = pageNo;
                   }
                   var nextPage = JNavigation.nextPage ? JNavigation.nextPage + "?" : 
                                                         jcontrol_getJspName() + "List.jsp?";
                   global.query = nextPage + jcontrol_getQueryString();
                   if (isAjax) {
                       var target = "list";
                       var mode = "&mode=ajaxList";
                       if (params) {
                           if (params.target) {
                               target = params.target;
                           }
                           if (params.param) {
                               global.query += ("&" + params.param);
                           }
                           if (params.mode) {
                               mode = "&mode=" + params.mode;
                           }
                       }
                       JLoadingMessage.show(target);
                       new Ajax.Updater(target, global.query + mode, {asynchronous:true, onComplete:JLoadingMessage.hide});
                   } else {
                       if (params) {
                           if (params.param) {
                               global.query += ("&" + params.param);
                           }
                       }
                       window.location.href = encodeURI(global.query);
                   }
               };
}

function JSave() {
    this.formIndex = 0;

    this.form = jcontrol_form;

    this.run = function() {
                   if (window.validate && !validate("SAVE")) {
                           return;
                   }
                   if (confirm(messages.get("JSM-1001"))) {
                       var form = document.forms[this.formIndex];
                       form.action = form.action + jcontrol_getQuery();
                       var mode = "create";
                       if (window.location.search.indexOf("p_id") > 0 || 
                               form.getElementsByTagName("input")[0].type == "hidden") {
                           mode = "update";
                       }
                       jcontrol_addMode(form, mode);
                       form.submit();
                   }
               };
}

function JSearch() {
    this.run = function(params) {
                   new JNavigation().run(1, params);
               };
}

function JSort() {
    this.run = function(columnName, element, params) {
                   var row = element.parentNode.parentNode;
                   var elements = row.getElementsByTagName("th");
                   for (var i = 0; i < elements.length; i++) {
                       if (element.parentNode == elements[i]) {
                           global.orderIndex = i;
                           break;
                       }
                   }
                   var thead = row.parentNode;
                   elements = document.getElementsByTagName("thead");
                   for (var i = 0; i < elements.length; i++) {
                       if (thead == elements[i]) {
                           global.orderTheadIndex = i;
                           break;
                       }
                   }
                   global.orderColumn = columnName;
                   if (global.orderMode) {
                       global.orderMode = (global.orderMode == "ASC") ? "DESC" : "ASC";
                   } else {
                       global.orderMode = "DESC";
                   }
                   if (!global.pageNo) {
                       global.pageNo = 1;
                   }
                   new JNavigation().run(global.pageNo, params);
               };
}

function jcontrol_addMode(form, mode) {
    if (!form.mode) {
        var input = document.createElement("input");
        input.name = "mode";
        input.type = "hidden";
        input.value = mode;
        form.appendChild(input); // MS IE에서는 추가된 INPUT에 바로 접근할 수 없음.
        return input;
    } else {
        form.mode.value = mode;
        return form.mode;
    }
}

function jcontrol_addParam(name, value) {
    if (this.params) {
        this.params += "&";
    } else {
        this.params = "";                            
    }
    this.params += (name + "=" + value);
    return this;
}

function jcontrol_form(index) {
    this.formIndex = index;
    return this;
}

function jcontrol_formatModalWindow(left, top, width, height) {
    if (window.showModalDialog) {
        return "status:no; dialogLeft:" + left + "px; dialogTop:" + top + 
               "px; dialogWidth:" + width + "px; dialogHeight:" + height + "px";
    } else {
        return "modal=yes, left=" + left + "px, top=" + top + ", width=" + width + "px, height=" + height + "px";
    }
}

function jcontrol_getJspName() {
    var path = window.location.pathname;
    if (path.indexOf("Popup.jsp") > 0) {
        return path.replace("Popup.jsp", "Popup");
    } else if (path.indexOf("List.jsp") > 0) {
        return path.replace("List.jsp", "");
    } else if (path.indexOf("Form.jsp") > 0) {
        return path.replace("Form.jsp", "");
    } else if (path.indexOf("Detail.jsp") > 0) {
        return path.replace("Detail.jsp", "");
    }
    return path;
}

function jcontrol_getQuery() {
    var result = "";
    var query = window.location.search;
    if (!query || query.length == 0) {
        return result;
    }
    
    query = query.replace(/^\?(&)*/, "");
    query = query.split("&");
    for (var i = 0; i < query.length; i++) {
        if (query[i].substring(0, 2) != "p_") {
            if (result != "") {
                result += "&";
            } else {
                result += "?";
            }
            result += query[i];
        }
    }
    return result;
}

function jcontrol_getQueryString() {
    var result = "";
    var queryTags = document.getElementsByTagName("input");
    
    for (var i = 0; i < queryTags.length; i++) {

        var name = queryTags[i].name;
        var type = queryTags[i].type;
        var checked = queryTags[i].checked;
        
		if (name && name.substring(0, 2) == "q_") {
		    if (!((type == "checkbox" || type == "radio") && checked == false)){		                
	    		if (queryTags[i].value != null && queryTags[i].value != "") {
	                result += "&" + name + "=" + queryTags[i].value;
	            }		    
		    }  
		}     
    }

    queryTags = document.getElementsByTagName("select");
    for (var i = 0; i < queryTags.length; i++) {
        var name = queryTags[i].name;
        if (name && name.substring(0, 2) == "q_") {
            if (queryTags[i].value != null && queryTags[i].value != "") {
                result += "&" + name + "=" + queryTags[i].value;
            }
        }
    }
    
    if (global.pageNo != 1) {
        result += "&pageNo=" + global.pageNo;
    }
    
    if (global.orderColumn) {
        result += "&orderBy=" + global.orderColumn +
                      "&orderMode=" + global.orderMode +
                          "&orderIndex=" + global.orderIndex +
                          "&orderTheadIndex=" + global.orderTheadIndex;
    }
    return result != "" ? result.replace(/^&/, "") : result;
}

function jcontrol_param() {
    for (var i = 0 ; i < arguments.length; i++) {
        if (i == 0) {
            if (!this.params) {
                this.params = "";
            }
        }
        this.params += "&p_value" + (i + 1) + "=" + arguments[i];
    }
    return this;
}

function jcontrol_sortToggle() {
    if (global.orderColumn) {
        var thead = document.getElementsByTagName("thead")[global.orderTheadIndex];
        if (thead) {
            var elements = thead.getElementsByTagName("th");
            if (global.orderMode == "DESC") {
                elements[global.orderIndex].className = "desc";
            } else {
                elements[global.orderIndex].className = "asc";
            }
        }
    }
}

/**
 * Ajax를 이용한 요청을 수행한다.
 *
 * @url 요청 URL로 상대 경로와 절대 경로가 모두 가능함
 * @params 요청 파라미터
 * @theMode 모드로 호출할 메소드의 이름을 의미함. IAction에 대한 요청인 경우에는 ""를 입력
 * @after 서버에서의 처리가 완료된 후에 호출되는 메소드
 */
function callAjax(url, params, theMode, after, options) {
    jprivate_callMethod = after;
    jprivate_mode = theMode;

	jprivate_returnValue = null;
	
    var _async = (options && options["async"] == false)? false : true; // async 여부
    var _method = (options && options["method"] == "POST" )? "POST" : "GET"; // GET or POST
	var _params;
	if (params){
		_params = (typeof params == "string")? encodeURI(params) : jprivate_parseParam(params);
	}
	url += "?mode=" + theMode;
    
    /* 웹 브라우저에 따른 분기 */
    if (window.XMLHttpRequest) {                             /* 모질라 계열 */
        jprivate_request = new XMLHttpRequest();
        jprivate_request.onreadystatechange = jprivate_process;
        if (_method == "GET"){
            jprivate_request.open(_method, url + "&" + _params, _async);
            jprivate_request.send(null);
        }else{
            jprivate_request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
            jprivate_request.open(_method, url, _async);
            jprivate_request.send(_params);
        }
        if (_async == false && navigator.userAgent.toLowerCase().indexOf("firefox") > -1){
        	jprivate_process();
        }
    } else if (window.ActiveXObject) {                       /* MS IE */
        jprivate_request = new ActiveXObject("Microsoft.XMLHTTP");
        jprivate_request.onreadystatechange = jprivate_process;
        if (_method == "GET"){
            jprivate_request.open(_method, url + "&" + _params, _async);
            jprivate_request.send();
        }else{
            jprivate_request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
            jprivate_request.open(_method, url, _async);
            jprivate_request.send(_params);
        }
    }
    
    /*
    var style = "font: 12px 굴림, Tahoma, sans-serif;";
    style += "position: absolute; top: 6%; right: 5%;";
    style += "color: #fff; background-color: #008aff;";
    
    jprivate_message = (top) ? top.document.createElement("div") : document.createElement("div");
    jprivate_body = (top) ? top.document.getElementsByTagName("body")[0] : document.getElementsByTagName("body")[0];
    jprivate_message.innerHTML = "<span style=\"" + style + "\">작업중입니다...</span>";
    jprivate_body.appendChild(jprivate_message);
    */
    
    if (_async == false){ // synchronous 방식일 경우 after 함수의 리턴값을 되돌림
    	return jprivate_returnValue;
    }    
}

/**
 * 서버에서 Ajax 요청이 처리가 된 후 호출되는 메소드로 간단한 에러 처리를 한다. 성공적으로 수행된 경우에는
 * 사용자가 지정한 callMethod를 호출한다. 
 */
function jprivate_process() {
    try {
    	if (jprivate_request.readyState == 4) {
            //jprivate_body.removeChild(jprivate_message);
            if (jprivate_request.status == 200) {
                jprivate_returnValue = jprivate_callMethod(jprivate_request, jprivate_mode);
            } else {
                alert("예외가 발생하였습니다. (메시지=" + jprivate_request.statusText + ")");
            }
        }
    } catch (e) {
        alert("예외가 발생하였습니다. (메시지=" + e.message + ")");
    }
}

function getParams(select, name) {
    var size = select.options.length;
    var result = "";
    for (var i = 0; i < size; i++) {
        if (select.options[i].selected) {
            result += "&" + name + "=" + select.options[i].value
        }
    }
    return result;
}

/**
 * item 태그의 내용을 나타내는 클래스
 *
 * @param id 아이디
 * @param properties 속성 리스트
 */
function JItem(id, properties) {
    this.id = id;
    this.properties = properties;

    this.getProperty = function(name) {
                           return properties[name]; 
                       };
}

/**
 * id에 해당하는 item 태그의 내용을 JItem 객체로 반환한다.
 *
 * @param element response 태그를 나타내는 DOM 객체
 * @param id 아이디. 아이디가 없는 경우에는 첫번째 item 태그가 반환된다.
 */
function getItem(element, id) {
    var items = element.getElementsByTagName("item");
    for (var i = 0; i < items.length; i++) {
        if (id == null || items[i].getAttribute("id") == id) {
            var properties = items[i].getElementsByTagName("property");
            var args = new Array;
            for (var j = 0; j < properties.length; j++) {
                args[properties[j].getAttribute("name")] = properties[j].getAttribute("value");
            }
            return new JItem(id, args);
        }
    }
    return;
}

/**
 * id에 해당하는 list 태그의 내용을 JItem 객체를 담고 있는 배열로 반환한다.
 *
 * @param element response 태그를 나타내는 DOM 객체
 * @param id 아이디. 아이디가 없는 경우에는 첫번째 list 태그가 반환된다.
 */
function getList(element, id) {
    var lists = element.getElementsByTagName("list");
    for (var i = 0; i < lists.length; i++) {
        if (id == null || lists[i].getAttribute("id") == id) {
            var result = new Array;
            var items = lists[i].getElementsByTagName("item");
            for (var j = 0; j < items.length; j++) {
                var properties = items[j].getElementsByTagName("property");
                var args = new Array;
                for (var k = 0; k < properties.length; k++) {
                    args[properties[k].getAttribute("name")] = properties[k].getAttribute("value");
                }
                result[j] = new JItem(j, args);
            }
            return result;
        }
    }
    return new Array;
}



/*

    아래는 리스트를 새롭게 그리를 자바 스크립트 함수

*/

/**
 * 테이블 리스트를 그린다.
 * 
 * @param id tbody 태그의 아이디
 * @param size 테이블의 칼럼 수
 * @param countMessage 총 리스크 수를 표현하는 메시지
 */
function renderList(id, size, countMessage) {
    var table = new JTable(id, size, new JTableFormat());    
    jprivate_clearTbody(id);
    var response = jprivate_request.responseXML.documentElement;
    var list = getList(response);
    for (var i = 0; i < list.length; i++) {
        table.createRow(list[i]);
    }
    jprivate_renderNavigation(response, countMessage);
}

/**
 * 테이블을 포멧하는 클래스
 */
function JTableFormat() {
    this.formatCell = formatCell;
}

/**
 * tbody 태그의 모든 tr 태그를 지운다.<b> 
 *
 * @param tbodyId tbody 태그의 아이디
 */
function jprivate_clearTbody(tbodyId) {
    var body = document.getElementById(tbodyId);
    if (!body) {
        alert("tbody 태그의 아이디를 지정해야 합니다.");
    }
    var rows = body.getElementsByTagName("tr");
    for (var i = rows.length; i > 0; i--) {
        body.deleteRow(i - 1);
    }
}

/**
 * 리스트의 네비게이션 바를 동적으로 그리는 함수.
 * @param response XML의 response 태그를 나타내는 element
 * @param countMessage 총 리스크 수를 표현하는 메시지
 */
function jprivate_renderNavigation(response, countMessage) {
    var navigation = document.getElementById("navigationArea");
    if (!navigation) {
        return;
    }
    jprivate_tabSize = 10;
    jprivate_pageNo = document.forms[0].pageNo.value;
    jprivate_totalSize = response.getAttribute("totalSize");
    jprivate_listSize = response.getAttribute("listSize");

    var html = "";
    if (jprivate_lastPage() > 1) {
        html += "<a href='javascript:document.forms[0].pageNo.value=1;movePage()'>[처음]</a>&nbsp;";
    }
    if (jprivate_hasPreviousTab()) {
        html += "<a href='javascript:document.forms[0].pageNo.value=" + (jprivate_firstPageInTab() - 1) + ";movePage()'>[이전]</a>&nbsp;";
    }
    for (var i = jprivate_firstPageInTab(); i <= jprivate_lastPageInTab(); i++) {
        if (jprivate_pageNo == i) {
            html += "<strong>" + i + "</strong>&nbsp;";
        } else {
            html += "<a href='javascript:document.forms[0].pageNo.value=" + i + ";movePage()'>" + i + "</a>&nbsp;";
        }
    }
    if (jprivate_hasNextTab()) {
        html += "<a href='javascript:document.forms[0].pageNo.value=" + (jprivate_lastPageInTab() + 1) + ";movePage()'>[다음]</a>&nbsp;";
    }
    if (jprivate_lastPage() > 1) {
        html += "<a href='javascript:document.forms[0].pageNo.value=" + jprivate_lastPage() + ";movePage()'>[마지막]</a>";
    }
    if (countMessage) {
        html += "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" + countMessage.replace(/\{0\}/, jprivate_totalSize);
    }
    navigation.innerHTML = html;
}

/**
 * 마지막 페이지 번호를 반환한다. 항상 정수형을 반환한다.
 */
function jprivate_lastPage() {
    return Math.floor(jprivate_totalSize / jprivate_listSize) + ((jprivate_totalSize % jprivate_listSize) != 0 ? 1 : 0);
}

/**
 * 현재 탭의 번호를 반환한다. 최소 값은 1이며 항상 정수형을 반환한다.
 */
function jprivate_currentTab() {
    return 1 + Math.floor(((jprivate_pageNo - 1) / jprivate_tabSize));
}

/**
 * 현재 탭의 번호가 1보다 크면 true를, 그렇지 않으면 false를 반환한다.
 */
function jprivate_hasPreviousTab() {
    return jprivate_currentTab() > 1;
}

/**
 * 현재 탭에 마지막 페이지 번호가 포함되어 있으면 false를, 그렇지 않으면 true를 반환한다.
 */
function jprivate_hasNextTab() {
    return (jprivate_currentTab() * jprivate_tabSize) - jprivate_lastPage() < 0;
}

/**
 * 현재 탭의 첫번째 페이지 번호를 반환한다.
 */
function jprivate_firstPageInTab() {
    return ((jprivate_currentTab() - 1) * jprivate_tabSize) + 1;
}

/**
 * 현재 탭의 마지막 페이지 번호를 반환한다.
 */
function jprivate_lastPageInTab() {
    return jprivate_hasNextTab() ? jprivate_currentTab() * jprivate_tabSize : jprivate_lastPage();
}

/**
 * object형태로 받은 파라미터를 URL형태로 인코드 한다.
 */
function jprivate_parseParam(params) {
	if (typeof params == "string") return encodeURI(params);
	var sb = [];
	for(property in params){
		var value = params[property];
		if (typeof value == "object" && !!value.length){ // 값이 배열이면 원소 하나하나를  
		    for(var i = 0; i < value.length; i++){
		        sb.push(encodeURIComponent(property) + "=" + encodeURIComponent(value[i]));
		    }
		}else{
		    sb.push(encodeURIComponent(property) + "=" + encodeURIComponent(params[property]));
		}
	}
	return sb.join("&");
}

/**
 * SELECT 박스(Drop-Down)에 있는 모든 옵션을 삭제하는 함수
 * @param select HTML Form의 SELECT 객체(Drop-Down)
 */
function clearOptions(select) {
    if (!select) {
        return;
    }
    for (var i = select.options.length; i > 0; i--) {
        select.remove(i - 1);
    }
}

/**
 * SELECT 박스에 있는 모든 옵션을 선택하는 함수
 * @param select HTML Form의 SELECT 객체
 */
function selectAllOptions(select) {
    var size = select.options.length;
    for (var i = 0; i < size; i++) {
        select.options[i].selected = true;
    }
    return false;
}


/**
 * SELECT 박스에 있는 모든 옵션을 해제하는 함수
 * @param select HTML Form의 SELECT 객체
 */
function deselectAllOptions(select) {
    var size = select.options.length;
    for (var i = 0; i < size; i++) {
        select.options[i].selected = false;
    }
    return false;
}


/**
 * SELECT 박스에 있는 모든 옵션을 선택 여부를 반대로 변경하는 함수
 * @param select HTML Form의 SELECT 객체
 */
function changeAllOptions(select) {
    var size = select.options.length;
    for (var i = 0; i < size; i++) {
        if (select.options[i].selected) {
           select.options[i].selected = false;
        } else {
           select.options[i].selected = true;
        }
    }
    return false;
}


/**
 * SELECT 박스에 있는 선택된 데이터(Option)를 삭제하는 함수
 * @param select HTML Form의 SELECT 객체
 */
function dropOptions(select) {
    var size = select.options.length;
    for (var i = 0; i < size; i++)
    {
        if (select.options[i].selected)
        {
            select.options[i] = null;
            i--;
            size--;
        }
    }
    return false;
}


/**
 * SELECT 박스에 있는 모든 데이터(Option)를 삭제하는 함수
 * @param select HTML Form의 SELECT 객체
 */
function dropAllOptions(select) {
    selectAllOptions(select);
    dropOptions(select);
    return false;
}


/**
 * 하나의 HTML Form SELECT의 선택된 데이터(option)를 다른 HTML Form SELECT로 옮기는 함수
 * @param from 옮겨야 하는 데이터를 가지고 있는 HTML Form Select
 * @param to   데이터가 옮겨지는 HTML Form Select
 */
function moveOptions(from, to) {
    var size = from.options.length;
    for (var i = 0; i < size; i++) {
        if (from.options[i].selected) {
            to.options[to.options.length] 
                = new Option(from.options[i].text, from.options[i].value);
            from.options[i] = null;
            i--;
            size--;
        }
    }
    return false;
}


/**
 * 하나의 HTML Form SELECT의 모든 데이터(option)를 다른 HTML Form SELECT로 옮기는 함수
 * @param from 옮겨야 하는 데이터를 가지고 있는 HTML Form Select
 * @param to   데이터가 옮겨지는 HTML Form Select
 * @see        moveData()
 */
function moveAllOptions(from, to) {
    selectAllOptions(from);
    moveOptions(from, to);
    return false;
}


/**
 * HTML Form TextField의 값을 HTML Form Select로 추가하는 함수
 * @param data  데이터 객체
 * @param select HTML Form Select 객체
 */
function addTextToOptions(data, select) {
    select.options[select.options.length] = new Option(data, data);  
    return false;
}

/**
 * HTML 선택된 옵션을 위로 올린다.
 * @param select HTML Form Select 객체
 */
function upSelectedOptions(select) {

    /* select에 포함된 전체 option의 수 */
    var size = select.options.length;

    for (var i = 0; i < size; i++) {
        if (select.options[i].selected) {
        
            /* 맨 위의 option을 선택했는지를 확인한다. */
            if (i == 0) return false;
            
            var tempText = select.options[i - 1].text;
            var tempValue = select.options[i - 1].value;
            select.options[i - 1].text = select.options[i].text;
            select.options[i - 1].value = select.options[i].value;
            select.options[i - 1].selected = true;
            select.options[i].text = tempText;
            select.options[i].value = tempValue;
            select.options[i].selected = false;
        }
    }
    return false;
}


/**
 * HTML 선택된 옵션을 아래로 내린다.
 * @param select HTML Form Select 객체
 */
function downSelectedOptions(select) {
    /* select에 포함된 전체 option의 수 */
    var size = select.options.length;

    for (var i = size - 1; i >= 0; i--) {
        if (select.options[i].selected) {
        
            /* 맨 아래의 option을 선택했는지를 확인한다. */
            if (i == (size - 1)) return false;
            
            var tempText = select.options[i + 1].text;
            var tempValue = select.options[i + 1].value;
            select.options[i + 1].text = select.options[i].text;
            select.options[i + 1].value = select.options[i].value;
            select.options[i + 1].selected = true;
            select.options[i].text = tempText;
            select.options[i].value = tempValue;
            select.options[i].selected = false;
        }
    }
    return false;
}

/**
 * HTML 체크 박스를 모두 선택한다.
 * @param select HTML Form Checkbox 객체
 */
function selectAllCheckboxs(checkbox) {
    if (checkbox == null) return;
    
    /* 체크 박스가 하나인 경우에 대한 처리 */
    checkbox.checked = true;

    for (var i = 0; i < checkbox.length; i++) {
        checkbox[i].checked = true;
    }
    return true;
}

/**
 * HTML 채크 박스의 선택을 해제한다.
 * @param select HTML Form Checkbox 객체
 */
function deselectAllCheckboxs(checkbox) {
    if (checkbox == null) return;
    
    /* 체크 박스가 하나인 경우에 대한 처리 */
    checkbox.checked = false;
    
    for (var i = 0; i < checkbox.length; i++) {
        checkbox[i].checked = false;
    }
    return true;
}

/**
 * HTML 체크 박스의 선택 여부를 변경한다.
 * @param select HTML Form Checkbox 객체
 */
function changeAllCheckboxs(checkbox) {
    if (checkbox == null) return;
    
    /* 체크 박스가 하나인 경우에 대한 처리 */
    if (checkbox.checked) {
        checkbox.checked = false;
    } else {
        checkbox.checked = true; 
    }
    
    for (var i = 0; i < checkbox.length; i++) {
        if (checkbox[i].checked) {
            checkbox[i].checked = false;
        } else {
            checkbox[i].checked = true;
        }
    }
    return true;
}

/**
 * 특정 부분을 숨기거나 보여준다.
 * @param field : 숨기거나 보일 객체
 * @return false
 */
function showOrHide(obj) {
    var element = $(obj);
    if (element.style.display == "none") {
        element.style.display = "block";
    } else {
        element.style.display = "none";
    }
    return false;
}

/**
 * 선택한 라디오 버튼의 값을 반환한다.
 * @param radioButton 라디오 버튼
 * @return 선택한 라디오 버튼의 값
 */
function getRadioButtonValue(radioButton) {
    if (radioButton.length) {
        for (var i = 0; i < radioButton.length; i++) {
            if (radioButton[i].checked) {
                return radioButton[i].value;
            }
        }
    }
    return radioButton.value;
}

/**
 * 날짜를 선택할 수 있는 모달 팝업 화면 창을 연다.
 *
 * @param field 날짜가 입력될 필드
 */
function getDate(field) {
    var url = getContextName() + "/popup/DatePopup.jsp";
    var ws = formatWindowProperty(200, 100, 375, 410);

    /* IE */
    if (window.showModalDialog) {
        var dt = showModalDialog(url, window, ws);
        if (dt != null) {
            if (dt.year == "") {
                field.value = "";
            } else {
                //field.value = dt.year + "-" + dt.month + "-" + dt.day;
                field.value = dt.year + dt.month + dt.day;
            }
        }
    } else {
        url += "?formName=" + field.form.getAttribute("name") + "&fieldName=" + field.name;
        window.open(url, null, ws);
    }
}

/**
 * 순서를 변경할 수 있는 팝업 화면 창을 연다.
 *
 * @param getStatement 대상 리스트를 가져오는 쿼리 아이디
 * @param updateStatement 순서를 변경하는 쿼리 아이디
 * @param query 기타 파라미터로 JParam 객체를 담고 있는 배열 
 */
function openOrderPopup(getStatement, updateStatement, query) {
    var url = getContextName() + "/popup/OrderPopup.jsp?getStatement=" + getStatement + "&updateStatement=" + updateStatement;
    var ws = formatWindowProperty1(166, 129, 310, 280);

	if (query) {
		for (var i = 0; i < query.length; i++) {
			url = url + "&" + query[i].name + "=" + query[i].value;
		}
	}
    window.open(url, null, ws);
}

/**
 * 도움말 팝업 화면 창을 연다.
 * 
 * @param context 현재 보고 있는 업무 화면의 URL
 */
function openHelpPopup(context) {
    var url = getContextName() + "/popup/HelpPopup.jsp?context=" + context;
    var ws = formatWindowProperty1(166, 129, 700, 500);
    window.open(url, null, ws);
}

/**
 * 코드 리스트 팝업 화면 창을 연다.
 * 
 * @param id 코드 아이디가 입력될 필드
 * @param name 코드 이름이 입력될 필드
 * @param code 코드
 * @param selector 선택자
 */
function openCodePopup(id, name, code, selector) {
    var url = getContextName() + "/popup/CodePopup.jsp?q_targetId=" + id.name + "&q_targetName=" + name.name + "&q_code=" + code;
    if (selector) {
        url += "&q_selector=" + selector;
    }
    var ws = formatWindowProperty1(166, 129, 600, 500);
    window.open(url, null, ws);
}

/**
 * 서버 인스턴스 팝업 화면 창을 연다.
 * 
 * @param interface 컴포넌트 인터페이스
 * @param moduleId 컴포넌트 모듈 아이디
 */
function openInstancePopup(interfaceName, moduleId) {
    var url = getContextName() + "/RefreshComponent.do?c_interface=" + interfaceName + "&moduleId=" + moduleId;
    var ws = formatWindowProperty1(166, 129, 400, 300);
    window.open(url, null, ws);
}

/**
 * 주소 팝업 화면 창을 연다.
 * 
 * @param post 우편 본호가 입력될 필드
 * @param address 주소가 입력될 필드
 * post, address, formName, postId
 */
function openZipCodePopupFront() {
    var tmpPostId;

    //if(postId){ tmpPostId = postId.name;} else{ tmpPostId = ''; }

	var queryString = "";

	if(arguments.length == 1) {
		try {
			if(arguments[0] != null) {
				queryString += "?callFunction=" + arguments[0];
			}else queryString += "?callFunction=";
		}catch(e) {}
	}else {
		//post
		try {if(arguments[0] != null) {
				if(typeof arguments[0] == "object") {queryString += "?q_targetPost=" + arguments[0].name;
				}else if(typeof arguments[0] == "string") {queryString += "?q_targetPost=" + arguments[0];}
			}else queryString += "?q_targetPost=";
		}catch(e) {}

		//address
		try {if(arguments[1] != null) {
				if(typeof arguments[1] == "object") {queryString += "&q_targetAddress=" + arguments[1].name;
				}else if(typeof arguments[1] == "string") {queryString += "&q_targetAddress=" + arguments[1];}
			}else queryString += "&q_targetAddress=";
		}catch(e) {}

		//formName
		try {if(arguments[2] != null) {
				if(typeof arguments[2] == "object") {queryString += "&formName=" + arguments[2].name;
				}else if(typeof arguments[1] == "string") {queryString += "&formName=" + arguments[2];}
			}else queryString += "&formName=";
		}catch(e) {}

		//post id
		try {if(arguments[3] != null) {
				if(typeof arguments[3] == "object") {queryString += "&postId=" + arguments[3].name;
				}else if(typeof arguments[1] == "string") {queryString += "&postId=" + arguments[3];}
			}else queryString += "&postId=";
		}catch(e) {}
	}

    var url = "/wps/portal/b2c/popup/zipcode/ZipCode.do" + queryString;

    var ws = formatWindowProperty1(166, 129, 402, 378);
    window.open(url, null, ws);
}

/**
 * 주소 팝업 화면 창을 연다.
 * 
 * @param post 우편 본호가 입력될 필드
 * @param address 주소가 입력될 필드
 */
function openZipCodePopupAdmin(post, address, formName, postId) {
    var tmpPostId;
   	//if(postId){ tmpPostId = postId.name;} else{ tmpPostId = ''; }

	var queryString = "";

	if(arguments.length == 1) {
		try {
			if(arguments[0] != null) {
				queryString += "?callFunction=" + arguments[0];
			}else queryString += "?callFunction=";
		}catch(e) {}
	}else {
		//post
		try {if(arguments[0] != null) {
				if(typeof arguments[0] == "object") {queryString += "?q_targetPost=" + arguments[0].name;
				}else if(typeof arguments[0] == "string") {queryString += "?q_targetPost=" + arguments[0];}
			}else queryString += "?q_targetPost=";
		}catch(e) {}

		//address
		try {if(arguments[1] != null) {
				if(typeof arguments[1] == "object") {queryString += "&q_targetAddress=" + arguments[1].name;
				}else if(typeof arguments[1] == "string") {queryString += "&q_targetAddress=" + arguments[1];}
			}else queryString += "&q_targetAddress=";
		}catch(e) {}

		//formName
		try {if(arguments[2] != null) {
				if(typeof arguments[2] == "object") {queryString += "&formName=" + arguments[2].name;
				}else if(typeof arguments[1] == "string") {queryString += "&formName=" + arguments[2];}
			}else queryString += "&formName=";
		}catch(e) {}

		//post id
		try {if(arguments[3] != null) {
				if(typeof arguments[3] == "object") {queryString += "&postId=" + arguments[3].name;
				}else if(typeof arguments[1] == "string") {queryString += "&postId=" + arguments[3];}
			}else queryString += "&postId=";
		}catch(e) {}
	}

    var url = "/wps/myportal/b2bi/popup/zipcode/ZipCode.do" + queryString;

    var ws = formatWindowProperty1(166, 129, 402, 378);
    window.open(url, null, ws);
}


/**
 * 테이블 형식의 조직 팝업 화면 창을 연다.
 * 
 * @param id 조직 아이디가 입력될 필드
 * @param name 조직 이름이 입력될 필드
 */
function openOrgPopup(id, name) {
    var url = getContextName() + "/popup/OrgPopup.jsp?q_targetId=" + id.name + "&q_targetName=" + name.name;
    var ws = formatWindowProperty1(166, 129, 550, 450);
    window.open(url, null, ws);
}

/**
 * 트리 형식의 조직 팝업 화면 창을 연다.
 * 
 * @param id 조직 아이디가 입력될 필드
 * @param name 조직 이름이 입력될 필드
 */
function openOrgTreePopup(id, name) {
    var url = getContextName() + "/popup/OrgTreePopup.jsp?q_targetId=" + id.name + "&q_targetName=" + name.name;
    var ws = formatWindowProperty1(166, 129, 500, 450);
    window.open(url, null, ws);
}

/**
 * 조직 별 사용자 팝업 화면 창을 연다.
 * @param id 사용자 아이디가 입력될 필드
 * @param name 사용자 이름이 입력될 필드
 */ 
function openOrgUserPopup(id, name) {
    var url = getContextName() + "/popup/OrgUserPopup.jsp?q_targetId=" + id.name + "&q_targetName=" + name.name;
    var ws = formatWindowProperty1(166, 129, 600, 320);
    window.open(url, null, ws);
}

/**
 * 사용자 팝업 화면 창을 연다.
 * @param params 파라미터
 */
function openUserPopup(params) {
    var url = getContextName() + "/popup/UserPopup.jsp?" + params;
    var ws = formatWindowProperty1(166, 129, 540, 450);
    window.open(url, null, ws);
}

/**
 * 비밀번호 수정 화면 창을 연다.
 */
function openChangePasswordPopup(context) {
    var url = getContextName() + "/popup/ChangePasswordPopup.jsp";
    var ws = formatWindowProperty1(166, 129, 400, 200);
    window.open(url, null, ws);
}

function formatWindowProperty(left, top, width, height) {
    if (window.showModalDialog) {
        return "status:no; dialogLeft:" + left + "px; dialogTop:" + top + "px; dialogWidth:" + width + "px; dialogHeight:" + height + "px";
    } else {
        return "modal, left=" + left + "px, top=" + top + ", width=" + width + "px, height=" + height + "px";
    }
}

function formatWindowProperty1(left, top, width, height) {
    return "left=" + left + ", top=" + top + ", width=" + width + ", height=" + height;
}

function JParam(name, value) {
	this.name = name;
	this.value = value;
}

/*
 * Ext JS Library 1.0 Beta 1
 * Copyright(c) 2006-2007, Ext JS, LLC.
 * licensing@extjs.com
 * 
 * http://www.extjs.com/license
 */

/*  Prototype JavaScript framework, version 1.5.0
 *  (c) 2005-2007 Sam Stephenson
 *
 *  Prototype is freely distributable under the terms of an MIT-style license.
 *  For details, see the Prototype web site: http://prototype.conio.net/
 *
/*--------------------------------------------------------------------------*/

var Prototype = {
  Version: '1.5.0',
  BrowserFeatures: {
    XPath: !!document.evaluate
  },

  ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
  emptyFunction: function() {},
  K: function(x) { return x }
}

var Class = {
  create: function() {
    return function() {
      this.initialize.apply(this, arguments);
    }
  }
}

var Abstract = new Object();

Object.extend = function(destination, source) {
  for (var property in source) {
    destination[property] = source[property];
  }
  return destination;
}

Object.extend(Object, {
  inspect: function(object) {
    try {
      if (object === undefined) return 'undefined';
      if (object === null) return 'null';
      return object.inspect ? object.inspect() : object.toString();
    } catch (e) {
      if (e instanceof RangeError) return '...';
      throw e;
    }
  },

  keys: function(object) {
    var keys = [];
    for (var property in object)
      keys.push(property);
    return keys;
  },

  values: function(object) {
    var values = [];
    for (var property in object)
      values.push(object[property]);
    return values;
  },

  clone: function(object) {
    return Object.extend({}, object);
  }
});

Function.prototype.bind = function() {
  var __method = this, args = $A(arguments), object = args.shift();
  return function() {
    return __method.apply(object, args.concat($A(arguments)));
  }
}

Function.prototype.bindAsEventListener = function(object) {
  var __method = this, args = $A(arguments), object = args.shift();
  return function(event) {
    return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments)));
  }
}

Object.extend(Number.prototype, {
  toColorPart: function() {
    var digits = this.toString(16);
    if (this < 16) return '0' + digits;
    return digits;
  },

  succ: function() {
    return this + 1;
  },

  times: function(iterator) {
    $R(0, this, true).each(iterator);
    return this;
  }
});

var Try = {
  these: function() {
    var returnValue;

    for (var i = 0, length = arguments.length; i < length; i++) {
      var lambda = arguments[i];
      try {
        returnValue = lambda();
        break;
      } catch (e) {}
    }

    return returnValue;
  }
}

/*--------------------------------------------------------------------------*/

var PeriodicalExecuter = Class.create();
PeriodicalExecuter.prototype = {
  initialize: function(callback, frequency) {
    this.callback = callback;
    this.frequency = frequency;
    this.currentlyExecuting = false;

    this.registerCallback();
  },

  registerCallback: function() {
    this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
  },

  stop: function() {
    if (!this.timer) return;
    clearInterval(this.timer);
    this.timer = null;
  },

  onTimerEvent: function() {
    if (!this.currentlyExecuting) {
      try {
        this.currentlyExecuting = true;
        this.callback(this);
      } finally {
        this.currentlyExecuting = false;
      }
    }
  }
}
String.interpret = function(value){
  return value == null ? '' : String(value);
}

Object.extend(String.prototype, {
  gsub: function(pattern, replacement) {
    var result = '', source = this, match;
    replacement = arguments.callee.prepareReplacement(replacement);

    while (source.length > 0) {
      if (match = source.match(pattern)) {
        result += source.slice(0, match.index);
        result += String.interpret(replacement(match));
        source  = source.slice(match.index + match[0].length);
      } else {
        result += source, source = '';
      }
    }
    return result;
  },

  sub: function(pattern, replacement, count) {
    replacement = this.gsub.prepareReplacement(replacement);
    count = count === undefined ? 1 : count;

    return this.gsub(pattern, function(match) {
      if (--count < 0) return match[0];
      return replacement(match);
    });
  },

  scan: function(pattern, iterator) {
    this.gsub(pattern, iterator);
    return this;
  },

  truncate: function(length, truncation) {
    length = length || 30;
    truncation = truncation === undefined ? '...' : truncation;
    return this.length > length ?
      this.slice(0, length - truncation.length) + truncation : this;
  },

  strip: function() {
    return this.replace(/^\s+/, '').replace(/\s+$/, '');
  },

  stripTags: function() {
    return this.replace(/<\/?[^>]+>/gi, '');
  },

  stripScripts: function() {
    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
  },

  extractScripts: function() {
    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
    return (this.match(matchAll) || []).map(function(scriptTag) {
      return (scriptTag.match(matchOne) || ['', ''])[1];
    });
  },

  evalScripts: function() {
    return this.extractScripts().map(function(script) { return eval(script) });
  },

  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.length > 1 ?
      $A(div.childNodes).inject('',function(memo,node){ return memo+node.nodeValue }) :
      div.childNodes[0].nodeValue) : '';
  },

  toQueryParams: function(separator) {
    var match = this.strip().match(/([^?#]*)(#.*)?$/);
    if (!match) return {};

    return match[1].split(separator || '&').inject({}, function(hash, pair) {
      if ((pair = pair.split('='))[0]) {
        var name = decodeURIComponent(pair[0]);
        var value = pair[1] ? decodeURIComponent(pair[1]) : undefined;

        if (hash[name] !== undefined) {
          if (hash[name].constructor != Array)
            hash[name] = [hash[name]];
          if (value) hash[name].push(value);
        }
        else hash[name] = value;
      }
      return hash;
    });
  },

  toArray: function() {
    return this.split('');
  },

  succ: function() {
    return this.slice(0, this.length - 1) +
      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
  },

  camelize: function() {
    var parts = this.split('-'), len = parts.length;
    if (len == 1) return parts[0];

    var camelized = this.charAt(0) == '-'
      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
      : parts[0];

    for (var i = 1; i < len; i++)
      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);

    return camelized;
  },

  capitalize: function(){
    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
  },

  underscore: function() {
    return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
  },

  dasherize: function() {
    return this.gsub(/_/,'-');
  },

  inspect: function(useDoubleQuotes) {
    var escapedString = this.replace(/\\/g, '\\\\');
    if (useDoubleQuotes)
      return '"' + escapedString.replace(/"/g, '\\"') + '"';
    else
      return "'" + escapedString.replace(/'/g, '\\\'') + "'";
  }
});

String.prototype.gsub.prepareReplacement = function(replacement) {
  if (typeof replacement == 'function') return replacement;
  var template = new Template(replacement);
  return function(match) { return template.evaluate(match) };
}

String.prototype.parseQuery = String.prototype.toQueryParams;

var Template = Class.create();
Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
Template.prototype = {
  initialize: function(template, pattern) {
    this.template = template.toString();
    this.pattern  = pattern || Template.Pattern;
  },

  evaluate: function(object) {
    return this.template.gsub(this.pattern, function(match) {
      var before = match[1];
      if (before == '\\') return match[2];
      return before + String.interpret(object[match[3]]);
    });
  }
}

var $break    = new Object();
var $continue = new Object();

var Enumerable = {
  each: function(iterator) {
    var index = 0;
    try {
      this._each(function(value) {
        try {
          iterator(value, index++);
        } catch (e) {
          if (e != $continue) throw e;
        }
      });
    } catch (e) {
      if (e != $break) throw e;
    }
    return this;
  },

  eachSlice: function(number, iterator) {
    var index = -number, slices = [], array = this.toArray();
    while ((index += number) < array.length)
      slices.push(array.slice(index, index+number));
    return slices.map(iterator);
  },

  all: function(iterator) {
    var result = true;
    this.each(function(value, index) {
      result = result && !!(iterator || Prototype.K)(value, index);
      if (!result) throw $break;
    });
    return result;
  },

  any: function(iterator) {
    var result = false;
    this.each(function(value, index) {
      if (result = !!(iterator || Prototype.K)(value, index))
        throw $break;
    });
    return result;
  },

  collect: function(iterator) {
    var results = [];
    this.each(function(value, index) {
      results.push((iterator || Prototype.K)(value, index));
    });
    return results;
  },

  detect: function(iterator) {
    var result;
    this.each(function(value, index) {
      if (iterator(value, index)) {
        result = value;
        throw $break;
      }
    });
    return result;
  },

  findAll: function(iterator) {
    var results = [];
    this.each(function(value, index) {
      if (iterator(value, index))
        results.push(value);
    });
    return results;
  },

  grep: function(pattern, iterator) {
    var results = [];
    this.each(function(value, index) {
      var stringValue = value.toString();
      if (stringValue.match(pattern))
        results.push((iterator || Prototype.K)(value, index));
    })
    return results;
  },

  include: function(object) {
    var found = false;
    this.each(function(value) {
      if (value == object) {
        found = true;
        throw $break;
      }
    });
    return found;
  },

  inGroupsOf: function(number, fillWith) {
    fillWith = fillWith === undefined ? null : fillWith;
    return this.eachSlice(number, function(slice) {
      while(slice.length < number) slice.push(fillWith);
      return slice;
    });
  },

  inject: function(memo, iterator) {
    this.each(function(value, index) {
      memo = iterator(memo, value, index);
    });
    return memo;
  },

  invoke: function(method) {
    var args = $A(arguments).slice(1);
    return this.map(function(value) {
      return value[method].apply(value, args);
    });
  },

  max: function(iterator) {
    var result;
    this.each(function(value, index) {
      value = (iterator || Prototype.K)(value, index);
      if (result == undefined || value >= result)
        result = value;
    });
    return result;
  },

  min: function(iterator) {
    var result;
    this.each(function(value, index) {
      value = (iterator || Prototype.K)(value, index);
      if (result == undefined || value < result)
        result = value;
    });
    return result;
  },

  partition: function(iterator) {
    var trues = [], falses = [];
    this.each(function(value, index) {
      ((iterator || Prototype.K)(value, index) ?
        trues : falses).push(value);
    });
    return [trues, falses];
  },

  pluck: function(property) {
    var results = [];
    this.each(function(value, index) {
      results.push(value[property]);
    });
    return results;
  },

  reject: function(iterator) {
    var results = [];
    this.each(function(value, index) {
      if (!iterator(value, index))
        results.push(value);
    });
    return results;
  },

  sortBy: function(iterator) {
    return this.map(function(value, index) {
      return {value: value, criteria: iterator(value, index)};
    }).sort(function(left, right) {
      var a = left.criteria, b = right.criteria;
      return a < b ? -1 : a > b ? 1 : 0;
    }).pluck('value');
  },

  toArray: function() {
    return this.map();
  },

  zip: function() {
    var iterator = Prototype.K, args = $A(arguments);
    if (typeof args.last() == 'function')
      iterator = args.pop();

    var collections = [this].concat(args).map($A);
    return this.map(function(value, index) {
      return iterator(collections.pluck(index));
    });
  },

  size: function() {
    return this.toArray().length;
  },

  inspect: function() {
    return '#<Enumerable:' + this.toArray().inspect() + '>';
  }
}

Object.extend(Enumerable, {
  map:     Enumerable.collect,
  find:    Enumerable.detect,
  select:  Enumerable.findAll,
  member:  Enumerable.include,
  entries: Enumerable.toArray
});
var $A = Array.from = function(iterable) {
  if (!iterable) return [];
  if (iterable.toArray) {
    return iterable.toArray();
  } else {
    var results = [];
    for (var i = 0, length = iterable.length; i < length; i++)
      results.push(iterable[i]);
    return results;
  }
}

Object.extend(Array.prototype, Enumerable);

if (!Array.prototype._reverse)
  Array.prototype._reverse = Array.prototype.reverse;

Object.extend(Array.prototype, {
  _each: function(iterator) {
    for (var i = 0, length = this.length; i < length; i++)
      iterator(this[i]);
  },

  clear: function() {
    this.length = 0;
    return this;
  },

  first: function() {
    return this[0];
  },

  last: function() {
    return this[this.length - 1];
  },

  compact: function() {
    return this.select(function(value) {
      return value != null;
    });
  },

  flatten: function() {
    return this.inject([], function(array, value) {
      return array.concat(value && value.constructor == Array ?
        value.flatten() : [value]);
    });
  },

  without: function() {
    var values = $A(arguments);
    return this.select(function(value) {
      return !values.include(value);
    });
  },

  indexOf: function(object) {
    for (var i = 0, length = this.length; i < length; i++)
      if (this[i] == object) return i;
    return -1;
  },

  reverse: function(inline) {
    return (inline !== false ? this : this.toArray())._reverse();
  },

  reduce: function() {
    return this.length > 1 ? this : this[0];
  },

  uniq: function() {
    return this.inject([], function(array, value) {
      return array.include(value) ? array : array.concat([value]);
    });
  },

  clone: function() {
    return [].concat(this);
  },

  size: function() {
    return this.length;
  },

  inspect: function() {
    return '[' + this.map(Object.inspect).join(', ') + ']';
  }
});

Array.prototype.toArray = Array.prototype.clone;

function $w(string){
  string = string.strip();
  return string ? string.split(/\s+/) : [];
}

if(window.opera){
  Array.prototype.concat = function(){
    var array = [];
    for(var i = 0, length = this.length; i < length; i++) array.push(this[i]);
    for(var i = 0, length = arguments.length; i < length; i++) {
      if(arguments[i].constructor == Array) {
        for(var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
          array.push(arguments[i][j]);
      } else {
        array.push(arguments[i]);
      }
    }
    return array;
  }
}
var Hash = function(obj) {
  Object.extend(this, obj || {});
};

Object.extend(Hash, {
  toQueryString: function(obj) {
    var parts = [];

	  this.prototype._each.call(obj, function(pair) {
      if (!pair.key) return;

      if (pair.value && pair.value.constructor == Array) {
        var values = pair.value.compact();
        if (values.length < 2) pair.value = values.reduce();
        else {
        	key = encodeURIComponent(pair.key);
          values.each(function(value) {
            value = value != undefined ? encodeURIComponent(value) : '';
            parts.push(key + '=' + encodeURIComponent(value));
          });
          return;
        }
      }
      if (pair.value == undefined) pair[1] = '';
      parts.push(pair.map(encodeURIComponent).join('='));
	  });

    return parts.join('&');
  }
});

Object.extend(Hash.prototype, Enumerable);
Object.extend(Hash.prototype, {
  _each: function(iterator) {
    for (var key in this) {
      var value = this[key];
      if (value && value == Hash.prototype[key]) continue;

      var pair = [key, value];
      pair.key = key;
      pair.value = value;
      iterator(pair);
    }
  },

  keys: function() {
    return this.pluck('key');
  },

  values: function() {
    return this.pluck('value');
  },

  merge: function(hash) {
    return $H(hash).inject(this, function(mergedHash, pair) {
      mergedHash[pair.key] = pair.value;
      return mergedHash;
    });
  },

  remove: function() {
    var result;
    for(var i = 0, length = arguments.length; i < length; i++) {
      var value = this[arguments[i]];
      if (value !== undefined){
        if (result === undefined) result = value;
        else {
          if (result.constructor != Array) result = [result];
          result.push(value)
        }
      }
      delete this[arguments[i]];
    }
    return result;
  },

  toQueryString: function() {
    return Hash.toQueryString(this);
  },

  inspect: function() {
    return '#<Hash:{' + this.map(function(pair) {
      return pair.map(Object.inspect).join(': ');
    }).join(', ') + '}>';
  }
});

function $H(object) {
  if (object && object.constructor == Hash) return object;
  return new Hash(object);
};
ObjectRange = Class.create();
Object.extend(ObjectRange.prototype, Enumerable);
Object.extend(ObjectRange.prototype, {
  initialize: function(start, end, exclusive) {
    this.start = start;
    this.end = end;
    this.exclusive = exclusive;
  },

  _each: function(iterator) {
    var value = this.start;
    while (this.include(value)) {
      iterator(value);
      value = value.succ();
    }
  },

  include: function(value) {
    if (value < this.start)
      return false;
    if (this.exclusive)
      return value < this.end;
    return value <= this.end;
  }
});

var $R = function(start, end, exclusive) {
  return new ObjectRange(start, end, exclusive);
}

var Ajax = {
  getTransport: function() {
    return Try.these(
      function() {return new XMLHttpRequest()},
      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
    ) || false;
  },

  activeRequestCount: 0
}

Ajax.Responders = {
  responders: [],

  _each: function(iterator) {
    this.responders._each(iterator);
  },

  register: function(responder) {
    if (!this.include(responder))
      this.responders.push(responder);
  },

  unregister: function(responder) {
    this.responders = this.responders.without(responder);
  },

  dispatch: function(callback, request, transport, json) {
    this.each(function(responder) {
      if (typeof responder[callback] == 'function') {
        try {
          responder[callback].apply(responder, [request, transport, json]);
        } catch (e) {}
      }
    });
  }
};

Object.extend(Ajax.Responders, Enumerable);

Ajax.Responders.register({
  onCreate: function() {
    Ajax.activeRequestCount++;
  },
  onComplete: function() {
    Ajax.activeRequestCount--;
  }
});

Ajax.Base = function() {};
Ajax.Base.prototype = {
  setOptions: function(options) {
    this.options = {
      method:       'post',
      asynchronous: true,
      contentType:  'application/x-www-form-urlencoded',
      encoding:     'UTF-8',
      parameters:   ''
    }
    Object.extend(this.options, options || {});

    this.options.method = this.options.method.toLowerCase();
    if (typeof this.options.parameters == 'string')
      this.options.parameters = this.options.parameters.toQueryParams();
  }
}

Ajax.Request = Class.create();
Ajax.Request.Events =
  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];

Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
  _complete: false,

  initialize: function(url, options) {
    this.transport = Ajax.getTransport();
    this.setOptions(options);
    this.request(url);
  },

  request: function(url) {
    this.url = url;
    this.method = this.options.method;
    var params = this.options.parameters;

    if (!['get', 'post'].include(this.method)) {
      // simulate other verbs over post
      params['_method'] = this.method;
      this.method = 'post';
    }

    params = Hash.toQueryString(params);
    if (params && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) params += '&_='

    // when GET, append parameters to URL
    if (this.method == 'get' && params)
      this.url += (this.url.indexOf('?') > -1 ? '&' : '?') + params;

    try {
      Ajax.Responders.dispatch('onCreate', this, this.transport);

      this.transport.open(this.method.toUpperCase(), this.url,
        this.options.asynchronous);

      if (this.options.asynchronous)
        setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);

      this.transport.onreadystatechange = this.onStateChange.bind(this);
      this.setRequestHeaders();

      var body = this.method == 'post' ? (this.options.postBody || params) : null;

      this.transport.send(body);

      /* Force Firefox to handle ready state 4 for synchronous requests */
      if (!this.options.asynchronous && this.transport.overrideMimeType)
        this.onStateChange();

    }
    catch (e) {
      this.dispatchException(e);
    }
  },

  onStateChange: function() {
    var readyState = this.transport.readyState;
    if (readyState > 1 && !((readyState == 4) && this._complete))
      this.respondToReadyState(this.transport.readyState);
  },

  setRequestHeaders: function() {
    var headers = {
      'X-Requested-With': 'XMLHttpRequest',
      'X-Prototype-Version': Prototype.Version,
      'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
    };

    if (this.method == 'post') {
      headers['Content-type'] = this.options.contentType +
        (this.options.encoding ? '; charset=' + this.options.encoding : '');

      /* Force "Connection: close" for older Mozilla browsers to work
       * around a bug where XMLHttpRequest sends an incorrect
       * Content-length header. See Mozilla Bugzilla #246651.
       */
      if (this.transport.overrideMimeType &&
          (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
            headers['Connection'] = 'close';
    }

    // user-defined headers
    if (typeof this.options.requestHeaders == 'object') {
      var extras = this.options.requestHeaders;

      if (typeof extras.push == 'function')
        for (var i = 0, length = extras.length; i < length; i += 2)
          headers[extras[i]] = extras[i+1];
      else
        $H(extras).each(function(pair) { headers[pair.key] = pair.value });
    }

    for (var name in headers)
      this.transport.setRequestHeader(name, headers[name]);
  },

  success: function() {
    return !this.transport.status
        || (this.transport.status >= 200 && this.transport.status < 300);
  },

  respondToReadyState: function(readyState) {
    var state = Ajax.Request.Events[readyState];
    var transport = this.transport, json = this.evalJSON();

    if (state == 'Complete') {
      try {
        this._complete = true;
        (this.options['on' + this.transport.status]
         || this.options['on' + (this.success() ? 'Success' : 'Failure')]
         || Prototype.emptyFunction)(transport, json);
      } catch (e) {
        this.dispatchException(e);
      }

      if ((this.getHeader('Content-type') || 'text/javascript').strip().
        match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
          this.evalResponse();
    }

    try {
      (this.options['on' + state] || Prototype.emptyFunction)(transport, json);
      Ajax.Responders.dispatch('on' + state, this, transport, json);
    } catch (e) {
      this.dispatchException(e);
    }

    if (state == 'Complete') {
      // avoid memory leak in MSIE: clean up
      this.transport.onreadystatechange = Prototype.emptyFunction;
    }
  },

  getHeader: function(name) {
    try {
      return this.transport.getResponseHeader(name);
    } catch (e) { return null }
  },

  evalJSON: function() {
    try {
      var json = this.getHeader('X-JSON');
      return json ? eval('(' + json + ')') : null;
    } catch (e) { return null }
  },

  evalResponse: function() {
    try {
      return eval(this.transport.responseText);
    } catch (e) {
      this.dispatchException(e);
    }
  },

  dispatchException: function(exception) {
    (this.options.onException || Prototype.emptyFunction)(this, exception);
    Ajax.Responders.dispatch('onException', this, exception);
  }
});

Ajax.Updater = Class.create();

Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
  initialize: function(container, url, options) {
    this.container = {
      success: (container.success || container),
      failure: (container.failure || (container.success ? null : container))
    }

    this.transport = Ajax.getTransport();
    this.setOptions(options);

    var onComplete = this.options.onComplete || Prototype.emptyFunction;
    this.options.onComplete = (function(transport, param) {
      this.updateContent();
      onComplete(transport, param);
    }).bind(this);

    this.request(url);
  },

  updateContent: function() {
    var receiver = this.container[this.success() ? 'success' : 'failure'];
    var response = this.transport.responseText;

    if (!this.options.evalScripts) response = response.stripScripts();

    if (receiver = $(receiver)) {
      if (this.options.insertion)
        new this.options.insertion(receiver, response);
      else
        receiver.update(response);
    }

    if (this.success()) {
      if (this.onComplete)
        setTimeout(this.onComplete.bind(this), 10);
    }
  }
});

Ajax.PeriodicalUpdater = Class.create();
Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
  initialize: function(container, url, options) {
    this.setOptions(options);
    this.onComplete = this.options.onComplete;

    this.frequency = (this.options.frequency || 2);
    this.decay = (this.options.decay || 1);

    this.updater = {};
    this.container = container;
    this.url = url;

    this.start();
  },

  start: function() {
    this.options.onComplete = this.updateComplete.bind(this);
    this.onTimerEvent();
  },

  stop: function() {
    this.updater.options.onComplete = undefined;
    clearTimeout(this.timer);
    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
  },

  updateComplete: function(request) {
    if (this.options.decay) {
      this.decay = (request.responseText == this.lastText ?
        this.decay * this.options.decay : 1);

      this.lastText = request.responseText;
    }
    this.timer = setTimeout(this.onTimerEvent.bind(this),
      this.decay * this.frequency * 1000);
  },

  onTimerEvent: function() {
    this.updater = new Ajax.Updater(this.container, this.url, this.options);
  }
});
function $(element) {
  if (arguments.length > 1) {
    for (var i = 0, elements = [], length = arguments.length; i < length; i++)
      elements.push($(arguments[i]));
    return elements;
  }
  if (typeof element == 'string')
    element = document.getElementById(element);
  return Element.extend(element);
}

if (Prototype.BrowserFeatures.XPath) {
  document._getElementsByXPath = function(expression, parentElement) {
    var results = [];
    var query = document.evaluate(expression, $(parentElement) || document,
      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    for (var i = 0, length = query.snapshotLength; i < length; i++)
      results.push(query.snapshotItem(i));
    return results;
  };
}

document.getElementsByClassName = function(className, parentElement) {
  if (Prototype.BrowserFeatures.XPath) {
    var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
    return document._getElementsByXPath(q, parentElement);
  } else {
    var children = ($(parentElement) || document.body).getElementsByTagName('*');
    var elements = [], child;
    for (var i = 0, length = children.length; i < length; i++) {
      child = children[i];
      if (Element.hasClassName(child, className))
        elements.push(Element.extend(child));
    }
    return elements;
  }
};

/*--------------------------------------------------------------------------*/

if (!window.Element)
  var Element = new Object();

Element.extend = function(element) {
  if (!element || _nativeExtensions || element.nodeType == 3) return element;

  if (!element._extended && element.tagName && element != window) {
    var methods = Object.clone(Element.Methods), cache = Element.extend.cache;

    if (element.tagName == 'FORM')
      Object.extend(methods, Form.Methods);
    if (['INPUT', 'TEXTAREA', 'SELECT'].include(element.tagName))
      Object.extend(methods, Form.Element.Methods);

    Object.extend(methods, Element.Methods.Simulated);

    for (var property in methods) {
      var value = methods[property];
      if (typeof value == 'function' && !(property in element))
        element[property] = cache.findOrStore(value);
    }
  }

  element._extended = true;
  return element;
};

Element.extend.cache = {
  findOrStore: function(value) {
    return this[value] = this[value] || function() {
      return value.apply(null, [this].concat($A(arguments)));
    }
  }
};

Element.Methods = {
  visible: function(element) {
    return $(element).style.display != 'none';
  },

  toggle: function(element) {
    element = $(element);
    Element[Element.visible(element) ? 'hide' : 'show'](element);
    return element;
  },

  hide: function(element) {
    $(element).style.display = 'none';
    return element;
  },

  show: function(element) {
    $(element).style.display = '';
    return element;
  },

  remove: function(element) {
    element = $(element);
    element.parentNode.removeChild(element);
    return element;
  },

  update: function(element, html) {
    html = typeof html == 'undefined' ? '' : html.toString();
    $(element).innerHTML = html.stripScripts();
    setTimeout(function() {html.evalScripts()}, 10);
    return element;
  },

  replace: function(element, html) {
    element = $(element);
    html = typeof html == 'undefined' ? '' : html.toString();
    if (element.outerHTML) {
      element.outerHTML = html.stripScripts();
    } else {
      var range = element.ownerDocument.createRange();
      range.selectNodeContents(element);
      element.parentNode.replaceChild(
        range.createContextualFragment(html.stripScripts()), element);
    }
    setTimeout(function() {html.evalScripts()}, 10);
    return element;
  },

  inspect: function(element) {
    element = $(element);
    var result = '<' + element.tagName.toLowerCase();
    $H({'id': 'id', 'className': 'class'}).each(function(pair) {
      var property = pair.first(), attribute = pair.last();
      var value = (element[property] || '').toString();
      if (value) result += ' ' + attribute + '=' + value.inspect(true);
    });
    return result + '>';
  },

  recursivelyCollect: function(element, property) {
    element = $(element);
    var elements = [];
    while (element = element[property])
      if (element.nodeType == 1)
        elements.push(Element.extend(element));
    return elements;
  },

  ancestors: function(element) {
    return $(element).recursivelyCollect('parentNode');
  },

  descendants: function(element) {
    return $A($(element).getElementsByTagName('*'));
  },

  immediateDescendants: function(element) {
    if (!(element = $(element).firstChild)) return [];
    while (element && element.nodeType != 1) element = element.nextSibling;
    if (element) return [element].concat($(element).nextSiblings());
    return [];
  },

  previousSiblings: function(element) {
    return $(element).recursivelyCollect('previousSibling');
  },

  nextSiblings: function(element) {
    return $(element).recursivelyCollect('nextSibling');
  },

  siblings: function(element) {
    element = $(element);
    return element.previousSiblings().reverse().concat(element.nextSiblings());
  },

  match: function(element, selector) {
    if (typeof selector == 'string')
      selector = new Selector(selector);
    return selector.match($(element));
  },

  up: function(element, expression, index) {
    return Selector.findElement($(element).ancestors(), expression, index);
  },

  down: function(element, expression, index) {
    return Selector.findElement($(element).descendants(), expression, index);
  },

  previous: function(element, expression, index) {
    return Selector.findElement($(element).previousSiblings(), expression, index);
  },

  next: function(element, expression, index) {
    return Selector.findElement($(element).nextSiblings(), expression, index);
  },

  getElementsBySelector: function() {
    var args = $A(arguments), element = $(args.shift());
    return Selector.findChildElements(element, args);
  },

  getElementsByClassName: function(element, className) {
    return document.getElementsByClassName(className, element);
  },

  readAttribute: function(element, name) {
    element = $(element);
    if (document.all && !window.opera) {
      var t = Element._attributeTranslations;
      if (t.values[name]) return t.values[name](element, name);
      if (t.names[name])  name = t.names[name];
      var attribute = element.attributes[name];
      if(attribute) return attribute.nodeValue;
    }
    return element.getAttribute(name);
  },

  getHeight: function(element) {
    return $(element).getDimensions().height;
  },

  getWidth: function(element) {
    return $(element).getDimensions().width;
  },

  classNames: function(element) {
    return new Element.ClassNames(element);
  },

  hasClassName: function(element, className) {
    if (!(element = $(element))) return;
    var elementClassName = element.className;
    if (elementClassName.length == 0) return false;
    if (elementClassName == className ||
        elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
      return true;
    return false;
  },

  addClassName: function(element, className) {
    if (!(element = $(element))) return;
    Element.classNames(element).add(className);
    return element;
  },

  removeClassName: function(element, className) {
    if (!(element = $(element))) return;
    Element.classNames(element).remove(className);
    return element;
  },

  toggleClassName: function(element, className) {
    if (!(element = $(element))) return;
    Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
    return element;
  },

  observe: function() {
    Event.observe.apply(Event, arguments);
    return $A(arguments).first();
  },

  stopObserving: function() {
    Event.stopObserving.apply(Event, arguments);
    return $A(arguments).first();
  },

  // removes whitespace-only text node children
  cleanWhitespace: function(element) {
    element = $(element);
    var node = element.firstChild;
    while (node) {
      var nextNode = node.nextSibling;
      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
        element.removeChild(node);
      node = nextNode;
    }
    return element;
  },

  empty: function(element) {
    return $(element).innerHTML.match(/^\s*$/);
  },

  descendantOf: function(element, ancestor) {
    element = $(element), ancestor = $(ancestor);
    while (element = element.parentNode)
      if (element == ancestor) return true;
    return false;
  },

  scrollTo: function(element) {
    element = $(element);
    var pos = Position.cumulativeOffset(element);
    window.scrollTo(pos[0], pos[1]);
    return element;
  },

  getStyle: function(element, style) {
    element = $(element);
    if (['float','cssFloat'].include(style))
      style = (typeof element.style.styleFloat != 'undefined' ? 'styleFloat' : 'cssFloat');
    style = style.camelize();
    var value = element.style[style];
    if (!value) {
      if (document.defaultView && document.defaultView.getComputedStyle) {
        var css = document.defaultView.getComputedStyle(element, null);
        value = css ? css[style] : null;
      } else if (element.currentStyle) {
        value = element.currentStyle[style];
      }
    }

    if((value == 'auto') && ['width','height'].include(style) && (element.getStyle('display') != 'none'))
      value = element['offset'+style.capitalize()] + 'px';

    if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
      if (Element.getStyle(element, 'position') == 'static') value = 'auto';
    if(style == 'opacity') {
      if(value) return parseFloat(value);
      if(value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
        if(value[1]) return parseFloat(value[1]) / 100;
      return 1.0;
    }
    return value == 'auto' ? null : value;
  },

  setStyle: function(element, style) {
    element = $(element);
    for (var name in style) {
      var value = style[name];
      if(name == 'opacity') {
        if (value == 1) {
          value = (/Gecko/.test(navigator.userAgent) &&
            !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 0.999999 : 1.0;
          if(/MSIE/.test(navigator.userAgent) && !window.opera)
            element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
        } else if(value === '') {
          if(/MSIE/.test(navigator.userAgent) && !window.opera)
            element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
        } else {
          if(value < 0.00001) value = 0;
          if(/MSIE/.test(navigator.userAgent) && !window.opera)
            element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') +
              'alpha(opacity='+value*100+')';
        }
      } else if(['float','cssFloat'].include(name)) name = (typeof element.style.styleFloat != 'undefined') ? 'styleFloat' : 'cssFloat';
      element.style[name.camelize()] = value;
    }
    return element;
  },

  getDimensions: function(element) {
    element = $(element);
    var display = $(element).getStyle('display');
    if (display != 'none' && display != null) // Safari bug
      return {width: element.offsetWidth, height: element.offsetHeight};

    // All *Width and *Height properties give 0 on elements with display none,
    // so enable the element temporarily
    var els = element.style;
    var originalVisibility = els.visibility;
    var originalPosition = els.position;
    var originalDisplay = els.display;
    els.visibility = 'hidden';
    els.position = 'absolute';
    els.display = 'block';
    var originalWidth = element.clientWidth;
    var originalHeight = element.clientHeight;
    els.display = originalDisplay;
    els.position = originalPosition;
    els.visibility = originalVisibility;
    return {width: originalWidth, height: originalHeight};
  },

  makePositioned: function(element) {
    element = $(element);
    var pos = Element.getStyle(element, 'position');
    if (pos == 'static' || !pos) {
      element._madePositioned = true;
      element.style.position = 'relative';
      // Opera returns the offset relative to the positioning context, when an
      // element is position relative but top and left have not been defined
      if (window.opera) {
        element.style.top = 0;
        element.style.left = 0;
      }
    }
    return element;
  },

  undoPositioned: function(element) {
    element = $(element);
    if (element._madePositioned) {
      element._madePositioned = undefined;
      element.style.position =
        element.style.top =
        element.style.left =
        element.style.bottom =
        element.style.right = '';
    }
    return element;
  },

  makeClipping: function(element) {
    element = $(element);
    if (element._overflow) return element;
    element._overflow = element.style.overflow || 'auto';
    if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
      element.style.overflow = 'hidden';
    return element;
  },

  undoClipping: function(element) {
    element = $(element);
    if (!element._overflow) return element;
    element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
    element._overflow = null;
    return element;
  }
};

Object.extend(Element.Methods, {childOf: Element.Methods.descendantOf});

Element._attributeTranslations = {};

Element._attributeTranslations.names = {
  colspan:   "colSpan",
  rowspan:   "rowSpan",
  valign:    "vAlign",
  datetime:  "dateTime",
  accesskey: "accessKey",
  tabindex:  "tabIndex",
  enctype:   "encType",
  maxlength: "maxLength",
  readonly:  "readOnly",
  longdesc:  "longDesc"
};

Element._attributeTranslations.values = {
  _getAttr: function(element, attribute) {
    return element.getAttribute(attribute, 2);
  },

  _flag: function(element, attribute) {
    return $(element).hasAttribute(attribute) ? attribute : null;
  },

  style: function(element) {
    return element.style.cssText.toLowerCase();
  },

  title: function(element) {
    var node = element.getAttributeNode('title');
    return node.specified ? node.nodeValue : null;
  }
};

Object.extend(Element._attributeTranslations.values, {
  href: Element._attributeTranslations.values._getAttr,
  src:  Element._attributeTranslations.values._getAttr,
  disabled: Element._attributeTranslations.values._flag,
  checked:  Element._attributeTranslations.values._flag,
  readonly: Element._attributeTranslations.values._flag,
  multiple: Element._attributeTranslations.values._flag
});

Element.Methods.Simulated = {
  hasAttribute: function(element, attribute) {
    var t = Element._attributeTranslations;
    attribute = t.names[attribute] || attribute;
    return $(element).getAttributeNode(attribute).specified;
  }
};

// IE is missing .innerHTML support for TABLE-related elements
if (document.all && !window.opera){
  Element.Methods.update = function(element, html) {
    element = $(element);
    html = typeof html == 'undefined' ? '' : html.toString();
    var tagName = element.tagName.toUpperCase();
    if (['THEAD','TBODY','TR','TD'].include(tagName)) {
      var div = document.createElement('div');
      switch (tagName) {
        case 'THEAD':
        case 'TBODY':
          div.innerHTML = '<table><tbody>' +  html.stripScripts() + '</tbody></table>';
          depth = 2;
          break;
        case 'TR':
          div.innerHTML = '<table><tbody><tr>' +  html.stripScripts() + '</tr></tbody></table>';
          depth = 3;
          break;
        case 'TD':
          div.innerHTML = '<table><tbody><tr><td>' +  html.stripScripts() + '</td></tr></tbody></table>';
          depth = 4;
      }
      $A(element.childNodes).each(function(node){
        element.removeChild(node)
      });
      depth.times(function(){ div = div.firstChild });

      $A(div.childNodes).each(
        function(node){ element.appendChild(node) });
    } else {
      element.innerHTML = html.stripScripts();
    }
    setTimeout(function() {html.evalScripts()}, 10);
    return element;
  }
};

Object.extend(Element, Element.Methods);

var _nativeExtensions = false;

if(/Konqueror|Safari|KHTML/.test(navigator.userAgent))
  ['', 'Form', 'Input', 'TextArea', 'Select'].each(function(tag) {
    var className = 'HTML' + tag + 'Element';
    if(window[className]) return;
    var klass = window[className] = {};
    klass.prototype = document.createElement(tag ? tag.toLowerCase() : 'div').__proto__;
  });

Element.addMethods = function(methods) {
  Object.extend(Element.Methods, methods || {});

  function copy(methods, destination, onlyIfAbsent) {
    onlyIfAbsent = onlyIfAbsent || false;
    var cache = Element.extend.cache;
    for (var property in methods) {
      var value = methods[property];
      if (!onlyIfAbsent || !(property in destination))
        destination[property] = cache.findOrStore(value);
    }
  }

  if (typeof HTMLElement != 'undefined') {
    copy(Element.Methods, HTMLElement.prototype);
    copy(Element.Methods.Simulated, HTMLElement.prototype, true);
    copy(Form.Methods, HTMLFormElement.prototype);
    [HTMLInputElement, HTMLTextAreaElement, HTMLSelectElement].each(function(klass) {
      copy(Form.Element.Methods, klass.prototype);
    });
    _nativeExtensions = true;
  }
}

var Toggle = new Object();
Toggle.display = Element.toggle;

/*--------------------------------------------------------------------------*/

Abstract.Insertion = function(adjacency) {
  this.adjacency = adjacency;
}

Abstract.Insertion.prototype = {
  initialize: function(element, content) {
    this.element = $(element);
    this.content = content.stripScripts();

    if (this.adjacency && this.element.insertAdjacentHTML) {
      try {
        this.element.insertAdjacentHTML(this.adjacency, this.content);
      } catch (e) {
        var tagName = this.element.tagName.toUpperCase();
        if (['TBODY', 'TR'].include(tagName)) {
          this.insertContent(this.contentFromAnonymousTable());
        } else {
          throw e;
        }
      }
    } else {
      this.range = this.element.ownerDocument.createRange();
      if (this.initializeRange) this.initializeRange();
      this.insertContent([this.range.createContextualFragment(this.content)]);
    }

    setTimeout(function() {content.evalScripts()}, 10);
  },

  contentFromAnonymousTable: function() {
    var div = document.createElement('div');
    div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
    return $A(div.childNodes[0].childNodes[0].childNodes);
  }
}

var Insertion = new Object();

Insertion.Before = Class.create();
Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
  initializeRange: function() {
    this.range.setStartBefore(this.element);
  },

  insertContent: function(fragments) {
    fragments.each((function(fragment) {
      this.element.parentNode.insertBefore(fragment, this.element);
    }).bind(this));
  }
});

Insertion.Top = Class.create();
Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
  initializeRange: function() {
    this.range.selectNodeContents(this.element);
    this.range.collapse(true);
  },

  insertContent: function(fragments) {
    fragments.reverse(false).each((function(fragment) {
      this.element.insertBefore(fragment, this.element.firstChild);
    }).bind(this));
  }
});

Insertion.Bottom = Class.create();
Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
  initializeRange: function() {
    this.range.selectNodeContents(this.element);
    this.range.collapse(this.element);
  },

  insertContent: function(fragments) {
    fragments.each((function(fragment) {
      this.element.appendChild(fragment);
    }).bind(this));
  }
});

Insertion.After = Class.create();
Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
  initializeRange: function() {
    this.range.setStartAfter(this.element);
  },

  insertContent: function(fragments) {
    fragments.each((function(fragment) {
      this.element.parentNode.insertBefore(fragment,
        this.element.nextSibling);
    }).bind(this));
  }
});

/*--------------------------------------------------------------------------*/

Element.ClassNames = Class.create();
Element.ClassNames.prototype = {
  initialize: function(element) {
    this.element = $(element);
  },

  _each: function(iterator) {
    this.element.className.split(/\s+/).select(function(name) {
      return name.length > 0;
    })._each(iterator);
  },

  set: function(className) {
    this.element.className = className;
  },

  add: function(classNameToAdd) {
    if (this.include(classNameToAdd)) return;
    this.set($A(this).concat(classNameToAdd).join(' '));
  },

  remove: function(classNameToRemove) {
    if (!this.include(classNameToRemove)) return;
    this.set($A(this).without(classNameToRemove).join(' '));
  },

  toString: function() {
    return $A(this).join(' ');
  }
};

Object.extend(Element.ClassNames.prototype, Enumerable);
var Selector = Class.create();
Selector.prototype = {
  initialize: function(expression) {
    this.params = {classNames: []};
    this.expression = expression.toString().strip();
    this.parseExpression();
    this.compileMatcher();
  },

  parseExpression: function() {
    function abort(message) { throw 'Parse error in selector: ' + message; }

    if (this.expression == '')  abort('empty expression');

    var params = this.params, expr = this.expression, match, modifier, clause, rest;
    while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) {
      params.attributes = params.attributes || [];
      params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''});
      expr = match[1];
    }

    if (expr == '*') return this.params.wildcard = true;

    while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) {
      modifier = match[1], clause = match[2], rest = match[3];
      switch (modifier) {
        case '#':       params.id = clause; break;
        case '.':       params.classNames.push(clause); break;
        case '':
        case undefined: params.tagName = clause.toUpperCase(); break;
        default:        abort(expr.inspect());
      }
      expr = rest;
    }

    if (expr.length > 0) abort(expr.inspect());
  },

  buildMatchExpression: function() {
    var params = this.params, conditions = [], clause;

    if (params.wildcard)
      conditions.push('true');
    if (clause = params.id)
      conditions.push('element.readAttribute("id") == ' + clause.inspect());
    if (clause = params.tagName)
      conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
    if ((clause = params.classNames).length > 0)
      for (var i = 0, length = clause.length; i < length; i++)
        conditions.push('element.hasClassName(' + clause[i].inspect() + ')');
    if (clause = params.attributes) {
      clause.each(function(attribute) {
        var value = 'element.readAttribute(' + attribute.name.inspect() + ')';
        var splitValueBy = function(delimiter) {
          return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
        }

        switch (attribute.operator) {
          case '=':       conditions.push(value + ' == ' + attribute.value.inspect()); break;
          case '~=':      conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break;
          case '|=':      conditions.push(
                            splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect()
                          ); break;
          case '!=':      conditions.push(value + ' != ' + attribute.value.inspect()); break;
          case '':
          case undefined: conditions.push('element.hasAttribute(' + attribute.name.inspect() + ')'); break;
          default:        throw 'Unknown operator ' + attribute.operator + ' in selector';
        }
      });
    }

    return conditions.join(' && ');
  },

  compileMatcher: function() {
    this.match = new Function('element', 'if (!element.tagName) return false; \
      element = $(element); \
      return ' + this.buildMatchExpression());
  },

  findElements: function(scope) {
    var element;

    if (element = $(this.params.id))
      if (this.match(element))
        if (!scope || Element.childOf(element, scope))
          return [element];

    scope = (scope || document).getElementsByTagName(this.params.tagName || '*');

    var results = [];
    for (var i = 0, length = scope.length; i < length; i++)
      if (this.match(element = scope[i]))
        results.push(Element.extend(element));

    return results;
  },

  toString: function() {
    return this.expression;
  }
}

Object.extend(Selector, {
  matchElements: function(elements, expression) {
    var selector = new Selector(expression);
    return elements.select(selector.match.bind(selector)).map(Element.extend);
  },

  findElement: function(elements, expression, index) {
    if (typeof expression == 'number') index = expression, expression = false;
    return Selector.matchElements(elements, expression || '*')[index || 0];
  },

  findChildElements: function(element, expressions) {
    return expressions.map(function(expression) {
      return expression.match(/[^\s"]+(?:"[^"]*"[^\s"]+)*/g).inject([null], function(results, expr) {
        var selector = new Selector(expr);
        return results.inject([], function(elements, result) {
          return elements.concat(selector.findElements(result || element));
        });
      });
    }).flatten();
  }
});

function $$() {
  return Selector.findChildElements(document, $A(arguments));
}
var Form = {
  reset: function(form) {
    $(form).reset();
    return form;
  },

  serializeElements: function(elements, getHash) {
    var data = elements.inject({}, function(result, element) {
      if (!element.disabled && element.name) {
        var key = element.name, value = $(element).getValue();
        if (value != undefined) {
          if (result[key]) {
            if (result[key].constructor != Array) result[key] = [result[key]];
            result[key].push(value);
          }
          else result[key] = value;
        }
      }
      return result;
    });

    return getHash ? data : Hash.toQueryString(data);
  }
};

Form.Methods = {
  serialize: function(form, getHash) {
    return Form.serializeElements(Form.getElements(form), getHash);
  },

  getElements: function(form) {
    return $A($(form).getElementsByTagName('*')).inject([],
      function(elements, child) {
        if (Form.Element.Serializers[child.tagName.toLowerCase()])
          elements.push(Element.extend(child));
        return elements;
      }
    );
  },

  getInputs: function(form, typeName, name) {
    form = $(form);
    var inputs = form.getElementsByTagName('input');

    if (!typeName && !name) return $A(inputs).map(Element.extend);

    for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
      var input = inputs[i];
      if ((typeName && input.type != typeName) || (name && input.name != name))
        continue;
      matchingInputs.push(Element.extend(input));
    }

    return matchingInputs;
  },

  disable: function(form) {
    form = $(form);
    form.getElements().each(function(element) {
      element.blur();
      element.disabled = 'true';
    });
    return form;
  },

  enable: function(form) {
    form = $(form);
    form.getElements().each(function(element) {
      element.disabled = '';
    });
    return form;
  },

  findFirstElement: function(form) {
    return $(form).getElements().find(function(element) {
      return element.type != 'hidden' && !element.disabled &&
        ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
    });
  },

  focusFirstElement: function(form) {
    form = $(form);
    form.findFirstElement().activate();
    return form;
  }
}

Object.extend(Form, Form.Methods);

/*--------------------------------------------------------------------------*/

Form.Element = {
  focus: function(element) {
    $(element).focus();
    return element;
  },

  select: function(element) {
    $(element).select();
    return element;
  }
}

Form.Element.Methods = {
  serialize: function(element) {
    element = $(element);
    if (!element.disabled && element.name) {
      var value = element.getValue();
      if (value != undefined) {
        var pair = {};
        pair[element.name] = value;
        return Hash.toQueryString(pair);
      }
    }
    return '';
  },

  getValue: function(element) {
    element = $(element);
    var method = element.tagName.toLowerCase();
    return Form.Element.Serializers[method](element);
  },

  clear: function(element) {
    $(element).value = '';
    return element;
  },

  present: function(element) {
    return $(element).value != '';
  },

  activate: function(element) {
    element = $(element);
    element.focus();
    if (element.select && ( element.tagName.toLowerCase() != 'input' ||
      !['button', 'reset', 'submit'].include(element.type) ) )
      element.select();
    return element;
  },

  disable: function(element) {
    element = $(element);
    element.disabled = true;
    return element;
  },

  enable: function(element) {
    element = $(element);
    element.blur();
    element.disabled = false;
    return element;
  }
}

Object.extend(Form.Element, Form.Element.Methods);
var Field = Form.Element;
var $F = Form.Element.getValue;

/*--------------------------------------------------------------------------*/

Form.Element.Serializers = {
  input: function(element) {
    switch (element.type.toLowerCase()) {
      case 'checkbox':
      case 'radio':
        return Form.Element.Serializers.inputSelector(element);
      default:
        return Form.Element.Serializers.textarea(element);
    }
  },

  inputSelector: function(element) {
    return element.checked ? element.value : null;
  },

  textarea: function(element) {
    return element.value;
  },

  select: function(element) {
    return this[element.type == 'select-one' ?
      'selectOne' : 'selectMany'](element);
  },

  selectOne: function(element) {
    var index = element.selectedIndex;
    return index >= 0 ? this.optionValue(element.options[index]) : null;
  },

  selectMany: function(element) {
    var values, length = element.length;
    if (!length) return null;

    for (var i = 0, values = []; i < length; i++) {
      var opt = element.options[i];
      if (opt.selected) values.push(this.optionValue(opt));
    }
    return values;
  },

  optionValue: function(opt) {
    // extend element because hasAttribute may not be native
    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
  }
}

/*--------------------------------------------------------------------------*/

Abstract.TimedObserver = function() {}
Abstract.TimedObserver.prototype = {
  initialize: function(element, frequency, callback) {
    this.frequency = frequency;
    this.element   = $(element);
    this.callback  = callback;

    this.lastValue = this.getValue();
    this.registerCallback();
  },

  registerCallback: function() {
    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
  },

  onTimerEvent: function() {
    var value = this.getValue();
    var changed = ('string' == typeof this.lastValue && 'string' == typeof value
      ? this.lastValue != value : String(this.lastValue) != String(value));
    if (changed) {
      this.callback(this.element, value);
      this.lastValue = value;
    }
  }
}

Form.Element.Observer = Class.create();
Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
  getValue: function() {
    return Form.Element.getValue(this.element);
  }
});

Form.Observer = Class.create();
Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
  getValue: function() {
    return Form.serialize(this.element);
  }
});

/*--------------------------------------------------------------------------*/

Abstract.EventObserver = function() {}
Abstract.EventObserver.prototype = {
  initialize: function(element, callback) {
    this.element  = $(element);
    this.callback = callback;

    this.lastValue = this.getValue();
    if (this.element.tagName.toLowerCase() == 'form')
      this.registerFormCallbacks();
    else
      this.registerCallback(this.element);
  },

  onElementEvent: function() {
    var value = this.getValue();
    if (this.lastValue != value) {
      this.callback(this.element, value);
      this.lastValue = value;
    }
  },

  registerFormCallbacks: function() {
    Form.getElements(this.element).each(this.registerCallback.bind(this));
  },

  registerCallback: function(element) {
    if (element.type) {
      switch (element.type.toLowerCase()) {
        case 'checkbox':
        case 'radio':
          Event.observe(element, 'click', this.onElementEvent.bind(this));
          break;
        default:
          Event.observe(element, 'change', this.onElementEvent.bind(this));
          break;
      }
    }
  }
}

Form.Element.EventObserver = Class.create();
Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
  getValue: function() {
    return Form.Element.getValue(this.element);
  }
});

Form.EventObserver = Class.create();
Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
  getValue: function() {
    return Form.serialize(this.element);
  }
});
if (!window.Event) {
  var Event = new Object();
}

Object.extend(Event, {
  KEY_BACKSPACE: 8,
  KEY_TAB:       9,
  KEY_RETURN:   13,
  KEY_ESC:      27,
  KEY_LEFT:     37,
  KEY_UP:       38,
  KEY_RIGHT:    39,
  KEY_DOWN:     40,
  KEY_DELETE:   46,
  KEY_HOME:     36,
  KEY_END:      35,
  KEY_PAGEUP:   33,
  KEY_PAGEDOWN: 34,

  element: function(event) {
    return event.target || event.srcElement;
  },

  isLeftClick: function(event) {
    return (((event.which) && (event.which == 1)) ||
            ((event.button) && (event.button == 1)));
  },

  pointerX: function(event) {
    return event.pageX || (event.clientX +
      (document.documentElement.scrollLeft || document.body.scrollLeft));
  },

  pointerY: function(event) {
    return event.pageY || (event.clientY +
      (document.documentElement.scrollTop || document.body.scrollTop));
  },

  stop: function(event) {
    if (event.preventDefault) {
      event.preventDefault();
      event.stopPropagation();
    } else {
      event.returnValue = false;
      event.cancelBubble = true;
    }
  },

  // find the first node with the given tagName, starting from the
  // node the event was triggered on; traverses the DOM upwards
  findElement: function(event, tagName) {
    var element = Event.element(event);
    while (element.parentNode && (!element.tagName ||
        (element.tagName.toUpperCase() != tagName.toUpperCase())))
      element = element.parentNode;
    return element;
  },

  observers: false,

  _observeAndCache: function(element, name, observer, useCapture) {
    if (!this.observers) this.observers = [];
    if (element.addEventListener) {
      this.observers.push([element, name, observer, useCapture]);
      element.addEventListener(name, observer, useCapture);
    } else if (element.attachEvent) {
      this.observers.push([element, name, observer, useCapture]);
      element.attachEvent('on' + name, observer);
    }
  },

  unloadCache: function() {
    if (!Event.observers) return;
    for (var i = 0, length = Event.observers.length; i < length; i++) {
      Event.stopObserving.apply(this, Event.observers[i]);
      Event.observers[i][0] = null;
    }
    Event.observers = false;
  },

  observe: function(element, name, observer, useCapture) {
    element = $(element);
    useCapture = useCapture || false;

    if (name == 'keypress' &&
        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
        || element.attachEvent))
      name = 'keydown';

    Event._observeAndCache(element, name, observer, useCapture);
  },

  stopObserving: function(element, name, observer, useCapture) {
    element = $(element);
    useCapture = useCapture || false;

    if (name == 'keypress' &&
        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
        || element.detachEvent))
      name = 'keydown';

    if (element.removeEventListener) {
      element.removeEventListener(name, observer, useCapture);
    } else if (element.detachEvent) {
      try {
        element.detachEvent('on' + name, observer);
      } catch (e) {}
    }
  }
});

/* prevent memory leaks in IE */
if (navigator.appVersion.match(/\bMSIE\b/))
  Event.observe(window, 'unload', Event.unloadCache, false);
var Position = {
  // set to true if needed, warning: firefox performance problems
  // NOT neeeded for page scrolling, only if draggable contained in
  // scrollable elements
  includeScrollOffsets: false,

  // must be called before calling withinIncludingScrolloffset, every time the
  // page is scrolled
  prepare: function() {
    this.deltaX =  window.pageXOffset
                || document.documentElement.scrollLeft
                || document.body.scrollLeft
                || 0;
    this.deltaY =  window.pageYOffset
                || document.documentElement.scrollTop
                || document.body.scrollTop
                || 0;
  },

  realOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.scrollTop  || 0;
      valueL += element.scrollLeft || 0;
      element = element.parentNode;
    } while (element);
    return [valueL, valueT];
  },

  cumulativeOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
    } while (element);
    return [valueL, valueT];
  },

  positionedOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
      if (element) {
        if(element.tagName=='BODY') break;
        var p = Element.getStyle(element, 'position');
        if (p == 'relative' || p == 'absolute') break;
      }
    } while (element);
    return [valueL, valueT];
  },

  offsetParent: function(element) {
    if (element.offsetParent) return element.offsetParent;
    if (element == document.body) return element;

    while ((element = element.parentNode) && element != document.body)
      if (Element.getStyle(element, 'position') != 'static')
        return element;

    return document.body;
  },

  // caches x/y coordinate pair to use with overlap
  within: function(element, x, y) {
    if (this.includeScrollOffsets)
      return this.withinIncludingScrolloffsets(element, x, y);
    this.xcomp = x;
    this.ycomp = y;
    this.offset = this.cumulativeOffset(element);

    return (y >= this.offset[1] &&
            y <  this.offset[1] + element.offsetHeight &&
            x >= this.offset[0] &&
            x <  this.offset[0] + element.offsetWidth);
  },

  withinIncludingScrolloffsets: function(element, x, y) {
    var offsetcache = this.realOffset(element);

    this.xcomp = x + offsetcache[0] - this.deltaX;
    this.ycomp = y + offsetcache[1] - this.deltaY;
    this.offset = this.cumulativeOffset(element);

    return (this.ycomp >= this.offset[1] &&
            this.ycomp <  this.offset[1] + element.offsetHeight &&
            this.xcomp >= this.offset[0] &&
            this.xcomp <  this.offset[0] + element.offsetWidth);
  },

  // within must be called directly before
  overlap: function(mode, element) {
    if (!mode) return 0;
    if (mode == 'vertical')
      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
        element.offsetHeight;
    if (mode == 'horizontal')
      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
        element.offsetWidth;
  },

  page: function(forElement) {
    var valueT = 0, valueL = 0;

    var element = forElement;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;

      // Safari fix
      if (element.offsetParent==document.body)
        if (Element.getStyle(element,'position')=='absolute') break;

    } while (element = element.offsetParent);

    element = forElement;
    do {
      if (!window.opera || element.tagName=='BODY') {
        valueT -= element.scrollTop  || 0;
        valueL -= element.scrollLeft || 0;
      }
    } while (element = element.parentNode);

    return [valueL, valueT];
  },

  clone: function(source, target) {
    var options = Object.extend({
      setLeft:    true,
      setTop:     true,
      setWidth:   true,
      setHeight:  true,
      offsetTop:  0,
      offsetLeft: 0
    }, arguments[2] || {})

    // find page position of source
    source = $(source);
    var p = Position.page(source);

    // find coordinate system to use
    target = $(target);
    var delta = [0, 0];
    var parent = null;
    // delta [0,0] will do fine with position: fixed elements,
    // position:absolute needs offsetParent deltas
    if (Element.getStyle(target,'position') == 'absolute') {
      parent = Position.offsetParent(target);
      delta = Position.page(parent);
    }

    // correct by body offsets (fixes Safari)
    if (parent == document.body) {
      delta[0] -= document.body.offsetLeft;
      delta[1] -= document.body.offsetTop;
    }

    // set position
    if(options.setLeft)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
    if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
    if(options.setWidth)  target.style.width = source.offsetWidth + 'px';
    if(options.setHeight) target.style.height = source.offsetHeight + 'px';
  },

  absolutize: function(element) {
    element = $(element);
    if (element.style.position == 'absolute') return;
    Position.prepare();

    var offsets = Position.positionedOffset(element);
    var top     = offsets[1];
    var left    = offsets[0];
    var width   = element.clientWidth;
    var height  = element.clientHeight;

    element._originalLeft   = left - parseFloat(element.style.left  || 0);
    element._originalTop    = top  - parseFloat(element.style.top || 0);
    element._originalWidth  = element.style.width;
    element._originalHeight = element.style.height;

    element.style.position = 'absolute';
    element.style.top    = top + 'px';
    element.style.left   = left + 'px';
    element.style.width  = width + 'px';
    element.style.height = height + 'px';
  },

  relativize: function(element) {
    element = $(element);
    if (element.style.position == 'relative') return;
    Position.prepare();

    element.style.position = 'relative';
    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);

    element.style.top    = top + 'px';
    element.style.left   = left + 'px';
    element.style.height = element._originalHeight;
    element.style.width  = element._originalWidth;
  }
}

// Safari returns margins on body which is incorrect if the child is absolutely
// positioned.  For performance reasons, redefine Position.cumulativeOffset for
// KHTML/WebKit only.
if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
  Position.cumulativeOffset = function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      if (element.offsetParent == document.body)
        if (Element.getStyle(element, 'position') == 'absolute') break;

      element = element.offsetParent;
    } while (element);

    return [valueL, valueT];
  }
}

Element.addMethods();

/**
 * 동적으로 테이블의 Row를 추가하는 기능을 제공하는 클래스
 *
 * @param id <tbody/> 태그의 id 속성의 값
 * @param cellNumber 칼럼(<td/>) 태그의 숫자
 * @param tableFormat 추가되는 Row 및 칼럼에 대해 포멧을 정의하는 클래스.
 */
function JTable(id, cellNumber, tableFormat) {
    this.id = id;
    this.cellNumber = cellNumber;
    this.tableFormat = tableFormat;
    
    this.rowNumber = document.getElementById(id).rows.length;
        
    this.createRow = jtable_createRow;
    this.deleteRow = jtable_deleteRow;
}


/**
 * JTable createRow 메소드의 구현
 */
function jtable_createRow(model) {
    var body = document.getElementById(this.id);
    this.rowNumber += 1;

    var row;
    var cell;
        
    row = document.createElement("tr");
    body.appendChild(row);
    
    if (this.tableFormat.formatRow) {
        this.tableFormat.formatRow(row, this.rowNumber, model);
    }

    for (var i = 0; i < this.cellNumber; i++) {
        cell = document.createElement("td");
        row.appendChild(cell);
        this.tableFormat.formatCell(cell, i, this.rowNumber, model);
    }
    return false;
}


/**
 * JTable deleteeRow 메소드의 구현
 *
 * @param item 삭제할 TR 태그
 */
function jtable_deleteRow(item) {
    var rowIndexToDelete = -1;

    while (rowIndexToDelete == -1) {
        if (item.nodeName == "TR") {
            rowIndexToDelete = item.rowIndex;
        } else {
            item = item.parentNode;
        }
    }

    var body = document.getElementById(this.id);
    var rows = body.rows;
    for (i = 0; i < rows.length; i++) {
        if (rows[i].rowIndex == rowIndexToDelete) {
            body.deleteRow(i);
            return false;
        }
    }
    return false;        
}

/* 전역 변수 */

var LEAF_NODE_IMAGE = CONTEXT_NAME + "/ext/image/tree/File.gif";
var OPENED_NODE_IMAGE = CONTEXT_NAME + "/ext/image/tree/Opened.gif";
var CLOSED_NODE_IMAGE = CONTEXT_NAME + "/ext/image/tree/Closed.gif";
var INDENTION = "&nbsp;&nbsp;&nbsp;&nbsp";
var CALLBACK_METHOD = "goTo";

var allNodes = new Array; /* 모든 JNode 객체를 담고 있는 배열 */

/**
 * 하위 노드를 가지고 있는 노드를 클릭하는 경우에 대한 처리를 한다.
 *
 * @param nodeId JNode 객체의 아이디
 */
function clickOnNode(nodeId) {
    var element = document.getElementById("IMG_TREE" + nodeId);  /* <img src=""/> 태그 객체의 획득 */
    if (!element) {
        return;
    }
    var node = allNodes[nodeId];
    
    if (node.isOpened) {
        element.src = CLOSED_NODE_IMAGE;
        node.isOpened = false;
    } else {
        element.src = OPENED_NODE_IMAGE;
        node.isOpened = true;
    }
    node.change();
   
    return;
}

function openPath(nodeId) {
    var node = allNodes[nodeId];
    path = new Array;
       path[path.length] = node;
    while (node.parent) {
        node = node.parent;
        path[path.length] = node;
    }
    for (i = path.length-1; i >= 0; i--) {
        clickOnNode(path[i].id);
    }
    return;
}

/**
 * 노드를 나타내는 클래스.
 *
 * @param parent 부모 JNode 객체
 * @param id 노드의 아이디로 전체 화면에서 유일해야 한다.
 * @param name 노드의 이름
 * @param url 노드의 URL로, 있을 경우에 클릭시 CALLBACK_METHOD를 호출한다. 
 */
function JNode(parent, id, name, url) {

    /* Member Fields */
    this.id = id;
    this.name = name;
    this.url;
    if (url != "null") {
        this.url = url;
    }
    this.isOpened = false;
    this.isVisible = true;

    this.parent;
    this.children = new Array;

    if (parent) {
        this.parent = parent;
        this.isVisible = false;
        this.parent.children[this.parent.children.length] = this;
    }


    /* Member Methods */
    this.display = jnode_display;
    this.change = jnode_change;

    this.hasChild = function() {
                        return this.children.length > 0;
                    }

    /* Indention을 위한 메소드 */
    this.level = function() {
                       var result = 0;
                       var parent = this.parent;
                       while (parent != null) {
                           parent = parent.parent;
                           result++;
                       }
                       return result;
                   }

    allNodes[this.id] = this;
}


/**
 * JNode display 메소드의 구현. 트리를 화면에 출력한다.
 */
function jnode_display() {
    var style = "style=\"display:block\"";
    if (this.parent) {
        style = "style=\"display:none\"";
    }

    var content = "<div id=\"" + this.id + "\" " + style + ">";

    /* INDENTION 처리를 한다. */
    var level = this.level();
    for (var i = 0; i < level; i++) {
        content += INDENTION;
    }

    if (this.hasChild()) {
        content += "<a href='javascript:clickOnNode(\"" + this.id + "\")'>";
        content += "<img id=\"IMG_TREE" + this.id + "\" align=\"absmiddle\" ";
        content += "src=\"" + CLOSED_NODE_IMAGE + "\"/ border=\"0\"></a>";

        content += displayUrl(this);
        document.write(content);

        for (var i = 0; i < this.children.length; i++) {
            this.children[i].display();
        }
    } else {
        content += "<img src=\"" + LEAF_NODE_IMAGE + "\"/ border=\"0\" align=\"absmiddle\"></a>";
        content += displayUrl(this);
        document.write(content);
    }
}


/**
 * 노드의 URL 값을 반환한다. JTree 라이브러리 안에서만 사용하는 함수이다.
 *
 * @param JNode 객체
 */
function displayUrl(node) {
    if (node.url) {
        return "<a name=\"" + node.id + "\" href=\"javascript:" + CALLBACK_METHOD + "('" + node.url + "', " + node.level() + ")\">" + node.name + "</a></div>";
    } else {
        return node.name + "</div>";
    }
}


/**
 * JNode change 메소드의 구현. 노드가 열리고 닫히는 이벤트를 처리한다.
 */
function jnode_change() {
    for (var i = 0; i < this.children.length; i++) {
        var element = document.getElementById(this.children[i].id);
        if (this.isOpened && this.isVisible) {
            this.children[i].isVisible = true;
            element.style.display = "block";
        } else if (this.isOpened && !this.isVisible) {
            this.children[i].isVisible = false;
            element.style.display = "none";
        } else if (!this.isOpenend && this.isVisible) {
            this.children[i].isVisible = false;
            element.style.display = "none";
        } else if (!this.isOpenend && !this.isVisible) {
            this.children[i].isVisible = false;
            element.style.display = "none";
        }
        this.children[i].change();
    }
}

/**
 * <img/> 태그의 그림을 변경한다.
 * @param imgName <img/> 태그의 name 속성의 값
 * @param imgSrc 변경할 그림의 경로
 */
function rollover(imgName, imgSrc) {
    if (document.images) {
        document.images[imgName].src = imgSrc;
    }
}

/**
 * 트리의 모든 노드를 펼친다.
 *
 * @rootId 루트의 아이디
 */
function openAllNode(rootId) {
	var node = document.getElementById(rootId);
	if(node) {
		openChildNode(node);
	}
	var children = node.nextSibling;
	if(children) {
		var children = children.childNodes;
		for(var j=0; j < children.length; j++) {
			if(children[j].childNodes[0].className != 'leaf') {
           		openAllNode(children[j].childNodes[0].id);
           	}
        }
	}
}

/**
 * 트리의 모든 노드를 접는다.
 *
 * @rootId 루트의 아이디
 */
function closeAllNode(rootId) {
	var node = document.getElementById(rootId);
	if(node) {
		closeChildNode(node);
	}
	var children = node.nextSibling;
	if(children) {
		var children = children.childNodes;
		for(var j=0; j < children.length; j++) {
			if(children[j].childNodes[0].className != 'leaf') {
           		closeAllNode(children[j].childNodes[0].id);
           	}
        }
	}
}

/**
 * 지정한 노드를 펼친다.
 *
 * @id 노드 아이디
 */
function selectNode(id) {
    var node = document.getElementById(id);
    if (node) {
        for (;
             (node && (node.nodeName == "DIV"));
              node = node.parentNode.parentNode.previousSibling) {
	          openChildNode(node);
        }
    }
}

/**
 * 지정한 depth까지 트리를 펼친다.
 *
 * @rootId 시작하는 노드의 아이디
 */
function openNodeByDepth(rootId, depth) {
	var node = document.getElementById(rootId);
	if(node) {
		openChildNode(node);
	}
	var children = node.nextSibling;
	if(children && (depth > 1)) {
		var children = children.childNodes;
		var depth = depth-1;
		for(var j=0; j < children.length; j++) {
			if(children[j].childNodes[0].className != 'leaf') {
           		openNodeByDepth(children[j].childNodes[0].id, depth);
           	}
        }
	}
}

/**
 * 노드를 연다.
 *
 * @node 노드
 */
function openChildNode(node) {
    var classValue = node.className;
    if (classValue == "closed") {
        node.className = "opened";
    }
    for (var child = node.nextSibling; child; child = child.nextSibling) {
        if (child.nodeName == "UL") {
            if (child.style.display == "none" || child.style.display == "") {
                child.style.display = "block";
            }
            break;
        }
    }
}

/**
 * 노드를 닫는다.
 *
 * @node 노드
 */
function closeChildNode(node) {
    var classValue = node.className;
    if (classValue == "opened") {
        node.className = "closed";
    }
    for (var child = node.nextSibling; child; child = child.nextSibling) {
        if (child.nodeName == "UL") {
            if (child.style.display == "block" || child.style.display == "") {
                child.style.display = "none";
            }
            break;
        }
    }
}

/**
 * 노드의 여닫음을 토글한다. Url을 같이 넘기면 해당 페이지로 이동한다.
 *
 * @node 노드
 * @node url
 */
function toggleChildNode(node, url) {
	if (url) {
        window.location.href = getContextName() + url;
        return false;
    }
    var classValue = node.className;
    if (classValue == "closed") {
        openChildNode(node);
    } else if (classValue == "opened") {
        closeChildNode(node);
    }
}

function clickLeafNode(node) {
	if(window.doLeafNodeClick) {
		doLeafNodeClick(node);
	}
	return;
}

function clickTreeNode(node) {
	toggleChildNode(node);
	if(window.doTreeNodeClick) {
		doTreeNodeClick(node);
	}
	return;
}

/**
 * 업로드할 파일을 선택한 후에 필요한 작업을 수행한다.  
 *
 * @param element input(file)
 * @param max 업로드 가능한 최대 파일 사이즈
 * @param type 유형
 */
function createUpload(element, max, type) {

	/*IE 버그로 인한 입력값 정확성 체크 */
	var re = /^[a-z]:\\(.){0,300}$/i;
	if (element.value != "" && !element.value.match(re)) {
		alert("올바른 파일명이 아닙니다.파일을 다시 입력해 주시기 바랍니다.");
		element.focus();
		return;
	} else if (element.value == "") {
		return;
	}
	
    var liNode = element.parentNode;
    var ulNode = liNode.parentNode;
    
    /* 선택한 파일을 텍스트로 보이도록 함 */
    element.style.display = "none";
    var textSpan = document.createElement("span");
    var text = element.value + "<a onclick=\"deleteUpload(this, " + max + ", '" + type + "')\">삭제</a>";
    if (type == "N/A") {
        textSpan.innerHTML = text;
    } else {
        textSpan.innerHTML = text + "<input type=\"hidden\" name=\"j_file_type\" value=\"" + type + "\"/>";
    }
    liNode.appendChild(textSpan);

    jprivate_adjustCount(ulNode, max, type, true);
}

/**
 * 파일을 삭제하는 작업을 수행한다.  
 *
 * @param element a 태그
 * @param max 업로드 가능한 최대 파일 사이즈
 * @param type 유형
 * @param fileName 파일 이름 (이미 업로드 된 파일에만 해당)
 * @param filePath 파일 경로 (이미 업로드 된 파일에만 해당)
 */
function deleteUpload(element, max, type, fileName, filePath) {
    var liNode = element.parentNode.parentNode;
    var ulNode = liNode.parentNode;

    /* UI에서 삭제 */
    ulNode.removeChild(liNode);
    
    jprivate_adjustCount(ulNode, max, type, false);

    /* 삭제할 파일 정보 기록 */
    if (fileName) {
        var Forms = document.forms[0];
        var input = document.createElement("input");
        input.name = "j_file_delete";
        input.type = "hidden";
        input.value = fileName;
        Forms.appendChild(input);
        
        input = document.createElement("input");
        input.name = "j_file_path_delete";
        input.type = "hidden";
        input.value = filePath;
        Forms.appendChild(input);
    }
}

function jprivate_adjustCount(ulNode, max, type, flag) {
    /* 총 업로드 파일 수 수정 */
    var allSpan = ulNode.getElementsByTagName("span");
    var name = "j_file_count";
    if (type != "N/A") {
        name += ("_" + type);
    }
    var fileCount = document.forms[0][name];
    fileCount.value = allSpan.length;

    /* 새로운 파일 입력 박스 생성 */
    if ((flag && allSpan.length < max) || (!flag && allSpan.length == (max - 1))) {
        var upload = document.createElement("li");    
        upload.innerHTML = "<input type=\"file\" name=\"j_file\" onchange=\"createUpload(this, " + max + ", '" + type + "');\"/>";
        ulNode.appendChild(upload);
    }
}


/* 전역 변수 */

var IS_NAV = (navigator.appName == "Netscape");
var IS_IE = (navigator.appName == "Microsoft Internet Explorer");

var IS_WIN = (navigator.userAgent.indexOf("Win") != -1);
var IS_MAC = (navigator.userAgent.indexOf("Mac") != -1);
var IS_UNIX = (navigator.userAgent.indexOf("X11") != -1);

/**
 * 쿠키에 저장된 값을 반환한다.
 * @param name 쿠키 이름
 * @return 쿠키 이름에 대한 값을 반환. 없는 경우에는 ""를 반환.
 */
function getCookie(name) {
    var arg = name + "=";
    var alen = arg.length;
    var clen = document.cookie.length;
    var i = 0;
    while (i < clen) {
        var j = i + alen;
        if (document.cookie.substring(i, j) == arg) {
            return getCookieVal(j);
        }
        i = document.cookie.indexOf(" ", i) + 1;
        if (i == 0) break;
    }
    return "";
}


/**
 * 쿠키를 저장한다.
 * @param name 쿠키 이름
 * @param value 쿠키 값
 * @param expires 쿠키의 유효 일
 * @param path
 * @param domain
 * @param secure
 */
function setCookie(name, value, expires, path, domain, secure) {
    if (!path) {
        path = "/";
    }
    document.cookie = name + "=" + escape (value) +
                    ((expires) ? "; expires=" + expires : "") +
                    ((path) ? "; path=" + path : "") +
                    ((domain) ? "; domain=" + domain : "") +
                    ((secure) ? "; secure" : "");
}


/**
 * 쿠키를 삭제한다.
 * @param name 삭제할 쿠키 이름
 * @param path
 * @param domain
 */
function deleteCookie(name, path, domain) {
    if (!path) {
        path = "/";
    }
    if (getCookie(name)) {
        document.cookie = name + "=" +
            ((path) ? "; path=" + path : "") +
            ((domain) ? "; domain=" + domain : "") + 
            "; expires=Thu, 01-Jan-70 00:00:01 GMT";
    }
}


/**
 * 쿠키를 저장할 때 필요한 적합한 형식의 유효기간을 반환한다.
 * @days 쿠키가 유효할 일 (예를 들어 3 일 동안 유효해야 하면 3을 입력)
 * @hours 쿠키가 유효할 시간 (예를 들어 2 시간 동안 유효해야 하면 2를 입력)
 * @minutes 쿠키가 유효할 분 (예를 들어 30 분 동안 유효해야 하면 30을 입력)
 */
function getExpDate(days, hours, minutes) {
    var expDate = new Date( );
    if (typeof days == "number" && typeof hours == "number" &&
        typeof hours == "number") {
        expDate.setDate(expDate.getDate( ) + parseInt(days));
        expDate.setHours(expDate.getHours( ) + parseInt(hours));
        expDate.setMinutes(expDate.getMinutes( ) +
        parseInt(minutes));
        return expDate.toGMTString( );
    }
}


/**
 * 쿠키 값을 읽을 때 사용하는 보조 함수
 */
function getCookieVal(offset) {
    var endstr = document.cookie.indexOf (";", offset);
    if (endstr == -1) {
        endstr = document.cookie.length;
    }
    return unescape(document.cookie.substring(offset, endstr));
}

/**
 * 입력받을 수 있는 값을 필터링한다.
 * ex : <input type="text" ..... onkeypress="filterKey('[0-9]')"> ; 숫자만 키입력이 가능한 text filed
 * ex : <input type="text" ..... onkeypress="filterKey('[0-9a-zA-Z]')"> ; 영문,숫자만 키입력이 가능한 text filed
 * @param filter : 필터링할 정규표현식 ex) '[0-9]':0~9의 값만 허용, '[a-zA-Z]':알파벳만 허용
 * @return 
 */
function filterInputData(filter) {
    if (filter) {
        var sKey = String.fromCharCode(event.keyCode);
        var re = new RegExp(filter);
        if (!re.test(sKey)) {
            event.returnValue = false;
        }
    }
}


/**
 * 주어진 텍스트(source)에 특정 문자(query)가 나타나는 횟수를 반환한다.
 * @param source 대상이 되는 텍스트
 * @param query 검색하려는 텍스트
 * @return 주어진 텍스트에 특정 문자가 나타나는 횟수
 */
function countInstances(source, query) {
    var re = new RegExp(query, "g");
    var result = source.match(re);
    return (result) ? result.length : 0;
}

/**
 * <div> 태그의 내용 중 특정 텍스트(before)를 특정 텍스트(after)로 변경한다.
 * @param id <div> 태그의 id 속성 값
 * @param before 변경 전 텍스트
 * @param after 변경 후 텍스트
 */
function replaceTextInDiv(id, before, after) {
    var element = document.getElementById(id).firstChild;
    var re = new RegExp(before, "g");
    element.nodeValue = element.nodeValue.replace(re, after);
    return false;
}

function writeTextInElement(id, text) {
    var element = document.getElementById(id);
    if (element.firstChild) {
        element.firstChild.nodeValue = text;
    } else {
        var child = document.createTextNode(text);
        element.appendChild(child);
    }
    return false;
}

/**
 * 입력 변수에 3 자리마다 콤마(,)를 붙여 반환한다.
 * @param field 콤마를 붙일 값
 */
function formatCommas(numString) {
    var re = /,|\s+/g;
    numString = numString.replace(re, "");

    re = /(-?\d+)(\d{3})/;
    while (re.test(numString)) {
        numString = numString.replace(re, "$1,$2");
    }
    return numString;
}

function stripCommas(numString) {
    var re = /,/g;
    return numString.replace(re, "");
}

/**
 * 텍스트 필드에 입력한 값에 3자리마다 콤마(,)를 붙인다.
 * 텍스트 필드에 아래를 기입한다. onkeyup="toMoney(this)"
 * @param field 텍스트 필드
 */
function toMoney(field) {
    var value = field.value;
    var indexOfPoint = value.indexOf(".");
    if (indexOfPoint == -1) {
        field.value = formatCommas(value);
    } else {
        field.value = formatCommas(value.substring(0, indexOfPoint)) +
                        value.substring(indexOfPoint, value.length);
    }
}


/**
 * 두 날짜 사이의 일수를 계산하여 반환한다.
 * @param date1 문자열 데이터로 '20041012' 형식
 * @param date2 문자열 데이터로 '20041012' 형식
 */
function daysBetween(date1, date2) {
    date1 = new Date(date1.substring(0, 4), date1.substring(4, 6)-1, date1.substring(6,8));
    date2 = new Date(date2.substring(0, 4), date2.substring(4, 6)-1, date2.substring(6,8));
    var DSTAdjust = 0;
    oneMinute = 1000 * 60;
    var oneDay = oneMinute * 60 * 24;
    date1.setHours(0);
    date1.setMinutes(0);
    date1.setSeconds(0);
    date2.setHours(0);
    date2.setMinutes(0);
    date2.setSeconds(0);
    DSTAdjust = (date2.getTimezoneOffset( ) - 
                     date1.getTimezoneOffset( )) * oneMinute;
    var diff = date2.getTime( ) - date1.getTime() - DSTAdjust;
    return Math.ceil(diff/oneDay);
}


/**
 * 특정 노드가 가지고 있는 모든 속성을 TEXTAREA에 출력한다. 개발 편의를 위해서
 * 제공되는 함수이다.
 * @param obj 속성 값을 알고자 하는 노드
 */
function listProperties(obj) {
    var objName;

    if (obj.nodeName) {
        objName = obj.nodeName;
    } else {
        objName = "navigator";
    }

    var result = "";
    for (var i in obj) {
        result += objName + "." + i + "=" + obj[i] + "\n";
    }

    var area = document.createElement("textarea");
    area.rows = 20;
    area.cols = 50;
    var body = document.getElementsByTagName("BODY");
    if (body) {
        body[0].appendChild(area);
    } else {
        alert("body 태그가 있어야 합니다.");
        return false;
    }
    area.value = result;
    return false;
}

function getQueryString() {
    var result = "";
    var queryTags = document.getElementsByTagName("input");
    for (var i = 0; i < queryTags.length; i++) {
        var name = queryTags[i].name;
		if (name && name.substring(0, 2) == "q_" || name == "pageNo") {
		    result += "&" + name + "=" + queryTags[i].value;
		}
    }

    queryTags = document.getElementsByTagName("select");
    for (var i = 0; i < queryTags.length; i++) {
        var name = queryTags[i].name;
		if (name && name.substring(0, 2) == "q_" || name == "pageNo") {
		    result += "&" + name + "=" + queryTags[i].value;
		}
    }
    return result;
}


/**
 * 텍스트 필드에 입력한 값에 6자리 이후에 대시(-)를 붙인다.
 * 텍스트 필드에 아래를 기입한다. onkeyup="toSsn(this)"
 * @param field 텍스트 필드
 */
function toSsn(field) {
    var number = String(field.value);
    number = number.replace(/-|\s+/g, "");

    var length = number.length;

    if (length < 6) {
        field.value = number;
    } else {
        var result = number.substring(0, 6) + "-";
        result += number.substring(6, length);
        field.value = result;
    }
}

/*

  JForm   : 검증할 모든 객체를 담는데 사용.
  JText   : 텍스트 데이터를 검증하는데 사용.
  JNumber : 숫자 데이터를 검증하는데 사용.
  JMoney  : 화폐 데이터를 검증하는데 사용.
  JSelect : Drop/Down 데이터를 검증하는데 사용.
  JChcek  : 체크박스와 라디오 버튼을 검증하는데 사용.
  JSsn    : 주민등록번호를 검증하는데 사용.
  JMail   : 이메일 주소를 검증하는데 사용.
  JDate   : 날짜 데이터를 검증하는데 사용.
  JFile   : 파일 업로드 데이터를 검증하는데 사용.

*/

var DAY_OF_WEEK = new Array("일","월","화","수","목","금","토");

function JForm() {

    /* Member field */
    this.children = new Array

    /* Member Method */
    this.add = jform_add;
    this.validate = jform_validate;

    return this;
}

function jform_add(child) {
    this.children[this.children.length] = child;
    return this;
}

function jform_validate() {
    for (var i = 0; i < this.children.length; i++) {
        if (!this.children[i].validate()) {
            return false;
        }
    }
    return true;
}

function JText(name, object) {

    /* Member Field */
    this.name = name;
    this.object = object;

    this.min;
    this.max;
    
    this.filterCheck;
    this.nullCheck = true;
    this.rangeCheck;

    /* Member Method */
    this.validate = jtext_validate;
    this.nullable = jtext_nullable;
    this.range = jtext_range;
    this.filter = jtext_filter;
    this.focus = jtext_focus;

    return this;
}

function jtext_validate() {

    var value = this.object.value;

    if (this.nullCheck && trimmed(value).length == 0) {
        alert(messages.get("JSM-1004", ul(this.name)));
        return this.focus();
    }
    
    if (this.rangeCheck && (trimmed(value).length != 0) && !checkCharacterSize(value, this.min, this.max)) {
        if (this.min == this.max) {
            messages.alert("JSM-1005", [this.name, this.min]);
        } else {
            messages.alert("JSM-1006", [this.name, this.min, this.max]);
        }
        return this.focus();
    }

    if (this.filterCheck && isSpecial(value)) {
        messages.alert("JSM-1007", [this.name]);
        return this.focus();
    }
    return true;
}

function jtext_nullable() {
    this.nullCheck = false;
    return this;
}

function jtext_range(min, max) {
    this.rangeCheck = true;
    this.min = min;
    this.max = max;
    return this;
}

function jtext_filter() {
    this.filterCheck = true;
    return this;
}

function jtext_focus() {
    this.object.focus();
    this.object.select();
    return false;
}

function JNumber(name, object) {

    /* Member Field */
    this.name = name;
    this.object = object;
    this.nullCheck = true;
    this.rangeCheck;
    
    this.min;
    this.max;

    /* Member Method */
    this.validate = jnumber_validate;
    this.nullable = jtext_nullable;
    this.range = jtext_range;
    this.focus = jtext_focus;
    
    return this;
}

function jnumber_validate() {

    var value = this.object.value;

    if (this.nullCheck && trimmed(value).length == 0) {
        messages.alert("JSM-1004", ul(this.name));
        return this.focus();
    }

    if (isNaN(value)) {
        messages.alert("JSM-1008", this.name);
        return this.focus();
    }
    
    if (this.rangeCheck && (trimmed(value).length != 0) && !checkNumberSize(value, this.min, this.max)) {
        if (this.min == this.max) {
            messages.alert("JSM-1009", [this.name, this.min]);
        } else {
            messages.alert("JSM-1010", [this.name, wa(this.min), this.max]);
        }
        return this.focus();
    }
    return true;
}

function JSelect(name, object) {

    /* Member Field */
    this.name = name;
    this.object = object;

    this.min;
    this.max;

    this.nullCheck = true;
    this.rangeCheck;
    
    /* Member Method */
    this.validate = jselect_validate;
    this.nullable = jtext_nullable;
    this.range = jtext_range;
    this.focus = jselect_focus;

    return this;
}

function jselect_validate() {

    var value = this.object.value;

    if (this.nullCheck && trimmed(value).length == 0) {
        messages.alert("JSM-1011", ul(this.name));
        return this.focus();
    }

    var number = isSelected(this.object);

    if (this.rangeCheck && number != 0 && (number < this.min || number > this.max)) {
        if (this.min == this.max) {
            messages.alert("JSM-1012", [this.name, this.min]);
        } else {
            messages.alert("JSM-1013", [this.name, wa(this.min), this.max]);
        }
        return this.focus();
    }

    return true;
}

function jselect_focus() {
    this.object.focus();
    return false;
}

function isSelected(item) {
    if (item == null) return 0;
    var result = 0;

    for (var i = 0; i < item.length; i++) {
        if (item[i].selected) {
            result++;
        }
    }
    return result;
}

function JCheck(name, object) {

    /* Member Field */
    this.name = name;
    this.object = object;
    
    this.min;
    this.max;

    this.nullCheck = true;
    this.rangeCheck;

    /* Member Method */
    this.validate = jcheck_validate;
    this.nullable = jtext_nullable;
    this.range = jtext_range;
    this.focus = jcheck_focus;

    return this;
}

function jcheck_validate() {

    var number = isChecked(this.object);
    if (this.nullCheck && number == 0) {
        messages.alert("JSM-1011", ul(this.name));
        return this.focus();
    }

    if (this.rangeCheck && number != 0 && (number < this.min || number > this.max)) {
        if (this.min == this.max) {
            messages.alert("JSM-1012", [this.name, this.min]);
        } else {
            messages.alert("JSM-1013", [this.name, wa(this.min), this.max]);
        }
        return this.focus();
    }
    return true;
}

function jcheck_focus() {
    if(this.object.length==1){
    	this.object.focus();
    } else if( this.object.length>1) {
    	this.object[0].focus();
    }
    return false;
}

function isSpecial(s) {
    for (i = 0; i < s.length; i++) {   
        /* Check that current character letter. */
        var c = s.charAt(i);
        if (c == "'" || c == "." || c == "\"") {
            return true;
        }
    }
    return false;
}

function isChecked(list) {
    if (list == null) return 0;
    var result = 0;

    /* list array의 데이터가 1인 경우 */
    if (list.checked) {
        return 1;
    }
    for (var i = 0; i < list.length; i++) {
        if (list[i].checked) {
            result++;
        }
    }
    return result;
}

function trimmed(value) {
    value = value.replace(/^\s+/, "");  /* remove leading white spaces */
    value = value.replace(/\s+$/g, ""); /* remove trailing while spaces */
    return value;
}

function checkCharacterSize(data, min, max) {
    var total = 0;

    for (var i = 0; i < data.length; i++) {
        var a = data.charAt(i);
        /* 한글인 경우 길이가 6 이다. */
        if (escape(a).length >= 6) {
            total = total + 2;
        } else {
            total = total + 1;
        }
    }
    return total >= min && total <= max;
}

function checkNumberSize(data, min, max) {
    if(max <= 0) {
        return data >= min;
    } else {
        return data >= min && data <= max;
    }
}

function JMoney(name, object) {

    /* Member Field */
    this.name = name;
    this.object = object;
    this.nullCheck = true;
    this.rangeCheck;
    
    this.min;
    this.max;

    /* Member Method */
    this.validate = jmoney_validate;
    this.nullable = jtext_nullable;
    this.range = jtext_range;
    this.focus = jtext_focus;

    return this;
}

function jmoney_validate() {

    var value = this.object.value;

    if (this.nullCheck && trimmed(value).length == 0) {
        messages.alert("JSM-1004", ul(this.name));
        return this.focus();
    }

    value = value.replace(/,|\s+/g, "");

    if (isNaN(value)) {
        messages.alert("JSM-1008", this.name);
        return this.focus();
    }

    if (this.rangeCheck && (trimmed(value).length != 0) && !checkNumberSize(value, this.min, this.max)) {
        if (this.min == this.max) {
            messages.alert("JSM-1014", [this.name, this.min]);
        } else {
            messages.alert("JSM-1015", [this.name, wa(this.min), this.min]);
        }
        return this.focus();
    }
    return true;
}

function JSsn(name, object) {

    /* Member Field */
    this.name = name;
    this.object = object;
    this.nullCheck = true;

    /* Member Method */
    this.validate = jssn_validate;
    this.nullable = jtext_nullable;
    this.focus = jtext_focus;

    return this;
}

function jssn_validate() {

    var value = this.object.value;

    if (isNaN(value.replace(/-|\s+/g, ""))) {
        messages.alert("JSM-1008", this.name);
        return this.focus();
    }

    if (!this.nullCheck && trimmed(value).length == 0) {
        return true;
    }

    var year = value.substring(0, 2);
    var month = value.substring(2, 4);
    var day = value.substring(4, 6);
    var sex = value.substring(6, 7);

    if ((value.length != 14 ) || (year < 25 || month < 1 || month >12 || day < 1)) {
        messages.alert("JSM-1016", ul(this.name));
        return this.focus();
    }

    if ((sex > 0 && sex <= 6 )) {
        messages.alert("JSM-1016", ul(this.name));
        return this.focus();
    }

    return true;
}

function JMail(name, object) {

    /* Member Field */
    this.name = name;
    this.object = object;

    this.nullCheck = true;

    /* Member Method */
    this.validate = jmail_validate;
    this.nullable = jtext_nullable;
    this.focus = jtext_focus;

    return this;
}

function jmail_validate() {

    var value = this.object.value;

    if (this.nullCheck && trimmed(value).length == 0) {
        messages.alert("JSM-1004", ul(this.name));
        return this.focus();
    }
    
    if (!this.nullCheck && trimmed(value).length == 0) {
        return true;
    }

    /* 이메일 주소의 허용되는 최대 크기 체크 */
    
    var re = new RegExp("(^[_0-9a-zA-Z-]+(\.[_0-9a-zA-Z-]+)*@[0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*$)");
    if (!re.test(value)) {
        messages.alert("JSM-1017", un(this.name));
        return this.focus();
    }
    return true;
}

/**
 *    Date 타입 검증에 사용된다.
 */
function JDate(name, object, re) {

    /* Member Field */
    this.name = name;
    this.object = object;
     
    this.nullCheck = true;
    this.rangeCheck = false;
    
    this.date = null;
    
    if (re)  {
    	this.re = re;
    } else {
		this.re = /^\d{4}[-.\/]?\d{2}[-.\/]?\d{2}$/;
    }
    
    this.min = null; /* YYYYMMDD Format */
    this.max = null; /* YYYYMMDD Format */
    
    this.format = "YYYY년MM월DD일(DAY)";

    /* Member Method */
    this.validate = jdate_validate;
    this.nullable = jtext_nullable;
    this.focus = jtext_focus;
    this.range = jtext_range;
    
    this.toDate = jdate_toDate;
    this.parse = jdate_parse;
    this.toString = jdate_toString;
    
    this.getYear  = jdate_getYear;
    this.getMonth = jdate_getMonth;
    this.getDate  = jdate_getDate;    
    this.getDay = jdate_getDay;

    this.parse();
    
    return this;
}

/* 데이터 타입 유효성 테스트  */
function jdate_validate() {

    var value = this.object.value;

    if (this.nullCheck && trimmed(value).length == 0) {
        messages.alert("JSM-1004", ul(this.name));
        return this.focus();
    }
    
    if (!this.nullCheck && trimmed(value).length == 0) {
        return true;
    }
  
    if (this.date == null) {
        messages.alert("JSM-1018", wa(this.name));
        return this.focus();    
    } 
    
    if (this.rangeCheck) {
        var aDate = this.toDate();
        var minDate, maxDate;
        
        if (this.min != null) {
            minDate = new JDate().parse(this.min);
        } else {
            minDate = new JDate().parse("10000101");
        }
        if (this.max != null) {
            maxDate = new JDate().parse(this.max);
        } else {
            maxDate = new JDate().parse("99991231");
        }
        if (this.min == null && aDate > maxDate.toDate()) {
            messages.alert("JSM-1019", [this.name, maxDate]);
            return this.focus();
            
        } else if (this.max == null && aDate < minDate.toDate()) {
            messages.alert("JSM-1020", [this.name, minDate]);
            return this.focus();
        } else if (aDate > maxDate.toDate() || aDate < minDate.toDate()) {
            messages.alert("JSM-1021", [this.name, minDate, maxDate]);
            return this.focus();
        }
    }
    
    return true;
}

/* Date 파싱  */
/* 날짜 데이터 인지 검증한다. 일차적으로는 정규식을 이용한다. */
/* 년,월,일 사이의 Delimiter는 ".-/" 중 하나이거나 빈문자을 사용한다. */
/* Date Object를 초기화하여 데이터가 적합한지 체크한다. */
function jdate_parse() {
    var value = this.object;
    if (jdate_parse.arguments.length > 0) {
        value = jdate_parse.arguments[0];
    } else if (this.object && typeof this.object=="object") {
        value = this.object.value;
    } else {
        this.date = new Date();
        return this;
    }
    
    this.date = null;
    if (value.search(this.re) >= 0) {
        value = value.replace(/[-.\/]/g,"");
        var aDate = new Date(value.substring(0,4),value.substring(4,6)-1,value.substring(6,8));
        if (   aDate.getFullYear()  == Math.abs(value.substring(0,4))
            && aDate.getMonth() == Math.abs(value.substring(4,6))-1 
            && aDate.getDate()  == Math.abs(value.substring(6,8)) ) {
            this.date = aDate;
        }
    }
    return this;
}

/* Date 타입으로 변환 */
function jdate_toDate() {
    return this.date;
}

function jdate_getYear() {
    return this.date == null ? 1000 : this.date.getFullYear();
}

function jdate_getMonth() {
    var num = (this.date == null ? 0 : this.date.getMonth()+1);
    return (num < 10 ? '0' + new String(num) : num);
}

function jdate_getDate() {
    var num = (this.date == null ? 0 : this.date.getDate());
    return (num < 10 ? '0' + new String(num) : num);
}

function jdate_getDay() {
    return (this.date == null ? DAY_OF_WEEK[0] : DAY_OF_WEEK[this.date.getDay()]);
}


/* 스트링으로 변환  */
function jdate_toString() {
    var formatString = this.format;
    if (jdate_toString.arguments.length > 0) {
        formatString = jdate_toString.arguments[0];
    }
    var str = formatString.replace(/YYYY/g , this.getYear());
    str = str.replace(/MM/g , this.getMonth());
    str = str.replace(/DD/g , this.getDate());
    str = str.replace(/DAY/g , this.getDay());
    str = str.replace(/yy/g , new String(this.getYear()).substring(2,4));
    return str;
}


/* 파일업로딩시 입력파일에 대한 검증을 실시한다.  */
function JFile(name, type) {
    /* Member Field */
    this.name = name;
    this.object = document.forms[0].j_file;
    this.type = type;

    this.nullCheck = true;
    this.allowedExtension;
    this.disallowedExtension;

    this.nullable = jtext_nullable;
    this.extension = jfile_extension;
    this.xExtension = jfile_xExtension;
    this.validate = jfile_validate;

    return this;
}

/*확장명 체크*/
function jfile_extension(array) {
    this.allowedExtension = array;
    return this;
}

/*입력금지된 확장명 체크*/
function jfile_xExtension(array) {
    this.disallowedExtension = array;
    return this;
}

function jfile_validate() {

	/*만땅 채운 upload를 수정할때 object는 없다. */
	if (!this.object) {
		return true;
	}

	/*IE 버그로 인한 입력값 정확성 체크 */
	var re = /^[a-z]:\\(.){0,300}$/i;
	if (!this.object.length) {
		var temp = this.object;
		this.object = new Array();
		this.object[0] = temp;
	}
	for (var i = 0; i < this.object.length; i++) {
        if (this.type) {            
            var extension = this.object[i].value;
            if (extension != "" && !extension.match(re)) {
            	messages.alert("JSM-1022");
            	this.object[i].focus();
        		return false;
            }
        }
    }
    
    if (this.nullCheck) {
        var countInputName = "j_file_count";
        if (this.type) {
            countInputName += "_" + this.type;
        }
        var countInput = document.forms[0][countInputName];
        if (!countInput || countInput.value < 1) {
            messages.alert("JSM-1023", this.name);
            return false;
        }
    }
    if (this.allowedExtension) {
        var passed = true;
        for (var i = 0; i < this.object.length; i++) {
            if (this.type) {
                var spanNode = this.object[i].nextSibling;
                if (!spanNode || spanNode.getElementsByTagName("input")[0].value != this.type) {
                    continue;
                }
            }
            var extension = this.object[i].value;
            if (trimmed(extension).length == 0) {
                continue;
            }
            var index = extension.lastIndexOf(".");
            if (index == -1) {
                passed = false;
                break;
            }
            extension = extension.substring(index + 1).toLowerCase();
            for(var j = 0; j < this.allowedExtension.length ; j++) {
                if (this.allowedExtension[j].toLowerCase() == extension) {
                    break;
                }
                if (j == this.allowedExtension.length - 1) {
                    passed = false;
                }
            }
        }
        if (!passed) {
            messages.alert("JSM-1024", [this.name, this.allowedExtension.join(", ")]);
            return false;
        }
    }
    if (this.disallowedExtension) {
        var passed = true;
        for (var i = 0; i < this.object.length; i++) {
            if (this.type) {
                var spanNode = this.object[i].nextSibling;
                if (!spanNode || spanNode.getElementsByTagName("input")[0].value != this.type) {
                    continue;
                }
            }
            var extension = this.object[i].value;
            if (trimmed(extension).length == 0) {
                continue;
            }
            var index = extension.lastIndexOf(".");
            if (index == -1) {
                break;
            }
            extension = extension.substring(index + 1).toLowerCase();
            for(var j = 0; j < this.disallowedExtension.length ; j++) {
                if (this.disallowedExtension[j].toLowerCase() == extension) {
                	passed = false;
                    break;
                }
                if (j == this.disallowedExtension.length - 1) {
                    passed = true;
                }
            }
        }
        if (!passed) {
            messages.alert("JSM-1025", [this.name, this.disallowedExtension.join(", ")]);
            return false;
        }
    }    
    return true;
}


/**
 *    Crontab Expression 타입 검증에 사용된다.
 */
function JCronExpression(name, object) {

    /* Member Field */
    this.name = name;
    this.object = object;
     
    this.nullCheck = true;
    
	this.re = new RegExp("^(((([0-9]|[0-5][0-9]),)*([0-9]|[0-5][0-9]))|(([0-9]|[0-5][0-9])(/|-)([0-9]|[0-5][0-9]))|([\\?])|([\\*]))[\\s](((([0-9]|[0-5][0-9]),)*([0-9]|[0-5][0-9]))|(([0-9]|[0-5][0-9])(/|-)([0-9]|[0-5][0-9]))|([\\?])|([\\*]))[\\s](((([0-9]|[0-1][0-9]|[2][0-3]),)*([0-9]|[0-1][0-9]|[2][0-3]))|(([0-9]|[0-1][0-9]|[2][0-3])(/|-)([0-9]|[0-1][0-9]|[2][0-3]))|([\\?])|([\\*]))[\\s](((([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1]),)*([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(C)?)|(([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(/|-)([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(C)?)|(L)|([\\?])|([\\*]))[\\s](((([1-9]|0[1-9]|1[0-2]),)*([1-9]|0[1-9]|1[0-2]))|(([1-9]|0[1-9]|1[0-2])(/|-)([1-9]|0[1-9]|1[0-2]))|(((JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC),)*(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))|((JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(-|/)(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))|([\\?])|([\\*]))[\\s]((([1-7],)*([1-7]))|([1-7](/|-)([1-7]))|(((MON|TUE|WED|THU|FRI|SAT|SUN),)*(MON|TUE|WED|THU|FRI|SAT|SUN)(C)?)|((MON|TUE|WED|THU|FRI|SAT|SUN)(-|/)(MON|TUE|WED|THU|FRI|SAT|SUN)(C)?)|(([1-7]|(MON|TUE|WED|THU|FRI|SAT|SUN))?(L)?)|([1-7]#([1-7])?)|([\\?])|([\\*]))([\\s]19[7-9][0-9]|20[0-9]{2})?$");       
    
    /* Member Method */
    this.validate = jcron_validate;
    this.nullable = jtext_nullable;
    this.focus = jtext_focus;
    
    return this;
}

function jcron_validate() {

    var value = this.object.value;

    if (this.nullCheck && trimmed(value).length == 0) {
        messages.alert("JSM-1004", this.name);
        return this.focus();
    }
    
    if (!this.nullCheck && trimmed(value).length == 0) {
        return true;
    }
        
    if (!this.re.test(value)) {
    	messages.alert("JSM-1026", wa(this.name));
    	return this.focus();
    }
    
    return true;
}

/**
 *  Textarea에 NewLine으로 구분된 Properties 타입 검증에 사용된다.
 */
function JProperties(name, object, requiredProps) {

    /* Member Field */
    this.name = name;
    this.object = object;
     
    this.nullCheck = true;
    
    /* 콤마(,)로 구분된 필수필드명*/
    this.requiredProps = requiredProps;
   	this.re = new RegExp("^[_0-9a-zA-Z-\.]+=");
    
    /* Member Method */
    this.validate = jproperties_validate;
    this.nullable = jtext_nullable;
    this.focus = jtext_focus;
    
    return this;
}

function jproperties_validate() {

    var value = this.object.value;
    var props = new Array();

    if (this.nullCheck && trimmed(value).length == 0) {
        messages.alert("JSM-1004", this.name);
        return this.focus();
    }
    
    if (!this.requiredProps && !this.nullCheck && trimmed(value).length == 0) {
        return true;
    }
    
    var lines = trimmed(value).split("\n");
    for(var i=0; i < lines.length ; i++) {
    	if (!this.re.test(lines[i])) {
    		messages.alert("JSM-1027", [this.name, un(lines[i])]);
    		return this.focus();
    	} else {
    		var idx = lines[i].indexOf("=");
    		props[lines[i].substring(0,idx)] = lines[i].substring(idx+1,lines[i].length);
    	}
    }
    
    if(this.requiredProps) {
    	var rProps = this.requiredProps.split(",");
    	for(var i=0; i<rProps.length ; i++) {
    		if(!props[rProps[i]]) {
        		messages.alert("JSM-1028", [this.name, ka(rProps[i])]);
    			return this.focus();
    		}
    	}
    }
    
    return true;
}

/* 내부함수 (한글 종성체크) */
function isJongsong(wd) {

    var INDETERMINATE = 0;
    var NOJONGSONG = 1;
    var JONGSONG = 2;

    word = new String(wd);                    /* 숫자가 들어오는 등에 대비해 무조건 문자열로 바꿈 */
    numStr1 = "013678lmnLMN";                 /* '조' 전까지는 0이 받침이 있는걸로 나옴 --; */
    numStr2 = "2459aefhijkoqrsuvwxyzAEFHIJKOQRSUVWXYZ";
    /* bdgpt들은 읽기에 따라 받침이 있기도 하고 없기도 한다고 판단. */
    /* 대문자는 단독으로 읽을 때를 감안하면 받침 있다고 확정되는 것이 더 적음. */
    
    if (word == null || word.length < 1) {
        return INDETERMINATE;
    }
    
    lastChar = word.charAt(word.length - 1);
    lastCharCode = word.charCodeAt(word.length - 1);
    
    if (numStr1.indexOf(lastChar) > -1) {
        return JONGSONG;
    }else if (numStr2.indexOf(lastChar) > -1) {
        return NOJONGSONG;
    }
    
    if (lastCharCode<0xac00 || lastCharCode>0xda0c) {
        return INDETERMINATE;
    }
    else{
        lastjongseong = (lastCharCode - 0xAC00) % (21*28) % 28  ;
        
        if (lastjongseong == 0){
            return NOJONGSONG;
        }else{
            return JONGSONG;
        }
    }
}

/* 내부함수 (을/를) */
function ul(s) {
    var ul0 = new Array("(을)를", "를", "을");
    return s + ul0[isJongsong(s)];
}

/* 내부함수 (이/가) */
function ka(s){
    var ka0 = new Array("(이)가", "가", "이");
    return s + ka0[isJongsong(s)];
}

/* 내부함수 (은/는) */
function un(s){
    var un0 = new Array("(은)는", "는", "은");
    return s + un0[isJongsong(s)];
}

/* 내부함수 (와/과) */
function wa(s){
    var arr = new Array("(와)과", "와", "과");
    return s + arr[isJongsong(s)];
}



