본문 바로가기

NICE/X-Platform

lfn_validate() : 주민등록번호 체크


//----------------------------------------------------------------------
// lfn_validate() : 주민등록번호 체크
//----------------------------------------------------------------------
function lfn_validate(grd, nRow, nCol, ds){
  if(nCol == 6){//주민번호
  var t_juminNo = smf_Str(dsMain.getColumn(nRow,"stdntRrnEncpt"));
  var t_birthday = t_juminNo.substring(0,6);   
  var iBirthday = "";

  /** 생년월일 형식 체크 **/
  if (t_juminNo.substring(0,1) != "0") {
   iBirthday = "19" + t_birthday;
  } else {
   iBirthday = "20" + t_birthday;
  }
  
  if(t_juminNo.length != 13){
   return utlf_getMessage("err.his.srg.3003");//주민번호는 최소 13자리여야 합니다.
  }
  //주민번호 체크후 저장
  trace('iBirthday = ' + iBirthday);
  var chkBirthday = utlf_IsDate(iBirthday);  
  //저장전에 Validate를 체크
  var juminChk = utlf_IsRsrNo(t_juminNo);

  if (chkBirthday == false) {
   return utlf_getMessage("err.his.srg.3004"); //주민등록번호 앞6자리가 유효하지 않습니다.
  }
  if (t_juminNo.length == 13 && juminChk == false && chkBirthday == true) {
   dsMain.rowposition = i;   
   if(utlf_confirm("err.his.srg.3005")){//잘못된 주민등록번호 입니다.\n입력하시겠습니까?
    return true;
   }else{    
    return utlf_getMessage("err.his.srg.3012");//주민등록 번호를 입력하세요.
   }
  }
 }
 return true;
}




<<공통스크립트>>---------------------------------------------------------------------------

/**
 * @fileoverview sm_script
 * @author 문호상
 * @version 1.9   버전
 */

include "LIB::utl_script.xjs"
include "LIB::svc_script.xjs"
include "LIB::pop_script.xjs"
include "LIB::web_script.xjs"

var frmFormSpy = null;
var smv_HasNodifySubject = false; //Form에 dsSubjectNotify데이터셋이 있는지 확인
var smv_IgnoreInvalidConfig = false; //잘못된 설정을 alert으로 찍는걸 무시할지 여부
var sLinebreak = "\r\n";
var True = "True";
var False = "";
var smv_Onloading = true;
var smv_Resize = false;
var smv_IsTabpagedMainForm = false; //초등학교처럼 tabpage처럼 관리하는 mainform인지
var smv_IsTabpagedSubForm = false; //초등학교처럼 tabpage처럼 관리되는 subform인지
var smv_Positions = [];

/**
 * localhost에서 실행한건지 아닌지
 *
 * @param boolean
 * @return void
 */
function smf_IsLocalMode(){
 var b;
 try{
  b = -1 != smf_Str(gv_url).indexOf("localhost");
 }catch(e){
  return false;
 }

 return b;
}

/**
 * 개발WAS에서 실행한건지 아닌지
 *
 * @param boolean
 * @return void
 */
function smf_IsDevMode(){
 var b;
 try{
  b = -1 != smf_Str(gv_url).indexOf("112.136.170.16");
 }catch(e){
  return false;
 }

 return b;
}

/**
 * 개발자에게 공지를 함.
 *
 * @param boolean
 * @return void
 */
function smf_AlertDevelopeMessage(sMessage){
 if(! smf_IsLocalMode()) return false;
 //alert(new Date().toFormatString("%Y-%m-%d %H:%M:%S"));
 var nHour = toNumber(new Date().toFormatString("%H"));
 var nMinute = toNumber(new Date().toFormatString("%M"));

 //2시간에 1분씩 3번만 공지
 if((nHour % 2) == 0 && (nMinute % 20) == 0){
  alert("** 개발공지 반드시 읽어주세요 ** \n\n" + sMessage + "\n\n이 메시지는 Local환경에서만 나타납니다.");
 }

 return true;
}

function smf_AlertInvalidConfig(sMessage){
 if(smv_IgnoreInvalidConfig == true) return;

 alert(sMessage);
}

/**
 * trace message
 *
 * @param sMessage
 * @return void
 */
function trace(sMessage){
    application.trace(sMessage);

    if(frmFormSpy != null){
        try{
            frmFormSpy.tab.tabLog.taLog.value += sMessage + "\n";
        }catch(e){
        }
    }
}

var smv_DevMessage = " ***교무업무만 해당합니다***\n"
                   + " 1. 초,중,고,특 조회조건이 개발되었습니다.\n"
                   + "    * 옆의 문서를 참고하십시오.>> OPENPMS의 메시지-게시-자료실(SPM)-[개발가이드] 교무공통 권한처리 조회조건\n"
                   + "    조회조건을 업무에 적용하기 힘드신분은 문호상대리에게 연락(주로 쪽지)를 주세요.\n"
                   + "    창재 조회조건은 11월부터 시작합니다. 많은 양해 부탁드립니다.\n"
                   ;
/**
 * Form이 로드되면 호출하는 함수
 *
 * @param  frm     호출한 객체
 * @return void
 */
function smf_InitForm(frm){
    smf_SaveInitPosition(frm);
 try{
  utlf_frmLoadsetEnv(frm);
 }finally{
  //임시임...
  smf_AddSession(frm);
  smf_Init(frm);
 }
 //smf_AlertDevelopeMessage(smv_DevMessage);
}

/**
 * Popup이 로드되면 호출하는 함수
 *
 * @param  frm     호출한 객체
 * @return void
 */
function smf_InitPopup(frm){
    smf_SaveInitPosition(frm);
 try{
  utlf_PopupLoadsetEnv(frm);
 }finally{
  var oSt_title = frm.all['st_title'];
  if(oSt_title == null) oSt_title = frm.all['sta_title'];
  if(oSt_title == null) oSt_title = frm.all['main_title'];

  if(oSt_title != null){
   oSt_title.text = frm.titletext;
  }

  smf_Init(frm);
 }
}

/**
 * URL로 연결된 Div나 Tabpage 초기화함수
 *
 * @param  frm     호출한 객체
 * @return void
 */
function smf_InitDiv(frm){
    smf_SaveInitPosition(frm);
    try{
        utlf_divLoadsetEnv(frm);
    }finally{
        smf_Init(frm);

        //onload callback함수가 있으면 호출
        if(smf_OwnerComponent(frm).smf_HasFunc != undefined){
   smf_OwnerComponent(frm).smf_DoLoadComponent(frm);
  }
    }
}

function smf_DoLoadComponent(obj){
//  if(smv_Resize == true){
//   smf_EndResize(this);
//  }

 if(smf_OwnerComponent(obj).smf_HasFunc != undefined){
  if(smf_OwnerComponent(obj).smf_HasFunc("lfn_DoLoadComponent")){
   smf_OwnerComponent(obj).lfn_DoLoadComponent(obj);
  }
 }
}

/**
 * 팝업을 안보이게
 *
 * @return void
 */
function smf_HidePopup(){
 this._nOldX = getOwnerFrame().position.x;
 getOwnerFrame().position.x = -10000;
}

/**
 * 팝업을 보이게
 *
 * @param  frm     호출한 객체
 * @return void
 */
function smf_ShowPopup(){
 getOwnerFrame().position.x = this._nOldX;
}

/**
 * (공통만사용)로드되면 호출하는 함수
 *
 * @param  frm  form
 * @return void
 * @see
 */
function smf_Init(frm){
    //onload중임을 마치는 함수
    frm.onactivate.addHandler(function (obj, e){
        smv_Onloading = false;
    });

    var oSt_title = frm.all['st_title'];

    if(oSt_title == null){
  oSt_title = frm.all['sta_title'];
    }

    if(oSt_title == null){
  oSt_title = frm.all['main_title'];
    }

    if(oSt_title != null){
//  oSt_title.text = "타이틀입니다.[디버깅..]";
  oSt_title.style.cursor = "hand";
  oSt_title.onclick.addHandler(function (obj, e){
   utlf_Open("test", "COMM::FormSpy.xfdl");
  });

  //디비에 있는 값으로 타이틀을 설정한다.
  if(isNotNil(this.pv_frmTitle)){
   oSt_title.text = this.pv_frmTitle;
  }
    }

 

    smf_CreateVariable();

    try{
        var dummy = lfn_trans_callback;
    }catch(e){
  smf_AlertInvalidConfig('콜백함수 lfn_trans_callback(svcid, errcd, errmsg)를 반드시 정의하세요');
    }

 smv_HasNodifySubject = ! isNil(objects('dsNotifySubject'));
    smf_UpdateServiceDataset();
 smf_CreateGridDataset(); //Grid를 담고 있는 데이터셋을 생성.
 smf_ResetGridConfig();

 smf_ValidatesService();
    smf_RegisterHandlers(frm);

    //local에서만 돌아가게..
    if(smf_IsLocalMode()){
        smf_ValidStandard();
    }

    //tabHisPrimary를 가지고 있으면
    if(isNotNil(components["tabPrimary"]) ||
       isNotNil(components["tabHisPrimary"]) ||
       isNotNil(components["tabMisPrimary"]) ||
       isNotNil(components["tabElsPrimary"]) ||
       isNotNil(components["tabSpsPrimary"])){
  smv_IsTabpagedMainForm = true;
  frm.onbeforeclose.addHandler(smf_TabpagedMainFormClose);
    }

    //tabpage로 관리되는 subform인지
    if(parent instanceof Tab && In(parent.name, ["tabHisPrimary", "tabMisPrimary", "tabElsPrimary", "tabSpsPrimary"])){
  smv_IsTabpagedSubForm = true;
    }
}

/**
 *
 * @param  frm  form
 * @param  e    event
 * @return void
 * @see
 */
function smf_SaveInitPosition(obj){
 var mainForm = smf_ForceMainForm(obj);
    smf_TravelComponents(this, function (oComp, fnc, nLvl){
  if(oComp instanceof Div || oComp instanceof Tab){
   if(oComp instanceof Tabpage) return;
   var jPosition = {width : oComp.position.width, height : oComp.position.height };
   mainForm.smv_Positions[smf_FullId(oComp)] = jPosition;
  }
    });
}

function smf_DescSaveInitPosition(mainForm){
    var arySoredPositions = mainForm.smv_Positions.sort();
 var sDesc = "";
 for(var sName in arySoredPositions){
  sDesc += format("{0} : width = {1}, height = {2}\n",
               [sName, arySoredPositions[sName].width, mainForm.smv_Positions[sName].height]);
 }
 
 trace(sDesc);
 alert(sDesc);
}

/**
 * (공통만사용)탭으로 관리되는 화면이 종료될때
 * SubForm에 dsNotifySubject에 FormClose에 값이 있으면
 * CloseConfirm을 띄워준다.
 *
 * @param  frm  form
 * @param  e    event
 * @return void
 * @see
 */
function smf_TabpagedMainFormClose(form, e){
 var tab = null;
 if(null != components["tabPrimary"]) tab = components["tabPrimary"];
 if(null != components["tabMisPrimary"]) tab = components["tabMisPrimary"];
 if(null != components["tabElsPrimary"]) tab = components["tabElsPrimary"];
 if(null != components["tabSpsPrimary"]) tab = components["tabSpsPrimary"];
 if(null != components["tabSpsPrimary"]) tab = components["tabSpsPrimary"];

 if(tab == null) return;

 //tabpages[i]들은 form들임.
 for(var i=0,size=tab.tabpages.length; i<size; i++){
  var oSubForm = tab.tabpages[i];
  if(isNil(oSubForm.url)) continue;
  if(oSubForm.smv_HasNodifySubject != true) continue; //dsNotifySubject가 있을경우만 실행한다.

  //closeconfirm을 실행해준다.
  if(! (new ModifyObserver(oSubForm)).closeNotifyWhenUpdatedData()){
   return false;
  }
 }
}

function smf_AddSession(frm){
 var sSystemName  = utlf_sysGbnNm(frm.name, "CD")[1] ;
 if(sSystemName != "HIS") return;

 utlf_setCache(sSystemName, "edc_yr_1","2010");  // 고등학고 1학년 교육년도
 utlf_setCache(sSystemName, "edc_yr_2","2010");  // 고등학고 2학년 교육년도
 utlf_setCache(sSystemName, "edc_yr_3","2010");  // 고등학고 3학년 교육년도
 utlf_setCache(sSystemName, "itrt_yr_1","2010"); // 고등학고 1학년 수업년도
 utlf_setCache(sSystemName, "itrt_yr_2","2010"); // 고등학고 2학년 수업년도
 utlf_setCache(sSystemName, "itrt_yr_3","2010"); // 고등학고 3학년 수업년도
 utlf_setCache(sSystemName, "edc_sem_1","1");    // 고등학교 1학년 교육학기
 utlf_setCache(sSystemName, "edc_sem_2","1");    // 고등학교 2학년 교육학기
 utlf_setCache(sSystemName, "edc_sem_3","1");    // 고등학교 3학년 교육학기
 utlf_setCache(sSystemName, "itrt_sem_1","1");   // 고등학교 1학년 수업학기
 utlf_setCache(sSystemName, "itrt_sem_2","1");   // 고등학교 2학년 수업학기
 utlf_setCache(sSystemName, "itrt_sem_3","1");   // 고등학교 3학년 수업학기
}

/**
 * (공통만사용)
 *
 * @return void
 * @see
 */
function smf_ValidStandard(){
    var jErrors = {};
    var sMessages = "\n";
    smf_TravelComponents(this, function (oComp, fnc, nLvl){
        var bInvalidDesign = false;
        var bInvalidProperty = false;


        var sMessage = "";
        var sComponentName = smf_FullId(oComp);
        var sComponentType = (oComp+"").substr(7);//[object Edit]
        sComponentType = sComponentType.substring(0, sComponentType.length-1).trim();

        if(oComp instanceof Calendar){
            if(smf_Str(oComp.dateformat).indexOf(".") == -1){
                bInvalidProperty = true;
                sMessage += format("Calendar {0}의 속성 {1}의 구분자 표준은 [.]입니다.\n", [sComponentName, 'dateformat']);
            }
            if(smf_Str(oComp.editformat).indexOf(".") == -1){
                bInvalidProperty = true;
                sMessage += format("Calendar {0}의 속성 {1}의 구분자 표준은 [.]입니다.\n", [sComponentName, 'editformat']);
            }
        }else if(oComp instanceof Static){
            if(! isNil(oComp.validate)){
                bInvalidProperty = true;
                sMessage += format("{0} {1}의 Static컴포넌트는 validate 속성을 가질수 없습니다.\n", [sComponentType, sComponentName, 'validate']);
            }
        }else if(oComp instanceof Edit || oComp instanceof TextArea){
            if(oComp.maxlength > 0){
                if(oComp.lengthunit != "utf8"){
                    bInvalidProperty = true;
                    sMessage += format("{0} {1}의 속성 {2}의 표준은 utf-8입니다.\n", [sComponentType, sComponentName, 'lengthunit']);
                }
            }
        }else if(oComp instanceof Grid){
            for(var i=0,size=oComp.getCellCount("body"); i<size; i++){
                var displaytype = smf_Str(oComp.getCellProperty("body", i, "displaytype"));
                if(displaytype.indexOf("date") != -1){
                    var mask = smf_Str(oComp.getCellProperty("body", i, "mask"));

                    if(mask.indexOf(".") == -1){
                        bInvalidProperty = true;
                        var sCellText = i;
                        try{
                            oComp.setCellProperty("head", i, "background", "cornflowerblue");
                            oComp.setCellProperty("body", i, "background2", "cornflowerblue");
                            sCellText = oComp.getCellProperty("head", i, "text") + format(" {0}/{1}번 cell", [i+1, size]);
                        }catch(e){}
                        sMessage += format("Grid {0}의 Body Band의 날짜타입 Cell[{1}] mask속성 표준은 [.]입니다.\n", [sComponentName, sCellText]);
                    }
                }
            }
        }

        if(bInvalidDesign || bInvalidProperty){
            if(bInvalidDesign) oComp.style.background.color = "cornflowerblue";
            if(bInvalidProperty) oComp.style.background.color = "cornflowerblue";

            sMessage += "\n";
            jErrors[sComponentName] = sMessage;

            oComp.tooltiptext = sMessage.replace("\n", "\r\n");
        }

        sMessages += sMessage;
    });

    if(! isNil(sMessages.trim())){
        alert("표준에 맞지 않은 디자인이나 설정들이 있습니다.\n로그를 확인하고 수정하세요\n* 컴포넌트 속성이 표준에 안맞는 경우 파랗게 표시했습니다.\n* 이 메시지는 개발시에만 나옵니다.(테스트 서버나 검수서버에선 이 메시지가 나오지 않습니다.)");
        trace(sMessages);
    }
}

/**
 * (공통만사용)
 *
 * @return void
 * @see
 */
function smf_UpdateServiceDataset(){
    if(null == this.objects["dsService"]) return;

    //공통에서 사용할 callback을 담을 컬럼을 추가한다.
    if(dsService.getColumnInfo("innerCallback") == null){
        dsService.addColumn("innerCallback", "string");
    }
   
    if(dsService.getColumnInfo("executeDate") == null){
        dsService.addColumn("executeDate", "string");
    }
   
    if(dsService.getColumnInfo("executeCount") == null){
        dsService.addColumn("executeCount", "string");
    }
}

/**
 * oOwner에 해당하는 Component를 재귀적으로 호출한다.
 *
 * @param  oOwner : Composite 컴포넌트(form, div, tab, tabpage)
 * @param  fncCallback : 재귀적으로 호출하는 function
 * @param  nLvl : component depth
 * @return void
 * @see    smf_TravelComponents(oOwner, fncCallback, bIgnoreUrl

 */
function smf_TravelComponents(oOwner, fncCallback, bIgnoreUrl, nLvl){
 if(oOwner == undefined || oOwner == null) oOwner = this;
 if(nLvl == undefined) nLvl = 1;
 if(bIgnoreUrl == undefined) bIgnoreUrl = true;


 for(var i=0,size=oOwner.components.length; i<size; i++){
  var oComp = oOwner.components[i];
  fncCallback(oComp, nLvl, oOwner);

  if(oComp instanceof Div){
            //url이 걸린 Div는 무시
            if(bIgnoreUrl == true && !isNil(oComp.url )) continue;
            smf_TravelComponents(oComp, fncCallback, bIgnoreUrl, nLvl+1);
  }else if(oComp instanceof Tab){
   smf_TravelComponents(oComp, fncCallback, bIgnoreUrl, nLvl+1);
  }else if(oComp instanceof Tabpage){
            //url이 걸린 Div는 무시
            if(bIgnoreUrl == true && !isNil(oComp.url )) continue;
            smf_TravelComponents(oComp, fncCallback, bIgnoreUrl, nLvl+1);
  }
 }
}

/**
 * (공통만사용)dsSubjectNotify에 정의된 이벤트를 설정하는 함수
 *
 * @param  frm : Formponent depth
 * @return void
 * @see
 */
function smf_RegisterHandlers(frm){
 if(! smv_HasNodifySubject) return;

 if( ! (new ModifyObserver(frm)).validates()) return;

 //폼이 닫힐때 컴폼
 frm.onbeforeclose.addHandler(function(frm, e){
  return (new ModifyObserver(frm)).closeNotifyWhenUpdatedData();
 });

 //Tab canchange 이벤트 핸들로 추가
 if(dsNotifySubject.lookup("type", "tabChange", "used") == "true"){
        smf_TravelComponents(this, smf_RegisterCanChangeEventToTab);
    }

 //Dataset의 canrowposchange될때 validate 체크
 smf_RegistDatasetCanRowPosChangedList(frm);
}


/**
 * (공통만사용)Tab이 tab canchange가 발생했을때 데이터 변경유무를 확인해서
 *             confirm하는 event 핸들러 추가 메소드
 *
 * @param  tab Tab
 * @return void
 * @see
 */
function smf_RegisterCanChangeEventToTab(tab){
    if(! (tab instanceof Tab)) return;

    tab.canchange.addHandler(function(obj:Tab,e:TabIndexChangeEventInfo){
  return (new ModifyObserver(this)).canchangeConfirmWhenModifyData(obj);
    });
}

/**
 * 폼의 모든 그리드의 설정을 Reset한다.
 * 보통 Tabpage를 다시 그렸을때 사용한다.
 *
 * @return void
 * @see
 */
function smf_ResetGridConfig(){
    _dsGridList.clearData();
 smf_AddGridIdToDataset('', this);
 smf_InitGrids();
}

/**
 * (공통만사용)Form에 정의된 Grid를 Dataset에 담는 Dataset을 생성
 *
 * @param  frm : Formponent depth
 * @return void
 * @see
 */
function smf_CreateGridDataset(){
 var ds = smf_GetOrCreateDataset('_dsGridList');
 ds.addColumn('gridName', 'String', 255);
}

/**
 * (공통만사용)Form에 정의된 Grid를 Dataset에 담기
 *
 * @param  void
 * @return void
 * @see
 */
function smf_AddGridIdToDataset(oOwnerName, oOwner){

 for(var i=0,size=oOwner.components.length; i<size; i++){
  var oComp = oOwner.components[i];
  var sParentName = "";

  if(oOwner == this){
   sParentName = oComp.name + ".";
  }else{
   sParentName = oOwnerName + oComp.name + ".";
  }

  if(oComp instanceof Div && isNil(oComp.url)){
   smf_AddGridIdToDataset(sParentName, oComp);
   continue;
  }

  if( oComp instanceof Tab){
   smf_AddGridIdToDataset(sParentName, oComp);
   continue;
  }

  if( oComp instanceof Tabpage  && isNil(oComp.url) ){
   smf_AddGridIdToDataset(sParentName, oComp);
   continue;
  }

  if(! (oComp instanceof Grid)) continue;

  var nRow = _dsGridList.addRow();
  if(oOwner == this){
   _dsGridList.setColumn(nRow, 'gridName', oOwnerName + oComp.name);
  }else{
   _dsGridList.setColumn(nRow, 'gridName', oOwnerName + oComp.name);
  }
 }
}

/**
 * (공통만사용)데이터셋에 공통 이벤트를 추가한다.
 *
 * @param  void
 * @return void
 * @see
 */
function smf_RegistDatasetCanRowPosChangedList(frm){
 for(var i=0,size=dsNotifySubject.rowcount; i<size; i++){
  var sType = dsNotifySubject.getColumn(i, 'type');
  var sUsed = dsNotifySubject.getColumn(i, 'used');

  if(sUsed != "true") continue;
  if(sType != "rowChange" && sType != "masterDetail") continue;
  var ds = eval(dsNotifySubject.getColumn(i, 'param01'));
  if(ds.canrowposchange.length != 0){
   var sMessage = format("데이터셋({0})에 이미 canrowposchange이벤트가 지정되어있습니다.\r\n"
         +"dsNotifySubject의 {1}/{2}행 {3}기능을 이용하려면\r\n데이터셋({0}) canrowposchange이벤트를 제거하세요",
         [ds.name, i+1, dsNotifySubject.rowcount, sType]);
   trace(sMessage);
   continue;
  }

  switch(sType){
   case "rowChange":
    if(ds.canrowposchange.length == 0){
     ds.canrowposchange.addHandler(smf_ValidateDatasetCanRCanRowPosChanged);
    }
    break;
   case "masterDetail":
    if(ds.canrowposchange.length == 0){
     ds.canrowposchange.addHandler(smf_ValidateDatasetCanRCanRowPosChanged);
    }
    break;
  }
 }
}

/**
 * 데이터셋이 변경되었는지
 *
 * @param  Dataset
 * @param  bWithoutChk _chk를 제외할건지
 * @return boolean
 * @see    smf_IsUpdate(ds); // true / false
 */
function smf_IsUpdate(ds, bWithoutCheck){
 var sWithoutColumnName = "_chk";
 if(bWithoutCheck == true) sWithoutColumnName = "__not exist column name__";
 if(ds.getDeletedRowCount() > 0 ) return true;

 for(var i=0,size=ds.getRowCount(); i<size; i++){
  switch(ds.getRowType(i)){
   case Dataset.ROWTYPE_INSERT: return true;
   case Dataset.ROWTYPE_UPDATE:
    if(smf_IsUpdateRow(ds, i, sWithoutColumnName)) return true;
    break;
  }
 }

 return false;
}

function smf_IsUpdateList(aryDataset){
 for(var i=0,size=aryDataset.length; i<size; i++){
  if(smf_IsUpdate(aryDataset[i])) return true;
 }
 
 return false;
}

/**
 * (공통만사용)순번셀에 insert, update 구별
 *
 * @param
 * @return boolean
 * @see
 */
function smf_RenderGridSeqCell(ds, sType, nCurRow, nRowType){
 switch(sType){
  case "text":
   switch(nRowType){
    case Dataset.ROWTYPE_INSERT: return "등록";
    case Dataset.ROWTYPE_UPDATE:
     if(smf_IsUpdateRow(ds, nCurRow)){
      return "수정";
     }else{
      return nCurRow+1;
     }
    default: return nCurRow+1;
   }
  case "background":
  case "background2":
   switch(nRowType){
    case Dataset.ROWTYPE_INSERT: return "#fad0fdff";
    case Dataset.ROWTYPE_UPDATE:
     if(smf_IsUpdateRow(ds, nCurRow)){
      return "#d6e4faff";
     }else{
      return "";
     }


    default: return "";
   }
 }

 return "";
}

/**
 * (공통만사용)State셀에 insert, update 구별
 *
 * @param
 * @return boolean
 * @see
 */
function smf_RenderGridStateCell(ds, sType, nCurRow, nRowType){
 switch(sType){
  case "text":
   switch(nRowType){
    case Dataset.ROWTYPE_INSERT: return "등록";
    case Dataset.ROWTYPE_UPDATE:
     if(smf_IsUpdateRow(ds, nCurRow)){
      return "수정";
     }else{
      return "";
     }
    default: return "";
   }
  case "background":
  case "background2":
   switch(nRowType){
    case Dataset.ROWTYPE_INSERT: return "#fad0fdff";
    case Dataset.ROWTYPE_UPDATE:
     if(smf_IsUpdateRow(ds, nCurRow)){
      return "#d6e4faff";
     }else{
      return "";
     }


    default: return "";
   }
 }

 return "";
}

/**
 * (공통만사용)공통에서 사용하기위해 그리드를 세팅작업.
 *
 * @param
 * @return boolean
 * @see
 */
function smf_InitGrids(){
 var ds = _dsGridList;
 for(var i=0,size=ds.rowcount; i<size; i++){
  var __grd = eval(ds.getColumn(i, 'gridName'));
  smf_UpdateGrid(__grd);
 }
}

/**
 * 그리드의 기본세팅
 *
 * @param grid
 * @return boolean
 * @see
 */
function smf_UpdateGrid(grd){
    for(var n=0,size2=grd.getCellCount('body'); n<size2; n++){
        if(isNil(grd.nodatatext)){
            grd.nodatatext = "데이터를 조회하십시오.";
        }

        var sText = grd.getCellProperty('body', n, 'text');

        if(sText == "expr:currow+1"){
            grd.setCellProperty('body', n, 'text', "expr:smf_RenderGridSeqCell(eval(name), 'text', currow, getRowType(currow))");
            grd.setCellProperty('body', n, 'background', "expr:smf_RenderGridSeqCell(eval(name), 'background', currow, getRowType(currow))");
            grd.setCellProperty('body', n, 'background2', "expr:smf_RenderGridSeqCell(eval(name), 'background2', currow, getRowType(currow))");
        }else if(sText == "expr:'state'"){
            grd.setCellProperty('body', n, 'text', "expr:smf_RenderGridStateCell(eval(name), 'text', currow, getRowType(currow))");
            grd.setCellProperty('body', n, 'background', "expr:smf_RenderGridStateCell(eval(name), 'background', currow, getRowType(currow))");
            grd.setCellProperty('body', n, 'background2', "expr:smf_RenderGridStateCell(eval(name), 'background2', currow, getRowType(currow))");
        }
    }
}

/**
 * 서비스ID로 outDataset에 있는 데이터셋을 BindDataset으로 참조하는 모든 그리드를 array로 반환한다.
 *
 * @param  Service ID
 * @return array
 * @see    smf_FindGridsByServiceId('select');
 */
function smf_FindGridsByServiceId(sServiceId, form){
 if(isNil(form)) form = this;

 var sOutDatasets = smf_Str(((new RequestService(form, sServiceId)).outDatasets));
 var arySplitDataset = sOutDatasets.split(' ');


 var idx = 0;
 var grids = [];
 for(var i=0,size=arySplitDataset.length; i<size; i++){
  var ds = objects(arySplitDataset[i].split('=')[0]);
  var aryGrid = smf_GridListByDataset(ds);

  smf_HandleList(aryGrid, function (index, item){
   grids.push(item);
  });
 }

 return grids;
}

/**
 * Dataset으로 Grid찾기
 *
 * @param  Service ID
 * @return Grid
 * @see    smf_GridByDataset(dsMain);
 */
function smf_GridByDataset(ds:Dataset){
 for(var i=0,size=_dsGridList.getRowCount(); i<size; i++){
  var __grd = eval(_dsGridList.getColumn(i, 'gridName'));
  if(isNil(__grd)) continue; //그리드가 동적으로 삭제될수 있다..
  var dsOfGrid = eval(__grd.binddataset);
  if(ds == dsOfGrid){
   return __grd;
  }
 }
 return null;
}

/**
 * Dataset으로 GridList 찾기
 *
 * @param  ds : Dataset
 * @return array
 * @see    smf_GridListByDataset(dsMain);
 */
function smf_GridListByDataset(ds : Dataset){
 var result = [];
 for(var i=0,size=_dsGridList.getRowCount(); i<size; i++){
  var __grd = eval(_dsGridList.getColumn(i, 'gridName'));
  if(isNil(__grd)) continue; //그리드가 동적으로 삭제될수 있다..

  var dsOfGrid = eval(__grd.binddataset);
  if(ds == dsOfGrid){
   result.push(__grd);
  }
 }
 return result;
}

/**
 * Validation을 체크한다.
 *
 * @param  oGridOrComposite : Grid나 (Div or Tabpage)를 넘긴다.
 * @param  dsValid          : Grid를 넘겼을 경우 validate할 Dataset
 * @return boolean
 * @see    smf_Validate(div_search); // Div으로 validate check
 *         smf_Validate(grdMain, dsValidGrdMain); // Grid로 validate check
 */
function smf_Validate(oGridOrComposite, dsValid){
 smf_ApplyData();
    var ary = [];

    if(oGridOrComposite instanceof Array){
        ary = oGridOrComposite;
    }else{
        ary[0] = oGridOrComposite;
    }

//  smf_HandleList(ary, function(i, item){
//   if(item instanceof Dataset){
//    var grd = smf_GridByDataset(item);
//    if(isNotNil(grd)){
//     alert(format("smf_Validate함수 사용에러\n"
//       +"smf_Validate함수의 첫번째 파라메터에는 Dataset을 사용할 수없습니다.\n"
//       +"(혹시 그리드({0})를 사용하는해야 하는지 확인해보세요)\n"
//       +"Grid나 Div, Tab, Tabpage를 사용하세요",
//       [grd.name]));
//    }else{
//     alert("smf_Validate함수 사용에러\n"
//       +"smf_Validate함수의 첫번째 파라메터에는 Dataset을 사용할 수없습니다.\n"
//       +"Grid나 Div, Tab, Tabpage를 사용하세요");
//    }
//    return false;
//   }
//  });

    for(var i=0,size=ary.length; i<size; i++){
        var oValid = ary[i];
        if(oValid instanceof Tab){
   //Tab이면 tabpage만큼 검증한다.
   for(var n=0,sizen=oValid.tabpages.length; n<sizen; n++){
    if(! svcf_getValidateCompList(oValid.tabpages[n])) return false;
   }
        }else if(oValid instanceof Div || oValid instanceof Tabpage){
            if(! svcf_getValidateCompList(oValid)) return false;
        }else{
            //그리드
            if(! smf_CheckValidDataset(dsValid)) return false;
            if(! svcf_dsValidCheck(oValid, dsValid)) return false;
        }
    }

    return true;
}


/**
 * (공통만사용)Validation 체크하는 데이터셋을 체크한다.
 *
 * @param  dsValid valiation 체크하는 데이터셋
 * @return boolean
 * @see
 *
 */
function smf_CheckValidDataset(dsValid){
    if(dsValid.rowcount != 1){
        alert(format("valid dataset 설정에러\n데이터셋 '{0}'는 rowcount가 1건이어야 합니다", [dsValid.name]));
        return false;
    }

    for(var i=0,size=dsValid.colcount-dsValid.getConstCount(); i<size; i++){
        var info = dsValid.getColumnInfo(i);
        if(isNil(info)) continue;
        //if(isNil(dsValid.getColumn(i))) continue;

        if(info.type.toUpperCase() != "STRING"){
            alert(format("valid dataset 설정에러\n데이터셋 '{0}'의 column '{1}'의 타입을 String형으로 변경하세요", [dsValid.name, info.name]));
            return false;
        }
    }

    return true;
}

/**
 * check가 된 항목이 있는지
 *
 * @param  ds : Dataset
 * @return boolean
 * @see    smf_ValidateToChecke(dsMyData);
 */
function smf_ValidateToChecke(ds, sMessage){
 var sCheckColumnName = "_chk";

 var nRow = ds.findRow(sCheckColumnName, '1');
 if(nRow >= 0){
  //ds.rowposition = nRow;
  if(isNil(sMessage)) sMessage = "체크된 데이터가 있습니다.\n저장하려면 체크를 해제하세요.";
  alert(sMessage);
  return true;
 }

 return false;
}

/**
 * Dataset.addRow하기전 Validation을 체크한다.
 *
 * 2010.07.19 수정 : Grid는 할필요없어서 제거
 * @param  oGridOrComposite : Grid나 (Div or Tabpage)를 넘긴다.
 * @param  dsValid          : Grid를 넘겼을 경우 validate할 Dataset
 * @return boolean
 * @see    smf_Validate(div_search); // Div으로 validate check
 * @see    smf_Validate(grdMain, dsValidGrdMain); // Grid로 validate check
 */
function smf_AddRowValidate(ds, oGridOrComposite, dsValid){
 if(ds.rowposition == -1) return true;
 if(ds.rowcount == 0) return true;

 //Row가 수정안되어있으면 Validation 체크를 하지 않는다.
 if(ds.getRowType(ds.rowposition) == Dataset.ROWTYPE_NORMAL ||
    ds.getRowType(ds.rowposition) == Dataset.ROWTYPE_EMPTY ||
    ds.getRowType(ds.rowposition) == Dataset.ROWTYPE_GROUP) return true;
 //_chk를 제외한 Row가 수정안되어있으면 Validation 체크를 하지 않는다.
    if(! smf_IsUpdateRow(ds, ds.rowposition)) return true;

 if(oGridOrComposite instanceof Div || oGridOrComposite instanceof Tabpage){
  return svcf_getValidateCompList(oGridOrComposite);
 }else{
        //그리드에서는 validation체크 하지 않는다.
 }

 return true;
}

/**
 * Validation을 체크후 Dataset.addRow를 추가한다.
 *
 * @param  ds : Dataset
 * @param  oComposite : Div나 Tabpage
 * @param  sValidTargetServiceId :

 * @return boolean
 * @see    smf_ValidAndAddRow(dsMyData, div_manage);
 */
function smf_ValidAndAddRow(ds:Dataset, oComposite, sValidTargetServiceId){
    if(ds.rowcount == 0){
        if(! isNil(sValidTargetServiceId)){
            if(! smf_HasBeenService(sValidTargetServiceId)){
                alert("추가하려면 조회먼저하세요");
                return -1;
            }
        }
    }

 if(! smf_AddRowValidate(ds, oComposite)) return -1;
 var nRow = ds.addRow();
 var aryGrid = smf_GridListByDataset(ds);
 smf_HandleList(aryGrid, function (index, gridOfItem){
        gridOfItem.nodatatext = "데이터가 없습니다";
 });

 return nRow;
}

/**
 * ds의 nRow의 Row를 string column을 ""으로 초기화한다.
 *
 * @param  ds : Dataset
 * @param  nRow : Integer
 * @return rowindex
 * @see    smf_InitRow(dsMain, dsMain.rowposition);
 */
function smf_InitRow(ds, nRow){
 for(var i=0,size=ds.colcount-ds.constcount; i<size; i++){
  var info = ds.getColumnInfo(i);
  if(info.type != "STRING") continue;
  if(ds.getColumn(nRow, i) != undefined) continue;
  ds.setColumn(nRow, i, "");
 }
 return nRow;
}

/**
 * ds의 string column을 ""으로 초기화한다.
 *
 * @param  ds : Dataset
 * @return void
 * @see    smf_InitRowList(dsMain);
 */
function smf_InitRowList(ds){
 for(var i=0,size=ds.rowcount; i<size; i++){
  smf_InitRow(ds, i);
 }
}

/**
 * 조회하기전 체크한다.
 *
 * @param  oGridOrComposite : Grid나 (Div or Tabpage)를 넘긴다.
 * @param  dsValid          : Grid를 넘겼을 경우 validate할 Dataset
 * @return boolean
 * @see    smf_SelectConfirm(div_search); // Div으로 validate check
           smf_SelectConfirm(grdMain, dsValidGrdMain); // Grid로 validate check
 */
function smf_SelectConfirm(oGridOrComposite, dsValid, sMessagePrefix){
    if(smv_HasNodifySubject){
        if(! (new ModifyObserver(this)).confirmSelect(oGridOrComposite, sMessagePrefix)){
            return false;
        }
    }

 return smf_Validate(oGridOrComposite, dsValid);
}

 

/**
 * 조회하기전 변경된 데이터가 있으면 정말 조회할건지 confirm.
 *
 * @param  oaryDataset    : array of Dataset
 * @param  sMessagePrefix : 메시지
 * @return boolean
 * @see    smf_ConfirmModifyData([dsMain]);
 */
function smf_ConfirmModifyData(aryDataset, sMessagePrefix){
 if(isNil(sMessagePrefix)) sMessagePrefix = "조회";

 for(var i=0,size=aryDataset.length; i<size; i++){
  var ds = aryDataset[i];
  if(smf_IsUpdate(ds)){
   return true == confirm(format("변경된 데이터가 있습니다.\n{0}하시겠습니까?\n", [sMessagePrefix]));
  }
 }

 return true;
}

/**
 * 폼을 닫기전 체크한다.
 * aryDataset
 *
 * @param  datasetOrAryDataset Dataset이나 Dataset으로 구성된 Array
 * @param  [sMessage] 변경된 데이터가 있을때 보여줄 메시지
 * @return boolean
 * @see    return smf_CloseFormConfirm(dsMain); // dsMain이 변경되었으면 Confirm메시지
           return smf_CloseFormConfirm([dsMain, dsSub], "변경된 데이터가 있습니다.\n화면을 종료하시겠습니까?"); // dsMain이 변경되었으면 Confirm메시지
 */
function smf_CloseFormConfirm(datasetOrAryDataset, sMessage){
    if(isNil(sMessage)) sMessage = "변경된 데이터가 있습니다.\종료하시겠습니까?";
    var ary = [];

    if(datasetOrAryDataset instanceof Array){
        ary = datasetOrAryDataset;
    }else if(datasetOrAryDataset instanceof Dataset){
        ary[0] = datasetOrAryDataset;
    }

    for(var i=0,size=ary.length; i<size; i++){
        if(smf_IsUpdate(ary[i])) return true == confirm(sMessage);
    }

    return true;
}

/**
 * tab이 변경되기전 데이터가 변경되었으면 confirm
 *
 * @param  datasetOrAryDataset Dataset이나 Dataset으로 구성된 Array
 * @param  [sMessage] 변경된 데이터가 있을때 보여줄 메시지
 * @return boolean
 * @see    return smf_CanChangeTabConfirm(dsMain); // dsMain이 변경되었으면 Confirm메시지
           return smf_CanChangeTabConfirm([dsMain, dsSub], "변경된 데이터가 있습니다.\n화면을 종료하시겠습니까?"); // dsMain이 변경되었으면 Confirm메시지
 */
function smf_ChangingTabConfirm(datasetOrAryDataset, sMessage){
    smf_ApplyData();

    if(isNil(sMessage)) sMessage = "변경된 데이터가 있습니다.\n다른 탭을 선택하시겠습니까?";
    var ary = [];

    if(datasetOrAryDataset instanceof Array){
        ary = datasetOrAryDataset;
    }else if(datasetOrAryDataset instanceof Dataset){
        ary[0] = datasetOrAryDataset;
    }

    for(var i=0,size=ary.length; i<size; i++){
        if(isNil(ary[i])) continue;
        if(smf_IsUpdate(ary[i])) return true == confirm(sMessage);
    }

    return true;
}

/**
 * tab이 변경되기전 데이터가 변경되었으면 tab을 변경못하게
 *
 * @param  datasetOrAryDataset Dataset이나 Dataset으로 구성된 Array
 * @param  [sMessage] 변경된 데이터가 있을때 보여줄 메시지
 * @return boolean
 * @see    return smf_DoNotChangeTabNotice(dsMain); // dsMain이 변경되었으면 Confirm메시지
           return smf_DoNotChangeTabNotice([dsMain, dsSub], "변경된 데이터가 있습니다.\n탭을 변경하려면 저장을 하세요"); // dsMain이 변경되었으면 Confirm메시지
 */
function smf_DoNotChangeTabNotice(datasetOrAryDataset, sMessage){
    smf_ApplyData();
    if(isNil(sMessage)) sMessage = "변경된 데이터가 있습니다.\n다른탭을 선택하려면 저장하세요";
    var ary = [];

    if(datasetOrAryDataset instanceof Array){
        ary = datasetOrAryDataset;
    }else if(datasetOrAryDataset instanceof Dataset){
        ary[0] = datasetOrAryDataset;
    }

    for(var i=0,size=ary.length; i<size; i++){
        if(isNil(ary[i])) continue;
        if(smf_IsUpdate(ary[i])){
            alert(sMessage);
            return false;
        }
    }

    return true;
}

/**
 * (공통만사용)
 *
 */
function smf_ValidateCheckOpion(ds, ChkOption, sMessagePrefix){
 var result = {};
 result.datasetUpdated = false;
 result.exitFunction = false;

 switch(ChkOption){
  case true:
  case "ignore": //_chk를 없는 컬럼 취급한다.
   result.datasetUpdated = smf_IsUpdate(ds, false);
   break;

  case "accept": //_chk를 데이터칼럼으로 취급한다.
   result.datasetUpdated = smf_IsUpdate(ds, true);
   break;

  case false :
  case "except": //_chk를 체크하면 안되는걸로 취급한다.
   if(smf_ValidateToChecke(ds, format("체크된 데이터가 있습니다.\n{0}하려면 체크를 해제하세요.", [sMessagePrefix]))){
    result.exitFunction = true;
   }
   result.datasetUpdated = smf_IsUpdate(ds, true);
   break;

  case "required":
   if(ds.findRow("_chk", "1") == -1){
    alert(format("그리드에 체크된 데이터가 없습니다.\n{0}하려면 데이터를 체크하세요.", [sMessagePrefix]));
    result.exitFunction = true;
   }
   result.datasetUpdated = true;
   break;
 }

 return result;
}

/**
 * 저장하기전 체크한다.
 *
 * @param  sServiceId        : service id
 * @param  oGridOrComposite  : Grid나 (Div or Tabpage)를 넘긴다.
 * @param  dsValid           : Grid를 넘겼을 경우 validate할 Dataset
 * @param  ChkOption         : true일경우 _chk를 체크안한다.(_chk가 체크되도 check하지 마라는 alert 안띄움)
 * @param  sPrefixOrJMessage :

 * @return boolean
 * @see    smf_SaveConfirm('save', div_search); // Div으로 validate check
           smf_SaveConfirm('save', grdMain, dsValidGrdMain); // Grid로 validate check
 */
function smf_SaveConfirm(sServiceId, oGridOrDiv, dsValid, ChkOption, sMessagePrefix){
//  if(oGridOrDiv instanceof Dataset){
//   var grd = smf_GridByDataset(oGridOrDiv);
//   if(isNotNil(grd)){
//    alert(format("smf_Validate함수 사용에러\n"
//      +"smf_Validate함수의 첫번째 파라메터에는 Dataset을 사용할 수없습니다.\n"
//      +"(혹시 그리드({0})를 사용하는해야 하는지 확인해보세요)\n"
//      +"Grid나 Div, Tab, Tabpage를 사용하세요",
//      [oGridOrDiv.name]));
//   }else{
//    alert("smf_Validate함수 사용에러\n"
//      +"smf_Validate함수의 첫번째 파라메터에는 Dataset을 사용할 수없습니다.\n"
//      +"Grid나 Div, Tab, Tabpage를 사용하세요");
//   }
//   return false;
//  }

 if(ChkOption == undefined || ChkOption == null) ChkOption = false;
 if(! smf_HasService(sServiceId)) return false;

 if(isNil(sMessagePrefix)) sMessagePrefix = "저장";

 var oService = new RequestService(this, sServiceId);
 var aryDataset = oService.toArrayByInDatasets();
 var hasUpdate = false;
 for(var i=0,size=aryDataset.length; i<size; i++){
  var ds = aryDataset[i];
  var checkOptionResult = smf_ValidateCheckOpion(ds, ChkOption, sMessagePrefix);
  if(checkOptionResult.exitFunction) return false; //함수를 빠져나간다.
  hasUpdate = hasUpdate || checkOptionResult.datasetUpdated;

  break; //첫번째만 체크한다.
 }
 if(! hasUpdate){
  alert(format("{0}할 데이터가 없습니다.", [sMessagePrefix]));
  return false;
 }

 if(isNotNil(oGridOrDiv)){
  if(!smf_Validate(oGridOrDiv, dsValid)) return false;
 }

 if(smf_IsDuplicateList(sServiceId)) return false;

 return confirm(format("{0}하시겠습니까?", [sMessagePrefix]));
}

/**
 * 저장하기전 체크한다. smf_SaveConfirm과는 다르게 첫번째 파라메터에 데이터셋을 넘긴다.
 *
 * @param  ds         : Grid나 (Div or Tabpage)를 넘긴다.
 * @param  oGridOrDiv : Grid를 넘겼을 경우 validate할 Dataset
 * @param  dsValid    : true일경우 _chk를 체크안한다.(_chk가 체크되도 check하지 마라는 alert 안띄움)
 * @return boolean
 * @see    smf_SaveConfirm('save', div_search); // Div으로 validate check
           smf_SaveConfirm('save', grdMain, dsValidGrdMain); // Grid로 validate check
 */
function smf_SaveConfirm2(ds, oGridOrDiv, dsValid, ChkOption, sMessagePrefix){
 if(ChkOption == undefined || ChkOption == null) ChkOption = false;
 if(isNil(sMessagePrefix)) sMessagePrefix = "저장";

 var checkOptionResult = smf_ValidateCheckOpion(ds, ChkOption, sMessagePrefix);
 if(checkOptionResult.exitFunction) return false; //함수를 빠져나간다.

 if(! checkOptionResult.datasetUpdated){
  alert(format("{0}할 데이터가 없습니다.", [sMessagePrefix]));
  return false;
 }

 if(!smf_Validate(oGridOrDiv, dsValid)) return false;
 if(smf_IsDuplicate(ds)) return false;

 return confirm(format("{0}하시겠습니까?", [sMessagePrefix]));
}
/**
 * true/false인지 비교
 *
 * @param  jParamOrBoolean : jParam이나 boolean값을 받아서 result를 판단.
 * @return boolean
*/
function smf_IsValid(jParamOrBoolean){
 if(jParamOrBoolean == true) return true;
 if(jParamOrBoolean == false) return false;
 if(isNil(jParamOrBoolean)) return false;
 return jParamOrBoolean._result == true ? true : false;
}

/**
 * 저장하기전 체크한다.
 *
 * @param  serviceId : 호출할 service Id
 * @param  validations._chkOption   : _chk를 어떻게 처리할지 옵션
                                      except : 체크시 에러
                                      ignore : _chk를 없는 컬럼처럼 판단
                                      accept : _chk를 DataColumn으로 판단.
                                      required : 체크를 해야만 저장가능.
           validations.dataset      : 중복체크, 저장할데이터가 있는지 확인할 Dataset( service의 InDatasets에 등록한 Dataset)

           validations.grid         : validation할 Grid
           validations.validDataset : validation할 Dataset (grid를 넘겼을경우만 필요함)
           validations.div          : validation할 div

           messagePrefix            : confirm메시지에서 보여줄 메시지접두어 [default "저장"] 예)"저장", "복사", "일괄저장"
           showConfirmMessage       : confirm message보일지 여부 [default true]

 * @return confirmResult
 * @see     //validation체크
   var confirmResult = smf_SaveConfirmList("save", {
    validations   : [{ "_chkOption" : "except"  , "dataset" : dsMain,      "grid" : Grid00, "validDataset" : dsMainValid}
        ,{ "_chkOption" : "required", "dataset" : dsMain02,    "grid" : Grid03, "validDataset" : dsMainValid02}
        ,{ "_chkOption" : "ignore"  , "dataset" : dsMainPopup, "grid" : Grid01, "validDataset" : dsMainPopupValid}
        ,{ "_chkOption" : "accept"  , "dataset" : dsSub,       "grid" : Grid02, "validDataset" : dsSubValid}
        ,{ "_chkOption" : "except"  , "dataset" : dsManage,    "div"  : Div00 }
        ,{ "div"  : Div01 } //dataset을 입력하지 않으면 무조건 validation을 체크한다.
        ],
    //기타옵션
    messagePrefix : "저장",    //"저장"하시겠습니까? << "저장" prefix [default "저장"]
    showConfirmMessage : true //confirm message보일지 여부 [default true]
   });
   if(! smf_IsValid(confirmResult)) return;
*/
function smf_SaveConfirmList(sServiceId, jParams){
 var result = { _result : false };
 if(! smf_HasService(sServiceId)) return result;
 var aryValidation = ! isNil(jParams.validations) ? jParams.validations : [];
 var messagePrefix = ! isNil(jParams.messagePrefix) ? jParams.messagePrefix : "저장";
 var showConfirmMessage = jParams.showConfirmMessage != undefined ? jParams.showConfirmMessage : true;


 ////////////////////////////검증//////////////////////////
 var bIllegalArgument = false;
 var sIllegalArgumentMessage = "";

 if(! (aryValidation instanceof Array)){
  sIllegalArgumentMessage += "validations 항목은 Array타입이어야 합니다.\n";
  bIllegalArgument = true;
 }

 if( notIn(showConfirmMessage, [true, false])){
  sIllegalArgumentMessage += "showConfirmMessage 항목은 boolean 타입이어야 합니다.(true/false).\n";
  bIllegalArgument = true;
 }

 //validations 확인
 for(var i=0,size=aryValidation.length; i<size; i++){
  var j = aryValidation[i];

  for(sParamName in j){
   if(notIn(sParamName, ["_chkOption", "dataset", "div", "grid", "validDataset"])){
    sIllegalArgumentMessage += format("validations항목중 [{0}/{1}]번째 잘못된 파라메터입니다('{2}').\n", [i+1, size, sParamName]);
    bIllegalArgument = true;
   }
  }

  var div = j.div;
  var grid = j.grid;

  if(isNotNil(j.dataset) &&  !(j.dataset instanceof Dataset)){
   sIllegalArgumentMessage += format("validations항목중 [{0}/{1}]번째 dataset항목은 Dataset이어야 합니다.\n", [i+1, size]);
   bIllegalArgument = true;
  }

  if(isNotNil(j.dataset) && notIn(j._chkOption, ["ignore", "accept", "except", "required"])){
   sIllegalArgumentMessage += format("validations항목중 [{0}/{1}]번째 _chkOption항목은 [ignore,accept,except]중 하나여야 합니다.\n", [i+1, size]);
   bIllegalArgument = true;
  }

  if(isNil(div) && isNil(grid)){
   sIllegalArgumentMessage += format("validations항목중 [{0}/{1}]번째 div 나 grid 항목중 반드시 하나를 입력하세요. \n", [i+1, size]);
   bIllegalArgument = true;
  }else if(! isNil(div)){
   if((!(div instanceof Div)) && (!(div instanceof Tabpage)) ){
    sIllegalArgumentMessage += format("validations항목중 [{0}/{1}]번째 div항목은 div나 Tabpage 이어야합니다. \n", [i+1, size]);
    bIllegalArgument = true;
   }
  }else if(! isNil(grid)){
   if(!(grid instanceof Grid)){
    sIllegalArgumentMessage += format("validations항목중 [{0}/{1}]번째 grid항목은 grid이어야합니다. \n", [i+1, size]);
    bIllegalArgument = true;
   }

   if(isNil(j.validDataset)){
    sIllegalArgumentMessage += format("validations항목중 [{0}/{1}]번째 validDataset 항목은 Dataset이어야 합니다.\n", [i+1, size]);
    bIllegalArgument = true;
   }
  }
 }

 if(bIllegalArgument){
  sIllegalArgumentMessage = "개발에러\nsmf_SaveConfirmList함수 사용중, 잘못된 파라메터가 존재합니다.\n\n" + sIllegalArgumentMessage;
  trace(sIllegalArgumentMessage);
  alert(sIllegalArgumentMessage);
  return result;
 }
 //////////////////////////검증완료////////////////////////

 

 

 var hasUpdate = false;
 for(var i=0,size=aryValidation.length; i<size; i++){
  var j = aryValidation[i];
  var ds = j.dataset;
  var bDatasetUpdated = false;
  var checkOptionResult;
  if(! isNil(ds)){
   checkOptionResult = smf_ValidateCheckOpion(ds, j._chkOption, messagePrefix);
   if(checkOptionResult.exitFunction) return result; //함수를 빠져나간다.
   bDatasetUpdated = checkOptionResult.datasetUpdated;
  }

  //변경이 있을때 or Dataset == null validation체크한다.
  var oGridOrDiv;
  if(! isNil(j.div)){
   oGridOrDiv = j.div;
  }else{
   oGridOrDiv = j.grid;
  }

  if((oGridOrDiv instanceof Grid)){
   if(!smf_Validate(oGridOrDiv, j.validDataset)) return result;
  }else{
   if(!smf_Validate(oGridOrDiv)) return result;
  }

  if(! isNil(ds)){
   if(smf_IsDuplicate(ds)) return result;
  }

  hasUpdate = hasUpdate || bDatasetUpdated;
 }


 if(hasUpdate != true){
  alert(format("{0}할 데이터가 없습니다.", [messagePrefix]));
  return result;
 }

 if(showConfirmMessage == true){
  if(! confirm(format("{0}하시겠습니까?", [messagePrefix]))){
   return result;
  }
 }

 result._result = true;
 return result;
}


/**
 * 서비스에 등록된 데이터셋들의 중복을 검증한다.
 * 기능을 사용하려면 Dataset에 ConstColumn에 keys컬럼을 만들고
 * 키가되는 컬럼아이디를 column1,column2 정의한다.
 *
 * @param  sServiceId : 서비스아이디
 * @return boolean (중복일경우 true)
 * @see    smf_IsDuplicateList('save');
 */
function smf_IsDuplicateList(sServiceId){
 var aryDataset = (new RequestService(this, sServiceId)).toArrayByInDatasets();

 for(var i=0,size=aryDataset.length; i<size; i++){
  if(smf_IsDuplicate(aryDataset[i])) return true;
 }

 return false;
}

/**
 * 데이터셋의 중복을 검증한다.
 * 기능을 사용하려면 Dataset에 ConstColumn에 keys컬럼을 만들고
 * 키가되는 컬럼아이디를 column1,column2 정의한다.
 * ConstColumn keys 정의 : columnText:"상황일자, 주야과정구분"staffSittnYmd,dghtCrseScCode
 *
 * @param  ds : Dataset
 * @return boolean (중복일경우 true)
 * @see    smf_IsDuplicateList('save');
 */
 function smf_IsDuplicate(ds: Dataset){
    var sKeys = smf_Str(ds.getConstColumn('keys'));
    if(isNil(sKeys)) return false;

    var sColumnMessage = "";
    if(sKeys.indexOf('columnText:"') == 0){
        sColumnMessage = sKeys.substring(sKeys.indexOf('"'), sKeys.lastIndexOf('"')+1);
        sColumnMessage = format("\n(항목 : {0})", [sColumnMessage]);
        sKeys = sKeys.substr(sKeys.lastIndexOf('"')+1);
    }

 

    var aryKey = sKeys.split(',');

    for(var i=ds.rowcount-1; i>=0; i--){
        if(! smf_IsUpdateRow(ds, i)) continue;
        var sSearchText = "currow != " + i + " ";
        for(var n=0,size2=aryKey.length; n<size2; n++){
            sSearchText += "&& smf_Str(" + aryKey[n] + ") == '" + smf_Str(ds.getColumn(i, aryKey[n])) + "'";
        }

        var nRow = ds.findRowExpr(sSearchText);
        if(nRow > -1){
            ds.rowposition = smf_SmartPosRow(ds, nRow, i);
            var sMessage = format("{1}행과 {2}행이 중복됩니다.{0}\n확인후 다시 시도하세요.",
                                  [sColumnMessage, nRow+1, i+1]);
            alert(sMessage);
            return true;
        }
    }

    return false;
}


/**
 * 두개의 데이터셋의 중복을 검증한다.
 *
 * @param  dsBase : Dataset
 * @param  dsSub  : Dataset
 * @param  aryColumn  : 컬럼리스트
 * @param  sMessage  : 중복시 사용자에게 보여줄 column메시지

 * @return boolean (중복일경우 true)
 * @see    smf_IsDuplicateTo(dsMain, dsMainPopup, ["ay","dghtCrseScCode"], "년도, 주야과정구분");
 */
function smf_IsDuplicateTo(dsBase, dsSub, aryColumn, sMessage){
    var result = {
        isDup : false
       ,message : ""
       ,upRow : -1
       ,downRow : -1
       ,dupRow : -1
    };


    var sColumnMessage = "";
    if(! isNil(sMessage)){
        sColumnMessage = "중복된 항목 : '" + sMessage + "'\n";
    }

 for(var i=dsSub.rowcount-1; i>=0; i--){
        if(dsSub.getRowType(i) == Dataset.ROWTYPE_EMPTY ||
           dsSub.getRowType(i) == Dataset.ROWTYPE_NORMAL ||
           dsSub.getRowType(i) == Dataset.ROWTYPE_GROUP ) continue;

  var sSearchText = "1 == 1 ";
  for(var n=0,size2=aryColumn.length; n<size2; n++){
   sSearchText += "&& smf_Str(" + aryColumn[n] + ") == '" + smf_Str(dsSub.getColumn(i, aryColumn[n])) + "'";
  }

  var nRow = dsBase.findRowExpr(sSearchText);
  if(nRow > -1){
            result.isDup = true;
   result.dupRow = nRow;
   result.message = format("{1}행에 이미 데이터가 있습니다.\n{0}확인후 다시 시도하세요.",
                                  [sColumnMessage, nRow+1, i+1]);
   break;
  }
 }

 return result;
}

/**
 * 데이터셋의 중복을 검증한다.
 *
 * @param  ds : Dataset
 * @param  aryColumn  : 컬럼리스트
 * @return JSON : isDup -> true면 중복 , upRow -> 중복된 상위 로우 downRow -> 중복된 하위 로우 gotoRow -> rowposition을 수정할 로우
 * @see    var jDupResult = smf_IsDuplicateColumns(dsMain, ["ay","dghtCrseScCode"]);
           if(jDupResult.isDup){
               dsMain.rowposition = jDupResult.row2;
               alert("중복발생");
           }
 */
function smf_IsDuplicateColumns(ds, aryColumn, fncAcceptRow){
    var result = {
        isDup : false
       ,upRow : -1
       ,downRow : -1
       ,dupRow : -1
    };

 for(var i=ds.rowcount-1; i>0; i--){
  if(fncAcceptRow != undefined){
   if(false == fncAcceptRow(i)) continue;
  }

  var sSearchText = "1 == 1 ";
  for(var n=0,size2=aryColumn.length; n<size2; n++){
   sSearchText += "&& smf_Str(" + aryColumn[n] + ") == '" + smf_Str(ds.getColumn(i, aryColumn[n])) + "'";
  }

  var nRow = ds.findRowExpr(sSearchText, 0, i);
  if(nRow > -1){
            result.isDup = true;
            result.upRow = nRow;
            result.downRow = i;
            result.dupRow = smf_SmartPosRow(ds, nRow, i);

            break;
  }
 }

 return result;
}

/**
 * (공통만사용)
 *
 * @param  ds : Dataset
 * @param  nUpRow : 위 row
 * @param  nDownRow : 아래 row
 * @return integer 가장 적합한 row를 리턴
 */
function smf_SmartPosRow(ds, nUpRow, nDownRow){
    var isModifyUpRow = ds.getRowType(nUpRow);
    var isModifyDownRow = ds.getRowType(nDownRow);

    if(isModifyDownRow == Dataset.ROWTYPE_INSERT) return nDownRow;
    if(isModifyUpRow == Dataset.ROWTYPE_INSERT)return nUpRow;

    if(isModifyDownRow == Dataset.ROWTYPE_UPDATE) return nDownRow;
    if(isModifyUpRow == Dataset.ROWTYPE_UPDATE) return nUpRow;

    return nDownRow;
}

/**
 * ds의 nRow로우가 수정됬는지 확인한다.
 * @param  ds Dataset
 * @return boolean (중복일경우 true)
 * @see    smf_IsUpdateRow('save');
 */
function smf_IsUpdateRow(ds:Dataset, nRow, sCheckColumnName){
 if(isNil(sCheckColumnName)) sCheckColumnName = '_chk';

 switch(ds.getRowType(nRow)){
  case Dataset.ROWTYPE_INSERT:
  case Dataset.ROWTYPE_DELETE: return true;

  case Dataset.ROWTYPE_UPDATE:
   for(var i=0,size=ds.getColCount(); i<size; i++){
    var sColumnName = ds.getColID(i);
    if(sColumnName == sCheckColumnName) continue;

    if(smf_Str(ds.getColumn(nRow, sColumnName)) != smf_Str(ds.getOrgColumn(nRow, sColumnName))){
     return true;
    }
   }
   return false;

  default: return false;
 }
}

/**
 * Insert된 Row를 delete RowType으로 변경한다.
 * @param  ds Dataset
 * @return boolean (중복일경우 true)
 * @see
 * @deprecated applyChange가 예상대로 동작하지 않아서 사용불가
 */
function smf_InsertToDelete(ds:Dataset){
 //사용하지 마세요..ㅠㅠ
 var aryInsertRow = [];
 var idx = 0;
 for(var i=0,size=ds.rowcount; i<size; i++){
  if(Dataset.ROWTYPE_INSERT == ds.getRowType(i)){
   aryInsertRow[idx++] = i;
  }
 }

 ds.applyChange();
 for(var i=aryInsertRow.length-1; i>=0; i--){
  ds.deleteRow(i);
 }
}

/**
 * 삭제 컨폼2
 * @param  ds : Dataset
 * @return bboolean
 * @see    if(! smf_DeleteConfirm2(dsMain)) return;
 */
function smf_DeleteConfirm2(ds, fncValidate, bDeleteRows, sMessage){
 if(isNil(sMessage)) sMessage = "삭제하시겠습니까?";

 if(ds.getColumnInfo(0) == null || ds.getColumnInfo(0).name != "_chk"){
  alert(format("Dataset 표준설정에러({0})\n삭제하려는 Dataset 0번째 컬럼은 반드시 _chk이어야 합니다.", [ds.name]));
  return false;
 }

 if(ds.rowcount == 0){
  alert("삭제할 데이터가 없습니다.");
  return false;
 }

 if(fncValidate != undefined && fncValidate != null){
  if( false == fncValidate(ds) ){
   return false;
  }
 }

 if(confirm(sMessage)){
  if(true == bDeleteRows){
   smf_DeleteCheckedRows(ds);
  }
 }else{
  return false;
 }

 return true;
}

/**
 * 삭제 컨폼
 * @param  sServiceID
 * @param  fncValidate : 사용자 validate함수
 * @param  bDeleteRows : false로 넘기면 row가 delete되지 않는다.
 * @return boolean
 * @see    if(! smf_DeleteConfirm("delete")) return;
 *         if(! smf_DeleteConfirm("delete", null, false)) return;
 *         if(! smf_DeleteConfirm("delete", lfn_DeleteValid)) return;
 */
function smf_DeleteConfirm(sServiceId, fncValidate, bDeleteRows, sMessage){
 if(! smf_HasService(sServiceId)) return false;
 if(isNil(sMessage)) sMessage = "삭제하시겠습니까?";

 if(bDeleteRows == undefined || bDeleteRows == null) bDeleteRows = true;
 var sCheckColumnName = '_chk';
 var aryDataset = [];
 aryDataset = (new RequestService(this, sServiceId)).toArrayByInDatasets();

 var aryInsertCount = []; //추가된 행의 갯수
 var aryUpdateCount = []; //수정된 행의 갯수

 var aryCheckedCount = []; //check된 갯수
 var aryNormalAndCheckCount = []; //normal행이 체크된갯수
 var aryInsertAndCheckCount = []; //추가행이 체크된갯수
 var aryUpdateAndCheckCount = []; //수정행이 체크된갯수

 for(var n=0,count=aryDataset.length; n<count; n++){
  if(n != 0) continue;//데이터셋 처음것만
  var ds = aryDataset[n];

  if(ds.getColumnInfo(0) == null || ds.getColumnInfo(0).name != "_chk"){
   alert(format("Dataset 표준설정에러({0})\n삭제하려는 Dataset 0번째 컬럼은 반드시 _chk이어야 합니다.", [ds.name]));
   return false;
  }


  aryInsertCount[n] = 0;
  aryUpdateCount[n] = 0;
  aryCheckedCount[n] = 0;
  aryNormalAndCheckCount[n] = 0;
  aryInsertAndCheckCount[n] = 0;
  aryUpdateAndCheckCount[n] = 0;

  //데이터셋에
  for(var i=ds.getRowCount()-1; i>=0; i--){
   var bChecked = false;
   if("1" == ds.getColumn(i, sCheckColumnName)){
    bChecked = true;
    aryCheckedCount[n]++;
   }

   switch(ds.getRowType(i)){
    case Dataset.ROWTYPE_INSERT:
     aryInsertCount[n]++;
     if(bChecked) aryInsertAndCheckCount[n]++;
     break;
    case Dataset.ROWTYPE_UPDATE:
     if(smf_IsUpdateRow(ds, i)){
      if(! bChecked) aryUpdateCount[n]++;
      else aryUpdateAndCheckCount[n]++;
     }else{
      if(bChecked) aryNormalAndCheckCount[n]++;
     }
     break;
    }
  }

//   alert(format("aryInsertCount[{0}] = {1}\n"
//               +"aryUpdateCount[{0}] = {2}\n"
//               +"aryCheckedCount[{0}] = {3}\n"
//               +"aryNormalAndCheckCount[{0}] = {4}\n"
//               +"aryInsertAndCheckCount[{0}] = {5}\n"
//               +"aryUpdateAndCheckCount[{0}] = {6}\n"
//               +"rowcount = {7}"
//   , [n, aryInsertCount[n],
//      aryUpdateCount[n],
//      aryCheckedCount[n],
//      aryNormalAndCheckCount[n],
//      aryInsertAndCheckCount[n],
//      aryUpdateAndCheckCount[n],
//      ds.rowcount
//      ]));

  // 삭제될 내역이 없는 경우는 false
  if(aryCheckedCount[n] == 0){
   alert("삭제할 데이터를 체크하세요.");
   return false;
  }
 }

 var bShowDeleteMessage = true;
 var bShowConfirmModify = false;
 var bShowConfirmDelete = false;

 for(var n=0,count=aryDataset.length; n<count; n++){
  //insert한행만 선택해서 삭제하는경우
  if(aryCheckedCount[n] != aryInsertAndCheckCount[n]){
   bShowDeleteMessage = false;
  }

  //insert 또는 update한 행을 check한경우
  if(aryInsertAndCheckCount[n] > 0 || aryUpdateAndCheckCount[n] > 0){
   bShowConfirmModify = true;
  }

  //
  if(aryNormalAndCheckCount[n] > 0 &&( aryInsertCount > 0 || aryUpdateCount > 0)){
   bShowConfirmModify = true;
  }
 }

 if(bShowDeleteMessage){
  for(var n=0,count=aryDataset.length; n<count; n++){
   var ds = aryDataset[n];
   for(var i=ds.getRowCount()-1; i>=0; i--){
    //체크한행과 등록된행을 삭제한다.
    if("1" == ds.getColumn(i, sCheckColumnName)){
     ds.deleteRow(i);
    }
   }
  }

  alert("삭제되었습니다.");
  return false;
 }

 if(fncValidate != undefined && fncValidate != null){
  if( false == fncValidate(aryDataset[0]) ){
   return false;
  }
 }

 if(bShowConfirmModify){
  if(confirm(sMessage + "\r\n등록 또는 수정된 항목은 반영되지 않습니다.")){
   for(var n=0,count=aryDataset.length; n<count; n++){
    if(bDeleteRows)smf_DeleteCheckedRows(aryDataset[n]);

    //등록한 행도 모두 지운다.
    for(i=aryDataset[n].rowcount-1; i>=0; i--){
     if(ds.getRowType(i) == Dataset.ROWTYPE_INSERT)
      ds.deleteRow(i);
    }
   }
   return true;
  }else{
   return false;
  }
 }else{
  if(confirm(sMessage)){
   for(var n=0,count=aryDataset.length; n<count; n++){
    if(bDeleteRows)smf_DeleteCheckedRows(aryDataset[n]);
   }
   return true;
  }else{
   return false;
  }
 }
}

/**
 * check된 row들을 삭제한다.
 * @param  Dataset
 * @return void
 * @see    smf_DeleteCheckedRows(dsHisScrPe0M2SVO_hisscrpe0M2DVOList);
 */
function smf_DeleteCheckedRows(ds, sCheckColumnName) {
 sCheckColumnName = "_chk";
 //insert된 row 처리하지 않기 위한 로직
 //alert(ds.GetRowCount()-1);
//  ds.enableevent = false;
//  try{
  for(var i=ds.getRowCount()-1; i>=0; i--){
   //RowType이 insert인 row 제거(OrgBuff로 이동 안됨)
   switch(ds.getRowType(i)){
    case Dataset.ROWTYPE_INSERT:
     ds.deleteRow(i);
     break;
   }
  }

  //Normal type으로 변경
  //등록 또는 수정된 로우가 Normal로 변경된다.
  ds.applyChange();

  //체크된 Row 삭제(OrgBuff로 이동)
  smf_RemoveCheckedRows(ds, sCheckColumnName);
//  }finally{
//   ds.enableevent = true;
//  }
}

/**
 * 그리드 내용 삭제(체크된 row)
 * @param  Dataset
 * @param  check column id
 * @return void
 * @see    smf_RemoveCheckedRows(ds, '_chk');
 */
function smf_RemoveCheckedRows(ds, sCheckColumnName) {
 for(var i=ds.rowcount-1; i>-1; i--) {
  if("1" == ds.getColumn(i, sCheckColumnName)) {
   ds.deleteRow(i);
  }
 }
}

/**
 * 데이터셋의 row가 변경될때 confirm
 * @param  aryDetailDataset array of Dataset
 * @return boolean
 * @see
 */
function smf_ValidateMasterDetail(aryDetailDataset, sMessage){
 if(isNil(sMessage)) sMessage = "수정된 항목이 있습니다.\r\n다른 행을 선택하시겠습니까?";

 for(var i=0,size=aryDetailDataset.length; i<size; i++){
  var ds = aryDetailDataset[i];
  if(smf_IsUpdate(ds)){
   return true == confirm(sMessage);
  }
 }

 return true;
}

/**
 * 데이터셋의 row가 변경될때 confirm
 * @param  ds : Dataset
 * @param  e : Event
 * @param  div : validation체크할 div
 * @return boolean
 * @see
 */
function smf_ValidateRowChange(ds, e, div){
 if(e.oldrow >= ds.rowcount) return true;
 if(e.oldrow == -1) return true;
 switch(ds.getRowType(e.oldrow)){
  case Dataset.ROWTYPE_EMPTY: return true;
  case Dataset.ROWTYPE_NORMAL: return true;
  case Dataset.ROWTYPE_GROUP: return true;
 }
  if(! smf_IsUpdateRow(ds, e.oldrow)) return true;
  return svcf_getValidateCompList(div);
}

/**
 * (공통만사용)데이터셋의 row가 변경될때 confirm
 * @return boolean
 * @see
 */
function smf_ValidateDatasetCanRCanRowPosChanged(ds:Dataset, e:DSRowPosChangeEventInfo){
 if(! smv_HasNodifySubject) return true;


 for(var i=0,size=dsNotifySubject.rowcount; i<size; i++){
  if(dsNotifySubject.getColumn(i, 'used') != "true") continue;
  var sType = dsNotifySubject.getColumn(i, 'type');
  if(ds.name != dsNotifySubject.getColumn(i, 'param01')) continue;

  switch(sType){
   case "rowChange":
    if(ds.getRowType(e.oldrow) == Dataset.ROWTYPE_EMPTY) return true;
    if(ds.getRowType(e.oldrow) == Dataset.ROWTYPE_NORMAL) return true;
    if(ds.getRowType(e.oldrow) == Dataset.ROWTYPE_GROUP) return true;
    if(! smf_IsUpdate(ds, e.oldrow)) return true;
    if(e.oldrow == -1)  return true;
    if(e.oldrow >= ds.rowcount) return true;

    var divOrGrid = eval(dsNotifySubject.getColumn(i, 'param02'));
    if(divOrGrid instanceof Div || divOrGrid instanceof Tabpage){
     return svcf_getValidateCompList(divOrGrid);
    }
    break;

   case "masterDetail":
    var dsMaster = objects[dsNotifySubject.getColumn(i, 'param01')];
    var sSplitedDetailDataset = dsNotifySubject.getColumn(i, 'param02');
    var sFuncName = dsNotifySubject.getColumn(i, 'param03');
    var sMessageId = smf_Str(dsNotifySubject.getColumn(i, 'message'));
    var aryDataset = sSplitedDetailDataset.split(",");

    for(var n=0,size2=aryDataset.length; n<size2; n++){
     var dsDetail = objects(aryDataset[n]);
     if(smf_IsUpdate(dsDetail)){
      if(isNotNil(sMessageId)){
       var confirmRowChange = utlf_confirm(sMessageId);
      }else{
       var confirmRowChange = confirm("수정된 항목이 있습니다.\r\n"
             +"다른 행을 선택하시겠습니까?");
      }

      if(confirmRowChange){
       if(isNotNil(sFuncName)){
        eval(sFuncName);
       }
      }
      return confirmRowChange;
     }
    }
    break;
  }
 }

 return true;
}

/**
 * (공통만사용)text를 formatting한다.
 *
 * @param  sFormat   : 포매팅할 문자열
 * @param  aryParams : 파라메터
 * @return String
 * @see    alert(Format("{0} x {1} = {2}", [10, 2, 10 * 2])); // 10 x 2 = 20
 */
function format(sFormat, aryParams){
 for(var i=0, size=aryParams.length; i<size; i++){
  sFormat = sFormat.replace("\{" + i + "\}" , aryParams[i] );
 }

 return sFormat;
}


function nvl(value, defaultValue){
 if(isNil(value)) return defaultValue;
 return value;
}


/**
 * (공통만사용)value가 널인지
 */
function isNil(value){
 return value == undefined || value == null || value == "";
}

/**
 * (공통만사용)value가 널이 아닌지
 */
function isNotNil(value){
 return ! isNil(value);
}

/**
 * (공통만사용)greatest
 *
 */
function greatest(aryParams){
 if(aryParams.length == 0) return;

 var result = aryParams[0];
 for(var i=1, size=aryParams.length; i<size; i++){
  if(result < aryParams[i]) result = aryParams[i];
 }
 return result;
}

/**
 * (공통만사용)least
 *
 */
function least(aryParams){
 if(aryParams.length == 0) return;

 var result = aryParams[0];
 for(var i=1, size=aryParams.length; i<size; i++){
  if(result > aryParams[i]) result = aryParams[i];
 }
 return result;
}

/**
 * (공통만사용)sValue가 aryInValues에 포함되는지
 *
 * @param  sValue   : 값
 * @param  aryInValues : 파라메터
 * @return boolean
 * @see    alert(In('1', ['1', '2', '3', '4'])); // 1 (true)
 */
function In(sValue, aryInValues){
 for(var i=0, size=aryInValues.length; i<size; i++){
  if(sValue == aryInValues[i]) return true;
 }

 return false;
}

/**
 * (공통만사용)sValue가 aryInValues에 포함 안하는지
 *
 * @param  sValue   : 값
 * @param  aryInValues : 파라메터
 * @return boolean
 * @see    alert(NotIn('1', ['1', '2', '3', '4'])); // 1 (false)
 */
function notIn(sValue, aryInValues){
 return ! In(sValue, aryInValues);
}

/**
 * (공통만사용)sValue가 sFrom, sTo에 속하는지
 *
 * @param  sValue - 값
 * @param  sFrom  - from value
 * @param  sTo    - to value
 * @return boolean
 * @see    alert(NotIn('1', ['1', '2', '3', '4'])); // 1 (false)
 */
function between(sValue, sFrom, sTo, bFixiedLength){
 if(bFixiedLength){
 }
 return sValue >= sFrom && sFrom <= sTo;
}

/**
 * (공통만사용)
 */
function smf_ToDatasetObject(datasetNameOrObject){
 if(datasetNameOrObject instanceof Dataset) return datasetNameOrObject;
 if(datasetNameOrObject instanceof String) return eval(datasetNameOrObject);
 if(typeof datasetNameOrObject == 'string') return eval(datasetNameOrObject);

 return null;
}

 

/**
 * (공통만사용)
 */
function smf_InitBindingControls(nRow){
 var sId = dsService.getColumn(nRow, "id");
 var sBinding = smf_Str(dsService.getColumn(nRow, "bindingControls"));
    var jHasSetDataset = {}; //firstcode를 세딩한 데이터셋을 기억해놔서 두번 실행안되도록..

    //constcolumn을 이용하여 firstCode를 세팅한다.
 var aryDataset = (new RequestService(this, sId)).toArrayByOutDatasets();
 for(var i=0,size=aryDataset.length; i<size; i++){
  var bSucc = smf_SetFirstCode(aryDataset[i]);
  if(bSucc)  jHasSetDataset[aryDataset[i].name] = aryDataset[i]; //firstcode를 세팅한 데이터셋을 기억해놓는다.
 }

 var sId = dsService.getColumn(nRow, "id");
 var sBindingControls = dsService.getColumn(nRow, "bindingControls");
 var aryBind = smf_Split(sBinding, " "); // dsCode=ALL divSearch.cboHakgyo=SEL
 for(var i=0,size=aryBind.length; i<size; i++){
  var sControlOrDatasetName = smf_Split(aryBind[i], '=')[0];
  var sOption = smf_Split(aryBind[i], '=')[1].trim(); // SEL,ALL,EMP or :SEL,%:ALL
  var sCodeValue = ""; //코드값
  var sDataValue = ""; //

  if(-1 == sOption.indexOf(":")){ //0:SEL, SEL
   sCodeValue = "";
   sDataValue = smf_Str(sOption);
  }else{
   sCodeValue = smf_Str(sOption.split(":")[0]);
   sDataValue = smf_Str(sOption.split(":")[1]);
  }

  var obj = this.all[sControlOrDatasetName];
  if(obj == null){
   obj = eval(sControlOrDatasetName);
  }
  if(obj == null){
   if(! smv_IgnoreInvalidConfig){
    var sMessage = format('service에러\n"{0}"서비스의 bindingControls("{1}")항목중\n{2}번째 항목"{3}"은 존재하지 않는 ID입니다.',
           [sId, sBinding, i+1, sControlOrDatasetName ]);
    alert(sMessage);
    return;
   }
  }

  //////codecolumn, datacolumn, dataset을 찾는다.
  var sCodeColumn = "";
  var sDataColumn = "";
  var ds = null;
  var controls = Array();
  if(obj instanceof Dataset){
   ds = obj;
   if(obj.getColID(0) == "_chk"){
    sCodeColumn = obj.getColID(1);
    sDataColumn = obj.getColID(2);
   }else{
    sCodeColumn = obj.getColID(0);
    sDataColumn = obj.getColID(1);
   }

   //Dataset을 innerdataset으로 사용하는 컴포넌트를 찾는다.
   smf_TravelComponents(this, function (comp, fncCallback, nLvl){
    if(comp instanceof Combo || comp instanceof Radio || comp instanceof ListBox){
                    var dsInnerDataset = smf_ToDatasetObject(comp.innerdataset);
                    if(dsInnerDataset == null) return;
                    if(ds != dsInnerDataset) return;

                    controls.push(comp);
    }
   });
  }else{
   ds = obj.innerdataset;
   if(ds instanceof String){
    ds = eval(ds);
   }

   if(ds == null){
    alert(format("{0}.innerdataset({1})의 Dataset ID가 존재하지 않습니다.", [obj.name, obj.innerdataset]));
    return;
   }
   controls[0] = obj;
   sCodeColumn = smf_Str(obj.codecolumn);
   sDataColumn = smf_Str(obj.datacolumn);
  }
  //////

  //smf_SetFirstCode 안한 데이터셋만 처리하도록
   if(isNotNil(jHasSetDataset[ds.name])) continue;

  ///////////필터수행//////////////
  if(! isNil(ds.getConstColumn("filterExpr"))){
   ds.enableevent = false;
   try{
    var sFilterExpr = ds.getConstColumn("filterExpr");
    ds.filter(sFilterExpr);
   }finally{
    ds.enableevent = true;
   }
  }
  //////////////////////////////////

  var nAddRow = ds.insertRow(0);
  switch(sDataValue){
   case "ALL":
    ds.setColumn(nAddRow, sCodeColumn, sCodeValue);
    ds.setColumn(nAddRow, sDataColumn, "전체");
    break;
   case "SEL":
    ds.setColumn(nAddRow, sCodeColumn, new String(sCodeValue));
    ds.setColumn(nAddRow, sDataColumn, "선택");
    break;
   case "EMP":
    ds.setColumn(nAddRow, sCodeColumn, sCodeValue);
    ds.setColumn(nAddRow, sDataColumn, "");
    break;
   default:
    var sMessage = format('service에러\n"{0}"서비스의 bindingControls("{1}")항목중\n{2}번째 항목"{3}"은\n(ALL,SEL,EMP)←(전체,선택,비어있게) 중 하나를 선택하십시오.',
           [sId, sBinding, i+1, sDataValue ]);
    alert(sMessage);
    return;

    break;
  }

  //Dataset을 innerdataset으로 사용하는 컴포넌트들의 index 세팅
//  ds.enableevent = false;

  try{
   for(var n=0,size2=controls.length; n<size2; n++){
    if(n != 0) break;
    var control = controls[n];
    //control.enableevent = false;

    var oBindItem = smf_BindItemByComponent(control);
    if(oBindItem == null){
                    control.value = sCodeValue;
    }else{
                    var controlBindDs = eval(oBindItem.datasetid);
                    if(controlBindDs instanceof Dataset){
                        if(controlBindDs.rowcount == 0 || controlBindDs.rowposition == -1){
                            control.index = 0;
                        }else{
                            var binddatasetColumnValue = controlBindDs.getColumn(controlBindDs.rowposition, oBindItem.columnid);
                            var nFindRow = ds.findRow(sCodeColumn, binddatasetColumnValue);
                            if( nFindRow >= 0){
                                binddatasetColumnValue = controlBindDs.getColumn(controlBindDs.rowposition, oBindItem.columnid);
                                control.enableredraw = false;
                                control.value = undefined;
                                control.value = binddatasetColumnValue;
                                control.enableredraw = true;
                            }else{
                                control.value = sCodeValue;
                            }
                        }
                    }else{
                    }
                }
    //control.enableevent = true;
   }

   ds.applyChange();
  }finally{
//   ds.enableevent = true;
  }
 }
}

/**
 * (공통만사용)dsService검증
 *
 * @param
 * @return boolean
 * @see
 */
function smf_ValidatesService(){
 try{
  var dummy = objects['dsService'];
  if(dummy == null) return false;
 }catch(e){
  return false;
 }

 var rtn = true;
 for(var i=0,size=dsService.rowcount; i<size; i++){
  var sId = dsService.getColumn(i, 'id');
  var sUrl = smf_Str(dsService.getColumn(i, 'url'));
  var oService = new RequestService(this, sId);
  var sMessage = oService.validate();

  if("codes" == sId){
   sMessage += "\nid->codes는 사용할수 없습니다.\n다른이름으로 사용하세요.";
  }

  if(sMessage != ""){
   var sUrl = "";
   if(isNotNil(this.url)){
    sUrl = this.url;
   }else{
    sUrl = this.getOwnerFrame().formurl;
   }
   rtn = false;
   alert(format(sUrl + " dsService 설정에러\n{0}/{1}행(id='{2}')의 설정에러입니다.\n" + sMessage, [i+1, dsService.rowcount, sId]));
  }


  var aryDataset = oService.toArrayByOutDatasets();
  for(var n=0,size2=aryDataset.length; n<size2; n++){
   var ds = aryDataset[n];
   if(ds == null) continue;

   var bChkInvalid = false;

   //그리드로 연결되어있는 데이터셋만 확인.
   var grd = smf_GridByDataset(ds);
   if(grd != null){
    bChkInvalid = ds.getColID(0) != "_chk";
   }

   var bUseClientLayoutInvalid = ds.useclientlayout != true;
   if(ds.useclientlayout){
    //다이나믹 레이아웃으로 사용하겠다고 선언하면 에러아님
    if(ds.getConstColumn("isDynamicLayout") == "true"){
     bUseClientLayoutInvalid = false;
    }
   }
   var bKeysInvalid = false;
   var sNotExistColumnNames = "";

   var sMessage = "";
   //keys체크
   var sKeys = smf_Str(ds.getConstColumn('keys'));;
   if(! isNil(sKeys)){
                if(sKeys.indexOf('columnText:"') == 0){
                    sKeys = sKeys.substr(sKeys.lastIndexOf('"')+1);
                }

    var idx = 0;
    var aryCol = sKeys.split(',');
    for(var x=0,sizex=aryCol.length; x<sizex; x++){
     if(null == ds.getColumnInfo(aryCol[x])){
      bKeysInvalid = true;
      sNotExistColumnNames += "," + aryCol[x] + "(" + x + ")";
     }
    }
    if(sNotExistColumnNames != "") sNotExistColumnNames = sNotExistColumnNames.substring(1, 999);
   }

   if(bUseClientLayoutInvalid || bKeysInvalid){//bChkInvalid || 제거
    rtn = false;
    var sMessage = format("Dataset({0})의 표준설정에러.\n", [ds.name]);
    if(bChkInvalid){
     //sMessage += format("\t그리드({0})에 binddataset으로 연결된 Dataset은 _chk컬럼을 0번째에 추가하셔야 합니다.\n", [grd.name]);
    }
    if(bUseClientLayoutInvalid){
     sMessage += "\tuseclientlayout속성을 true로 설정하셔야 합니다.\n";
    }
    if(bKeysInvalid){
     sMessage += format("\tConstColumns의 keys컬럼에서 [{0}]은 없습니다.\n", [sNotExistColumnNames]);
    }
    alert(sMessage);
   }
  }
 }
 return rtn;
}

/**
 * (공통만사용)서비스객체
 *
 * @param
 * @return boolean
 * @see
 */
RequestService = function(form, id, jParams){
 RequestService.prototype.evalValue = function(sValue){
  if(sValue == undefined) return "";

  if(0 == sValue.indexOf('eval:')){ //eval
   return eval(sValue.substring(5, sValue.length));
  }

  return sValue;
 }

 RequestService.prototype.toArrayByDatasets = function(sInOrOut){
  var nIndex = 0;
  var sDatasets = "";
  if(sInOrOut == "in"){
   nIndex = 1;
   sDatasets = this.inDatasets;
  }else if(sInOrOut == "out"){
   nIndex = 0;
   sDatasets = this.outDatasets;
  }

  sDatasets = smf_Str(sDatasets).trim();

  var arySplitDataset = smf_Split(sDatasets, " ");
  var aryDs = [];
  for(var i=0,size=arySplitDataset.length; i<size; i++){
   var sDatasetName = arySplitDataset[i].split('=')[nIndex];
   var ds = null;
   if(-1 == sDatasetName.indexOf(":")){
    ds = this.form.objects[sDatasetName];
   }else{
    sDatasetName = sDatasetName.substring(0, sDatasetName.indexOf(":"));
    ds = this.form.objects[sDatasetName];
   }

            aryDs.push(ds);
  }

  return aryDs;
 }

 RequestService.prototype.toArrayByInDatasets = function(){
  return toArrayByDatasets('in');
 }

 RequestService.prototype.toArrayByOutDatasets = function(){
  return toArrayByDatasets('out');
 }

 RequestService.prototype.hasDataset = function(sDatasetName){
  if(-1 == sDatasetName.indexOf(":")){
   return null != this.form.objects[sDatasetName];
  }else{
   sDatasetName = sDatasetName.substring(0, sDatasetName.indexOf(":"));
   return null != this.form.objects[sDatasetName];
  }

 }

 RequestService.prototype.validate = function(){
  if(this.row == -1){
   return format("id '{0}'은(는) dsService에 없습니다.", [this.id]);
  }

  if(smf_Str(this.url).indexOf("localhost") >= 0){
   return format('url->localhost는 사용할수 없습니다.\n"svc_neis::"로 사용하세요.', []);
  }

  if(this.url.indexOf("::") > -1 &&  this.url.indexOf("::/") == -1){
   return format('url항목에 {0}->"/"가 빠졌습니다.\n{1}로 변경하세요.',
    [this.url, this.url.substring(0, this.url.indexOf("::")+2) + "/" + this.url.substring(this.url.indexOf("::")+2, 999)]);
  }


   var sInDatasets = smf_Str(this.form.dsService.getColumn(this.row, "inDatasets")).trim();
   if(sInDatasets.indexOf("  ") != -1){
   trace(
   format("화면{0}의 dsService의 '{1}'에 inDatasets항목에 두개의 공백이 있습니다.\n"
         +"에러가 날수 있으니 데이터셋간의 공백은 하나만 입력하세요", [this.form.name, this.id]));
   }
//   if( sInDatasets.indexOf("  ") != -1){
//    return format('service에러\n{0}/{1}행의 "{2}"서비스의\n{3}항목[{4}]에서 데이터셋간의 구분공백이 두글자이상 입력되어있습니다.\n'
//                 +'공백은 한글자만 사용하세요',
//                 [this.row+1, this.form.dsService.rowcount, this.id, "inDatasets", sInDatasets]);
//  }

  if(sInDatasets != ""){
   var aryDatasetNames = sInDatasets.split(" ");
   for(var i=0,size=aryDatasetNames.length; i<size; i++){
    if(aryDatasetNames[i].trim() == "") continue;
    var aryServerAndLocalDatasetName = this.buildDatasetName(aryDatasetNames[i]).split("=");
    if(! this.hasDataset(aryServerAndLocalDatasetName[1])){
     return format('service에러\n{0}/{1}행의 "{2}"서비스의\n{3}("{4}")항목({5}번째)의 데이터셋명은 존재하지 않습니다.',
     [this.row+1, this.form.dsService.rowcount, this.id, "inDatasets", sInDatasets, i+1]);
    }
   }
  }

   var sOutDatasets = smf_Str(this.form.dsService.getColumn(this.row, "outDatasets")).trim();
   if(sOutDatasets.indexOf("  ") != -1){
   trace(
   format("화면{0}의 dsService의 '{1}'에 outDatasets항목에 두개의 공백이 있습니다.\n"
         +"에러가 날수 있으니 데이터셋간의 공백은 하나만 입력하세요", [this.form.name, this.id]));
   }
//   if( sOutDatasets.indexOf("  ") != -1){
//    return format('service에러\n{0}/{1}행의 "{2}"서비스의\n{3}항목[{4}]에서 데이터셋간의 구분공백이 두글자이상 입력되어있습니다.\n'
//                 +'공백은 한글자만 사용하세요',
//                 [this.row+1, this.form.dsService.rowcount, this.id, "outDatasets", sOutDatasets]);
//   }
  if(sOutDatasets != ""){
   var aryDatasetNames = sOutDatasets.split(" ");
   for(var i=0,size=aryDatasetNames.length; i<size; i++){
    if(aryDatasetNames[i].trim() == "") continue;
    var aryLocalAndServerDatasetName = this.buildDatasetName(aryDatasetNames[i]).split("=");
    if(! this.hasDataset(aryLocalAndServerDatasetName[0])){
     return format('service에러\n{0}/{1}행의 "{2}"서비스의\n{3}("{4}")항목({5}번째)의 데이터셋명은 존재하지 않습니다.',
     [this.row+1, this.form.dsService.rowcount, this.id, "outDatasets", sOutDatasets, i+1]);
    }
   }
  }

  return "";
 }

 RequestService.prototype.buildDatasetName = function(sSplitedDatasetNames){
  var result = "";
  var aryDatasetNames = smf_Split(sSplitedDatasetNames, " ");

  for(var i=0,size=aryDatasetNames.length; i<size; i++){
   var sName = aryDatasetNames[i];
   if(-1 == sName.indexOf("=")){
    sName += "=" + sName;
   }
   result += " " + sName;
  }

  return result.trim();
 }

 RequestService.prototype.attachUpdateMode = function(sDatasetName){
  if(this.id.indexOf("insert") == 0 ||
     this.id.indexOf("update") == 0 ||
     this.id.indexOf("delete") == 0 ||
     this.id.indexOf("save") == 0 ){

   if(sDatasetName.indexOf(":") == -1){
    sDatasetName += ":U";
   }
  }

  return sDatasetName;
 }

 RequestService.prototype.assignProperties = function(){
  i = this.row;
  var ds = this.form.dsService;
  this.group = ds.getColumn(i, 'group');
  this.id = ds.getColumn(i, 'id');
  this.url = smf_Str(evalValue(ds.getColumn(i, 'url')));

  this.inDatasets = this.attachUpdateMode(this.buildDatasetName(evalValue(smf_Str(ds.getColumn(i, 'inDatasets')).trim())));
  this.outDatasets= this.buildDatasetName(evalValue(smf_Str(ds.getColumn(i, 'outDatasets')).trim()));
  this.params = evalValue(ds.getColumn(i, 'params'));
  this.packageName = evalValue(ds.getColumn(i, 'package'));

  if(this.packageName.length > 0 && "." != this.packageName.charAt(this.packageName.length-1)){
   packageName += ".";
  }else{
   packageName = "";
  }

  this.service = evalValue(ds.getColumn(i, 'service'));
  this.vo = evalValue(ds.getColumn(i, 'vo'));
  this.method = evalValue(ds.getColumn(i, 'method'));
  this.svctp = evalValue(ds.getColumn(i, 'svctp'));
  this.callback = ds.getColumn(i, 'callback');

  if(this.callback != "lfn_trans_callback"){
   this.callback = "lfn_trans_callback";
   var sMessage = format('service에러\n"{0}"서비스의 callback은 "lfn_trans_callback"이어야 합니다.(표준임)', [this.id]);
   alert(sMessage);
  }
  this.state = ds.getColumn(i, 'state');
  this.reason = ds.getColumn(i, 'reason');
  this.message = ds.getColumn(i, 'message');
  
  this.executeDate = smf_Str(ds.getColumn(i, 'executeDate'));
  this.executeCount = parseInt(nvl(ds.getColumn(i, 'executeCount'), '0'));

  this.bizMessage = ds.getColumn(i, 'bizMessage');   //- 사용자 정의 Message 처리
  this.asynctp = iif('false'==ds.getColumn(i, 'asynctp'),false, true); //- 동기/비동기 설정(Default:true)
  this.bintp = ds.getColumn(i, 'bintp');     //- Binary 처리 여부(Default:false)
  this.comtp = ds.getColumn(i, 'comtp');     //- Data 압축 여부(Default:false)
 };

 //그리드의 Title중에 _chk인 항목을 uncheck해준다.
  RequestService.prototype.uncheckGridTitle = function (){
  var aryGrid = this.form.smf_FindGridsByServiceId(this.id);
  for(var nGridIndex=0,sizeGrid=aryGrid.length; nGridIndex<sizeGrid; nGridIndex++){
   var grd = aryGrid[nGridIndex];

   grd.enableredraw = false;
   //head cellcount과 body cell중 작은count로 loop를 돈다.
   var nCellCount = 0;
   if(grd.getCellCount('head') > grd.getCellCount('body')){
    nCellCount = grd.getCellCount('body');
   }else{
    nCellCount = grd.getCellCount('head');
   }

   for(var i=0,size=nCellCount; i<size; i++){
    var sText = grd.getCellProperty('body', i, 'text');
    if(sText == undefined) continue;

    //body의 컬럼이 _chk 이고 header의 edittype이 checkbox인것만 uncheck한다.
    if(sText.indexOf('bind:_chk') >= 0){
     if(grd.getCellProperty("head", i, "edittype") == "checkbox"){
      grd.setCellProperty('head', i, 'expr', '0');
      break;
     }
    }
   }
   grd.enableredraw = true;
  }
  }


  RequestService.prototype.execute = function (){
  //로그를 남긴다.
  if(frmFormSpy != null){
   frmFormSpy.fncWriteLog(this.toString());
  }
  this.form.dsService.setColumn(this.row, "state", "ready");

  var sMessage = this.validate();
  if("" != sMessage){
   this.state = "fail";
   this.reason = "-1";
   this.message = format("smf_Service에러\n{0}/{1}서비스(id='{2}')에러\n{3}", [this.row+1, this.form.dsService.rowcount, this.id, sMessage]);
   this.form.dsService.setColumn(this.row, "state", this.state);
   this.form.dsService.setColumn(this.row, "reason", this.reason);
   this.form.dsService.setColumn(this.row, "message", this.message);
   alert(this.message);
   return false;
  }


  //데이터를 가져오고 있습니다 표시
  var grids = this.form.smf_FindGridsByServiceId(this.id);
  for(var i=0,size=grids.length; i<size; i++){
   grids[i].enableredraw = false;
   grids[i].enableredraw = true;
   if(isNil(grids[i].binddataset)) continue;
   grids[i].nodatatext = "데이터를 가져오고 있습니다...";
  }

  //일단 로그안남기게
  if(this.id.indexOf("log_") == 0) return;

  this.form.dsService.setColumn(this.row, "executeDate", (new Date()).toFormatString("%Y-%m-%d %H:%M:%S"));
  this.executeCount++;
  this.form.dsService.setColumn(this.row, "executeCount", executeCount);

  //this.asynctp
  this.form.svcf_Transaction(
   this.id,
   this.url,
   this.inDatasets,
   this.outDatasets,
   "svcId='" + this.packageName + this.service + "' voId='" + this.packageName + this.vo + "' method='" + this.method + "'",
   'smf_RequestServiceDefaultCallback',
   this.curd,
   this.bizMessage,
   this.asynctp,
   this.bintp,
   this.comtp
  );

  if(this.asynctp == true){
   //async일때만 grid에 연결된 데이터셋을 모두 clear
   for(var i=0,size=grids.length; i<size; i++){
    if(isNil(grids[i].binddataset)) continue;
    var ds = this.form.objects(grids[i].binddataset);
    if(ds == null) ds = eval(grids[i].binddataset);
    if(ds == null) continue;
    ds.clearData();
   }
  }

  //조회일경우만 로그를 남긴다.
  //로그를 남길때는 로그를 남기지 않는다. 당연한가..;
  if(this.svctp == "R" && this.id.indexOf("log_") != 0){
   smf_ServiceLog(this);
  }
  };

  RequestService.prototype.hasBeenService = function (){
        var sReason = smf_Str(this.form.dsService.getColumn(this.row, "reason"));
        if(sReason == "" || sReason == "-1") return false;
        return true;
  };

  RequestService.prototype.toString = function (){
  return new Date().toFormatString("%Y-%m-%d %H:%M:%S") + "\n\t"
        +"id =[" + this.id + "]\n\t"
        +"url =[" + this.url + "]\n\t"
        +"inDatasets =[" + this.inDatasets + "]\n\t"
        +"outDatasets=[" + this.outDatasets + "]\n\t"
        +"params=[" + this.params + "]\n\t"
        +"package=[" + this.packageName + "]\n\t"
        +"vo =[" + this.vo + "]\n\t"
        +"service =[" + this.service + "]\n\t"
        +"method =[" + this.method + "]\n\t"
        +"svctp =[" + this.svctp + "]\n\t"
        +"callback =[" + this.callback + "]\n\t"
        +"state =[" + this.state + "]\n\t"
        +"reason =[" + this.reason + "]\n\t"
        +"message =[" + this.message + "]\n\t"
        +"bizMessage =[" + this.bizMessage + "]\n\t"
        +"asynctp =[" + this.asynctp + "]\n\t"
        +"bintp =[" + this.bintp + "]\n\t"
        +"comtp =[" + this.comtp + "]\n\n"
        ;
  };

 this.form = form;
 this.id = id;
 this.jParams = nvl(jParams, {});
 this.row = this.form.dsService.findRow("id", id);
 if(this.row == -1) return;
 this.form.dsService.setColumn(this.row, "state", "");
 this.assignProperties();
}


/**
 * (공통만사용)server에 로그를 남긴다.
 *
 * @param  oService : RequestService 객체

 * @return void
*/
function smf_ServiceLog(oService){
    var form = oService.form;
   
    var ds = form.smf_GetOrCreateDataset("_dsLog");
    var dsIn = form.smf_GetOrCreateDataset("_dsLog" + oService.id);
    dsIn.clear();
    dsIn.addColumn("PageId", "STRING");
    dsIn.addColumn("menuId", "STRING");
    dsIn.addColumn("SsubbSysCode", "STRING");
    dsIn.addColumn("serviceId", "STRING");
    dsIn.addColumn("searchXml", "STRING");
    dsIn.addColumn("searchType", "STRING");
    dsIn.addRow();

    var aryInDatasets = oService.toArrayByInDatasets();
    var sSearchXml = "";
    if(aryInDatasets.length > 0){
        sSearchXml = aryInDatasets[0].saveCSV();
    }

    dsIn.setColumn(0, "PageId", form.smf_FormPageId(form));
    dsIn.setColumn(0, "menuId", form.smf_FormMenuId(form));
    dsIn.setColumn(0, "SsubbSysCode", form.smf_SubSysCode());
    dsIn.setColumn(0, "serviceId", oService.url);
    dsIn.setColumn(0, "searchXml", sSearchXml);
    dsIn.setColumn(0, "searchType", oService.svctp);

    var sServiceId = "log_" + oService.id;
    form.smf_DService({
         id                 : sServiceId
        ,url                : "svc_neis::/sye_sye_lm00_000.xp"
        ,inDatasets         : "dsSyeSyeLm00M00SVO=" + dsIn.name
        ,outDatasets        : ""
        ,asynctp            : "true"
        ,callback           : "lfn_trans_callback"
        ,innerCallback      : ""
        ,bindingControls    : ""
    });
}


/**
 * (공통만사용)서비스실행후 호출되는 callback function
 *
 * @param  sId dsService Dataset에 등록한 id
 * @param  nReason 코드
 * @param  sMessage - message
 * @return void
 * @see
 */
function smf_RequestServiceDefaultCallback(sId, nReason, sMessage){
 var nRow = dsService.findRow('id', sId);
 var service = new RequestService(this, sId);

 sMessage = smf_Str(sMessage);

 dsService.setColumn(nRow, 'reason', nReason);
 dsService.setColumn(nRow, 'message', sMessage);

 try{
  if(nReason == -1){
   //실패
   dsService.setColumn(nRow, "state", "fail");

   //데이터를 가져오는데 실패했습니다
   var grids = smf_FindGridsByServiceId(sId);
   for(var i=0,size=grids.length; i<size; i++){
    grids[i].enableredraw = false;
    grids[i].enableredraw = true;
    if(isNil(grids[i].binddataset)) continue;

    if(eval(grids[i].binddataset).rowcount == 0){
     grids[i].nodatatext = "데이터를 가져오는데 실패했습니다";
    }
   }
  }else{
   //성공
   dsService.setColumn(nRow, "state", "succ");
   smf_InitBindingControls(nRow);// bindingControls에 있는 컨트롤들의 세팅
   service.uncheckGridTitle();

   //그리드에 데이터가 없습니다.표시
   var grids = smf_FindGridsByServiceId(sId);
   for(var i=0,size=grids.length; i<size; i++){
    if(isNil(grids[i].binddataset)) continue;

    if(eval(grids[i].binddataset).rowcount == 0){
     grids[i].nodatatext = "조회된 데이터가 없습니다";
    }else{
     grids[i].nodatatext = "데이터가 없습니다";
    }

    grids[i].enableredraw = false;
    grids[i].enableredraw = true;
   }
  }
 }finally{
  var jParams = smf_Service.prototype.serivces[sId];
        try{
            //innerCallback함수 호출
            var sInnerCallback = dsService.getColumn(nRow, 'innerCallback');
            if(isNotNil(sInnerCallback)){
                eval(format(sInnerCallback + "('{0}', '{1}', '{2}', {3}, jParams)", [sId, nReason, sMessage, "service"]));
            }

        }finally{
            this.lfn_trans_callback(sId, nReason, sMessage, jParams);
        }
 }
}


/**
 * (공통만사용)서비스를 얻어온다.
 *
 * @param  sId      - dsService Dataset에 등록한 id
 * @return boolean
 * @see
 */
function smf_HasService(sServiceId){
 if(objects['dsService'] == null){
  alert('dsService Dataset이 없습니다');
  return false;
 }

 var nRow = dsService.findRow('id', sServiceId);
 if(nRow == -1){
  alert(sServiceId + "는(은) dsService에 등록되지 않은 아이디입니다");
  return false;
 }

 return true;
}

/**
 * 서비스를 호출한다.
 *
 * @param  sId      - dsService Dataset에 등록한 id
 * @param  sParams  - parameter
 * @param  bMessage - message를 보일건지.. 사용안함
 * @return boolean
 * @see    smf_Service('selectCode');
 */
function smf_Service(sId, jParams){
 jParams = nvl(jParams, {});
 smf_Service.prototype.serivces[sId] = jParams;
 var srv = new RequestService(this, sId, jParams);
 return srv.execute();
}
smf_Service.prototype.serivces = {};

/**
 * 동적서비스를 호출한다.
 *
 * @param  jParam
 * @return void
 * @see    smf_DService({
             id              : "codeEx"
            ,url             : "svc_neis::/his_cdm_cc00_002.xp"
            ,inDatasets      : "dsHisCdmCc00M01SVO"
            ,outDatasets     : " dsDghtCrseScCode=dsHisCdmCc00M01SVO_listDghtCrseScCode"
                             + " dsDddepCode=dsHisCdmCc00M01SVO_listDddepCode"
                             + " dsGrade=dsHisCdmCc00M01SVO_listGrade"
                             + " dsOrdScCode=dsHisCdmCc00M01SVO_listOrdScCode"
                             + " dsOrdGradeDddep=dsHisCdmCc00M01SVO_listOrdGradeDddep"
            ,svctp           : "R"
            ,innerCallback   : ""
            ,bindingControls : " dsOrdGradeDddep=SEL"
                             + " dsDghtCrseScCode=SEL"
        });
 */
function smf_DService(jServiceParams, jParams){
 var sServiceId = smf_Str(jServiceParams.id).trim();
 var ds = objects("dsService");

 if(ds.findRow("id", sServiceId) != -1){
  ds.deleteRow(ds.findRow("id", sServiceId));
 }
 var nRow = ds.addRow();

 dsService.setColumn(nRow, "id", sServiceId);
 dsService.setColumn(nRow, "inDatasets", smf_Str(jServiceParams.inDatasets).trim());
 dsService.setColumn(nRow, "outDatasets", smf_Str(jServiceParams.outDatasets).trim());
 dsService.setColumn(nRow, "svctp", "");
 dsService.setColumn(nRow, "bindingControls", smf_Str(jServiceParams.bindingControls).trim());
 dsService.setColumn(nRow, "bizMessage", "");
 dsService.setColumn(nRow, "callback", "lfn_trans_callback");
 dsService.setColumn(nRow, "innerCallback", smf_Str(jServiceParams.innerCallback).trim());
 dsService.setColumn(nRow, "url", smf_Str(jServiceParams.url).trim());
 dsService.setColumn(nRow, "state", "");
 dsService.setColumn(nRow, "reason", "");
 dsService.setColumn(nRow, "message", "");
 dsService.setColumn(nRow, "asynctp", iif(smf_Str(jServiceParams.asynctp).trim(), "false", "true"));
 dsService.setColumn(nRow, "bintp", "");
 dsService.setColumn(nRow, "comtp", "");

 return smf_Service(sServiceId, jParams);
}


/**
 * 서비스상태를 반환한다.
 *
 * @param  sId      - dsService Dataset에 등록한 id
 * @return String
 * @see    alert(smf_ServiceSatate('selectCode')); // ready or succ or fail
 */
function smf_ServiceSatate(sId){
 var nRow = dsService.findRow('id', sId);
 return dsService.getColumn(nRow, "state");
}

/**
 * 서비스호출이 성공했는지
 *
 * @param  sId      - dsService Dataset에 등록한 id
 * @return String
 * @see    alert(smf_IsServiceSucc('selectCode')); // true or false
 */
function smf_IsServiceSucc(sId){
 return "succ" == smf_ServiceSatate(sId);
}

/**
 * 서비스호출이 성공하지 않았는지
 *
 * @param  sId      - dsService Dataset에 등록한 id
 * @return boolean
 * @see    alert(smf_IsNotServiceSucc('selectCode')); // true or false
 */
function smf_IsNotServiceSucc(sId){
 return ! smf_IsServiceSucc(sId);
}

/**
 * 서비스호출이 실패했는지
 *
 * @param  sId      - dsService Dataset에 등록한 id
 * @return boolean
 * @see    alert(smf_IsServiceFail('selectCode')); // true or false
 */
function smf_IsServiceFail(sId){
 return "fail" == smf_ServiceSatate(sId);
}

/**
 * 사용안함
 *
 * @param
 * @return
 * @see
 */
function smf_FindRows(ds, sColumnName, sColumnValue){
 var idx = 0;
 var aryResult = [];
 for(var i=0,size=ds.rowcount; i<size; i++){
  if(sColumnValue == ds.getColumn(i, sColumnName)){
   aryResult[idx++] = i;
  }
 }

 return aryResult;
}


/**
 * ds에서 aryExpr에 해당하는 row index를 얻는다.
 *
 * @param  ds : Dataset
 * @param  aryExpr : json
 * @return rowindex를 가지고 있는 array
 * @see    smf_SearchRows(ds, {hakbun:'10101010', name:'홍길동'});
 */
function smf_SearchRows(ds, jExpr){
 var idx = 0;
 var aryResult = [];
 for(var i=0,size=ds.rowcount; i<size; i++){
  var b = true;

  for(var sColumn in jExpr){
   sValue = jExpr[sColumn];
   if(sValue != ds.getColumn(i, sColumn)){
    b = false;
    break;
   }
  }

  if(b){
   aryResult[idx++] = i;
  }
 }

 return aryResult;
}

/**
 * (공통만사용)
 *
 * @param
 * @param
 * @return
 * @see
 */
ModifyObserver= function(form){
    ModifyObserver.prototype.updatedDataset = function (ds){
        if(ds.getDeletedRowCount() > 0 ) return true;

        for(var i=0,size=ds.getRowCount(); i<size; i++){
            if(smf_IsUpdateRow(ds, i)) return true;
        }

        return false;
    }

    ModifyObserver.prototype.hasComponent= function (sSplitedID){
        var aryID = sSplitedID.split(",");
        for(var i=0,size=aryID.length; i<size; i++){
            if(null == eval(aryID[i])) return false;
        }
        return true;
    }

    ModifyObserver.prototype.hasDataset= function (sSplitedID){
        var aryID = sSplitedID.split(",");
        try{
            for(var i=0,size=aryID.length; i<size; i++){
                if(isNil(eval(aryID[i]))) return false;
            }
        }catch(e){
            trace(e.message);
            return false;
        }
        return true;
    }

    ModifyObserver.prototype.validates = function (){
        var bResult = true;
        var sNotExistDatasetMessage = "dsNotifySubject데이터셋 설정에러\r\n{0}/{1}행 {2}에 {3}항목의 {4}({5})가 존재하지 않습니다.\r\n사용하지 않는 옵션은 used=false로 설정하세요.";
        var sNotExistComponentMessage = "dsNotifySubject데이터셋 설정에러\r\n{0}/{1}행 {2}에 {3}항목의 {4}({5})가 존재하지 않습니다.\r\n사용하지 않는 옵션은 used=false로 설정하세요.";


        for(var i=0,size=this.form.dsNotifySubject.rowcount; i<size; i++){
            var sType = this.form.dsNotifySubject.getColumn(i, "type");
            var sUsed = this.form.dsNotifySubject.getColumn(i, "used");
            if(sUsed != 'true') continue;
            var sParam01 = this.form.dsNotifySubject.getColumn(i, "param01");
            var sParam02 = this.form.dsNotifySubject.getColumn(i, "param02");
            var sParam03 = this.form.dsNotifySubject.getColumn(i, "param03");

            switch(sType){
                case "close":
                case "rowChange":
                    if(! this.hasDataset(sParam01)){
                        var sMessage = format(sNotExistDatasetMessage, [i+1, size, sType, 'param01', 'DatasetID', sParam01]);
                        trace(sMessage);
                        alert(sMessage);
                        bResult = false;
                    }
                    break;

                case "select":
                    if(! this.hasComponent(sParam01)){
                        var sMessage = format(sNotExistComponentMessage, [i+1, size, sType, 'param01', 'Div ID 또는 TabPage ID', sParam01]);
                        trace(sMessage);
                        alert(sMessage);
                        bResult = false;
                    }
                    if(! this.hasDataset(sParam02)){
                        var sMessage = format(sNotExistDatasetMessage, [i+1, size, 'param02', 'DatasetID', sParam02]);
                        trace(sMessage);
                        alert(sMessage);
                        bResult = false;
                    }
                    break;

                case "masterDetail":
                    if(! this.hasDataset(sParam01)){
                        var sMessage = format(sNotExistDatasetMessage, [i+1, size, sType, 'param01', 'DatasetID', sParam01]);
                        trace(sMessage);
                        alert(sMessage);
                        bResult = false;
                    }

                    if(! this.hasDataset(sParam02)){
                        var sMessage = format(sNotExistDatasetMessage, [i+1, size, sType, 'param02', 'DatasetID', sParam02]);
                        trace(sMessage);
                        alert(sMessage);
                        bResult = false;
                    }
                    break;
            }
        }

        return bResult;
    }

    ModifyObserver.prototype.isModifyDatasets = function (sSplitedDatasetNames){
        if(isNil(sSplitedDatasetNames)) return false;

        for(var i=0,names=sSplitedDatasetNames.split(","),size=names.length; i<size; i++){
            if(isNil(names)) continue;
            var ds = this.form.objects[names[i]];
            if(ds == null){
    ds = eval(names[i]);
   }

   if(ds == null) continue;
            if(! ds instanceof Dataset) continue;

            if(updatedDataset(ds)) return true;
        }

        return false;
    }


    ModifyObserver.prototype.findRows = function(sType){
        var idx = 0;
        var aryResult = [];
        var ds = this.form.dsNotifySubject;
        for(var i=0,size=ds.getRowCount(); i<size; i++){
            if('true' != ds.getColumn(i, 'used')) continue;

            if(sType == ds.getColumn(i, 'type')){
                aryResult[idx++] =i;
            }
        }

        return aryResult;
    }

    ModifyObserver.prototype.registMasterDetailHandler = function(){
        return;
        var rows = this.findRows('masterDetail');

        for(var i=0,size=rows.length; i<size; i++){
            var idx = rows[i];
            var dsMaster = this.form.objects(this.form.dsNotifySubject.getColumn(idx, "param01"));
            var sSplitedDetailDatasets = this.form.dsNotifySubject.getColumn(idx, "param02");

            if(isModifyDatasets(sSplitedDetailDatasets)){
                return true == confirm("변경된 데이터가 있습니다.\n조회하시겠습니까?\n예를 선택하면 변경된 내용은 저장되지 않습니다");
            }
        }

        return true;
    }

    ModifyObserver.prototype.closeNotifyWhenUpdatedData = function(){
        var rows = this.findRows('close');
        try{
            smf_ApplyData();
        }catch(e){}

        for(var i=0,size=rows.length; i<size; i++){
            var idx = rows[i];
            var sSplitedDatasetNames = this.form.dsNotifySubject.getColumn(idx, "param01");

            if(isModifyDatasets(sSplitedDatasetNames)){
                return true == confirm("변경된 데이터가 있습니다.\종료하시겠습니까?");
            }
        }

        return true;
    }

    ModifyObserver.prototype.confirmSelect = function(oComposite, sMessagePrefix){
        if(isNil(sMessagePrefix)) sMessagePrefix = "조회";
        var rows = this.findRows('select');
        for(var i=0,size=rows.length; i<size; i++){
            var idx = rows[i];
            var scomponentName = this.form.dsNotifySubject.getColumn(idx, "param01");
            var sSplitedDatasetNames = this.form.dsNotifySubject.getColumn(idx, "param02");

            var comp = this.form[scomponentName];
            if(comp == null){
    comp = eval(scomponentName);
   }

            if(comp != oComposite) continue;
            if(isModifyDatasets(sSplitedDatasetNames)){
                return true == confirm(format("변경된 데이터가 있습니다.\n{0}하시겠습니까?\n", [sMessagePrefix]));
            }
        }

        return true;
    }

    ModifyObserver.prototype.canchangeConfirmWhenModifyData = function(tab){
  smf_ApplyData();
        var rows = this.findRows('tabChange');
        for(var i=0,size=rows.length; i<size; i++){
            var idx = rows[i];
            var sTabId = this.form.dsNotifySubject.getColumn(idx, "param01");
            var oTab = eval(sTabId);
            if(oTab != tab) continue;

            var sSplitedDatasetNames = smf_Str(this.form.dsNotifySubject.getColumn(idx, "param02"));
            if(isModifyDatasets(sSplitedDatasetNames)){
                return true == confirm("변경된 데이터가 있습니다.\n다른 탭을 선택하시겠습니까?");
            }
        }
    }

    this.form = form;
}

/**
 * 폼이 종료될때 데이터셋이 변경되면 confirm메시지를 뿌린다.
 *
 * @param  sValue :
 * @param  sLikeValue
 * @return boolean
 * @see    smf_IsLike("100", "10")//true
 *         smf_IsLike("100", "100")//true
 *         smf_IsLike("100", "1000")//false
 */
function smf_CloseFormConfirm(aryDataset, bShowMessage, fncAccept){
 if(bShowMessage != false) bShowMessage = true;

 smf_ApplyData();

 var jResult = {canClose : true,
                message : "",
                modifiedDataset : null};

 for(var i=0,size=aryDataset.length; i<size; i++){
  var ds = aryDataset[i];
  if(fncAccept != undefined){
   if(! fncAccept(i, ds)) continue;
  }

  if(smf_IsUpdate(ds)){
   jResult.canClose = false;
   jResult.modifiedDataset = ds;
   jResult.message = "변경된 데이터가 있습니다.\종료하시겠습니까?";

   if(bShowMessage){
    //
    if(confirm(jResult.message)){
     jResult.canClose = true;
    }
   }

   return jResult;
  }
 }

 return jResult;
}

/**
 * like
 *
 * @param  sValue :
 * @param  sLikeValue
 * @return boolean
 * @see    smf_IsLike("100", "10")//true
 *         smf_IsLike("100", "100")//true
 *         smf_IsLike("100", "1000")//false
 */
function smf_IsLike(sValue, sLikeValue){
 if(sValue == null || sValue == undefined) return false;
 if(sLikeValue == null || sLikeValue == undefined) return false;

 sValue += "";
 sLikeValue += "";

 return 0 == sValue.indexOf(sLikeValue);
}

/**
 * text를 formatting한다.
 *
 * @param  sFormat   - 포매팅할 문자열
 * @param  sFormat   - 포매팅할 문자열
 * @param  aryParams - 파라메터
 * @return String
 * @see    alert(Format("{0} x {1} = {2}", [10, 2, 10 * 2])); // 10 x 2 = 20
 */
function smf_Format(sFormat, aryParams){
 for(var i=0, size=aryParams.length; i<size; i++){
  sFormat = sFormat.replace("\{" + i + "\}" , aryParams[i] );
 }

 return sFormat;
}

/**
 * ds데이터셋의 값을셋팅
 *
 * @param  ds : 데이터셋
 * @param  sColumnName : 변경될 컬럼명
 * @param  sTrueValue  : 조건이 참일경우 세팅할값(값이 undefined면 세팅이 안됨.)
 * @param  sFalseValue : 조건이 거짓일경우 세팅할값(값이 undefined면 세팅이 안됨.)
 * @param  jParam : json 객체
 * @return void
 * @see    smf_SetColumnsExpr(dsMyData, '_chk', '1', '0', {DSModifyRow:true, hakbun:'100', kname:'홍길동'});
 *           //dsMyData의 _chk를 1로 하는데 조건은 hakbun이 '100' and kname이 '홍길동'
 *             DSModifyRow는 ds.getRowType을 비교해서 INSERT,UPDATE일때 값을 변경함.
 *             currow는 ds.rowposition과 i가 같으면 true
 */
function smf_SetColumnsExpr(ds, sColumnName, sTrueValue, sFalseValue, jParam){
 for(var i=0,size=ds.rowcount; i<size; i++){
  var bCondition = true;
  for(var sColID in jParam){
   if(sColID == "DSRowType"){ //DSModifyRow는 ds.getRowType을 비교해서 INSERT,UPDATE일때 값을 변경함.
    var nRowType = ds.getRowType(i);
    var nRowType2 = jParam[sColID];
    bCondition = ( nRowType2 == (nRowType2 | nRowType));
   }else if(sColID == "currow"){ //currow는 ds.rowposition을 비교해서 i와 currow가 같을때 true
    var nCurrow = ds.rowposition;
    bCondition = nCurrow == i;
   }else if(ds.getColumn(i, sColID) != jParam[sColID]){
    bCondition = false;
   }

   if(bCondition == false) break;
  }

  if(bCondition){
   if(sTrueValue != undefined)ds.setColumn(i, sColumnName, sTrueValue);
  }else{
   if(sFalseValue != undefined) ds.setColumn(i, sColumnName, sFalseValue);
  }
 }
}

/**
 * sText를 split한다. sText가 String이 아니면 []넘김
 *
 * @param  sText : split할 text
 * @param  sDelemiter : 구분자
 * @return array
 * @see
 */
function smf_Split(sText, sDelemiter){
    sText = smf_Str(sText);
    if(sText.length == 0) return [];

    return sText.split(sDelemiter);
}

/**
 * sText를 String으로 형변환
 * undefined   -> ""
 * null        -> ""
 * 1           -> "1"
 * ""          -> ""
 * 1.1         -> "1.1"
 * @param  sText
 * @return String
 * @see
 */
function smf_Str(sText){
 if(sText == undefined) return "";
 if(sText == null) return "";
 if(sText instanceof String) return sText;

 return ""+sText;
}

/**
 * 공통코드조회
 *
 * @param  sServiceId : callback함수에서 받을 서비스명
 * @param  aryDataset : 공통코드로 넘길 Array 데이터셋
 * @param  bSync      : 비동기로 넘길지 default false
 * @return void
 * @see    smf_CodeList('code', [dsSchulCrseScCode, dsSchulFaScCode, dsSchulGrgdScCode, dsSchulKndScCode]);
 */
function smf_CodeList(sServiceId, aryDataset, bSync){
 bSync = bSync == true;

 ////////////////////////검증//////////////////////////////
 if(isNil(sServiceId)){
  trace("sServiceId를 입력하세요.");
  alert("sServiceId를 입력하세요.");
  return false;
 }
 sServiceId = smf_Str(sServiceId);
 if(sServiceId.indexOf("code") != 0){
  trace("sServiceId를 'code'로 시작하세요.\n예)code, code01, code02...");
  alert("sServiceId를 'code'로 시작하세요.\n예)code, code01, code02...");
  return false;
 }

 if(! (aryDataset instanceof Array)){
  trace("파라메터aryDataset는 Array이어야 합니다");
  alert("파라메터aryDataset는 Array이어야 합니다");
  return false;
 }


 if(aryDataset.length > 25){
  trace("공통코드갯수가 25개를 넘었습니다.\n25이하로 사용하세요");
  alert("공통코드갯수가 25개를 넘었습니다.\n25이하로 사용하세요");
  return false;
 }

 for(var i=0,size=aryDataset.length; i<size; i++){
  var ds = aryDataset[i];
  var sMessage = "";

  if(! (aryDataset[i] instanceof Dataset)){
   alert(format("aryDataset[{0}]번째가 Dataset이어야 합니다.", [i]));
   return false;
  }

  for(var n=0,sizen=ds.getConstCount(); n<sizen; n++){
   var sConstId = ds.getConstColID(n);
   if(notIn(sConstId, ["firstCode", "clsfCode", "userDfnSet", "filterExpr"])){
    sMessage += format("{0}.ConstColumns[{1}]({2}) 항목은 잘못된이름입니다.\n", [ds.name, n, sConstId]);
   }


   if(sConstId == "clsfCode" && ds.getConstColumn(sConstId).indexOf("eval:") == 0){
    try{
     eval(ds.getConstColumn(sConstId).substr(5, 999));
    }catch(e){
     sMessage += format("{0}.ConstColumns[{1}]({2}) eval문({3})에 에러가 있습니다.\n", [ds.name, n, sConstId, ds.getConstColumn(sConstId)]);
    }
   }

   if(sConstId == "userDfnSet" && isNil(ds.getConstColumn(sConstId)) == false){
    var sUserDfnSet = smf_Str(ds.getConstColumn(sConstId));
    if(sUserDfnSet.indexOf("=") == -1 && sUserDfnSet.indexOf("eval:") == -1){
     sMessage += format("{0}.ConstColumns[{1}]({2}) 사용에러입니다.\nKEY=VALUE형태로 사용하세요\n"
                       +"예)USER_DFN_CODE_VALUE01=001 또는 USER_DFN_CODE_VALUE05=KR\n", [ds.name, n, sConstId, ds.getConstColumn(sConstId)]);
    }
   }

   if(sConstId == "userDfnSet" && ds.getConstColumn(sConstId).indexOf("eval:") == 0){
    try{
     eval(ds.getConstColumn(sConstId).substr(5, 999));
    }catch(e){
     sMessage += format("{0}.ConstColumns[{1}]({2}) eval문({3})에 에러가 있습니다.\n", [ds.name, n, sConstId, ds.getConstColumn(sConstId)]);
    }
   }
  }

  if("" != sMessage){
   alert(sMessage);
   trace(sMessage);
   return false;
  }
 }

 ////////////////////////검증끝//////////////////////////////
 var _dsCodeList = smf_GetOrCreateDataset('_dsCodeList');
 _dsCodeList.clear();
 //컬럼들을 생성한다.
 for(var i=0,size=aryDataset.length; i<size; i++){
  var ds = aryDataset[i];
  _dsCodeList.addColumn("clsfCode"+(i), "STRING", 256);
  _dsCodeList.addColumn("userDfnSet"+(i), "STRING", 256);
 }
 _dsCodeList.addRow();
 for(var i=0,size=aryDataset.length; i<size; i++){
  var ds = aryDataset[i];

  //clsfCode 값 세팅
  var sClsfCode = smf_Str(ds.getConstColumn("clsfCode"));
  if(sClsfCode.indexOf("eval:") == 0){
   sClsfCode = eval(sClsfCode.substr(5, 999));
  }
  _dsCodeList.setColumn(0, "clsfCode"+(i), sClsfCode);

  //userDfnSet 값 세팅
  var sUserDfnSet = smf_Str(ds.getConstColumn("userDfnSet"));
  if(sUserDfnSet.indexOf("eval:") == 0){
   sUserDfnSet = eval(sUserDfnSet.substr(5, 999));
  }
  _dsCodeList.setColumn(0, "userDfnSet"+(i), sUserDfnSet);
 }

 if(dsService.findRow("id", sServiceId) > -1){
  var nDeleteRow = dsService.findRow("id", sServiceId);
  dsService.deleteRow(nDeleteRow);
 }

 //dsService에 설정
 var nSrvRow = dsService.addRow();
 var sOutDatasets = "";
 for(var i=0,size=aryDataset.length; i<size; i++){
  var ds = aryDataset[i];
  sOutDatasets += ds.name + "="+"dsSymCdmCc01M00SVO_codeList"+i+ " ";
 }

 var sBindingCtrls = "";
 for(var i=0,size=aryDataset.length; i<size; i++){
  var ds = aryDataset[i];
  if(isNil(ds.getConstColumn("firstCode"))) continue;
  sBindingCtrls += " " + ds.name + "=" + ds.getConstColumn("firstCode");
 }

 dsService.setColumn(nSrvRow, "id", sServiceId);
 dsService.setColumn(nSrvRow, "inDatasets", "dsSymCdmCc01M00SVO=_dsCodeList");
 dsService.setColumn(nSrvRow, "outDatasets", sOutDatasets.trim());
 dsService.setColumn(nSrvRow, "package", "edusys.cm.sym.cdm.cc.svc");
 dsService.setColumn(nSrvRow, "vo", "SymCdmCc01M00SVO");
 dsService.setColumn(nSrvRow, "service", "SymCdmCc01M00SVC");
 dsService.setColumn(nSrvRow, "method", "listMultiCode");
 dsService.setColumn(nSrvRow, "svctp", "");
 dsService.setColumn(nSrvRow, "bindingControls", sBindingCtrls.trim());
 dsService.setColumn(nSrvRow, "bizMessage", "");
 dsService.setColumn(nSrvRow, "callback", "lfn_trans_callback");
 dsService.setColumn(nSrvRow, "url", "svc_neis::/sym_cdm_cc00_002.xp");
 dsService.setColumn(nSrvRow, "state", "");
 dsService.setColumn(nSrvRow, "reason", "");
 dsService.setColumn(nSrvRow, "message", "");
 dsService.setColumn(nSrvRow, "asynctp", iif(bSync, "false", "true"));
 dsService.setColumn(nSrvRow, "bintp", "");
 dsService.setColumn(nSrvRow, "comtp", "");

 smf_Service(sServiceId);

 return true;
}

/**
 * sSource를 클립보드로
 *
 * @param  sSource : string
 * @return void
 * @see    smf_CopyToClipboard("this is string")
 */
function smf_CopyToClipboard(sSource){
 system.clearClipboard();
 return system.setClipboard("CF_TEXT",sSource);
}

/**
 * combo에 년도를 추가한다.
 *
 * @param  scmb : combo
 * @param  nYear : 기준년도
 * @param  nPlusMinus : nYear +-할 년수의 수
 * @return void
 * @see    smf_AddYearToDS(cmb, 2010, 3);
 *         --------결과--------
 *         2013
 *         2012
 *         2011
 *         2010 -- 기준년도
 *         2009
 *         2008
 *         2007
 */
function smf_AddYearToDS(cmb, nYear, nPlusMinus){
 nYear = parseInt(nYear);
 nPlus = parseInt(nPlusMinus);

 var ds = cmb.innerdataset;
 if(ds instanceof String){
  ds = objects[ds];
 }

 var sCodeColumn = cmb.codecolumn;
 var sDataColumn = cmb.datacolumn;

 ds.clearData();

 for(var i=1; i<=nPlusMinus; i++){
  var nRow = ds.addRow();
  ds.setColumn(nRow, sCodeColumn, nYear+i);
  ds.setColumn(nRow, sDataColumn, nYear+i);
 }

 var nRow = ds.addRow();
 ds.setColumn(nRow, sCodeColumn, nYear);
 ds.setColumn(nRow, sDataColumn, nYear);

 for(var i=1; i<=nPlusMinus; i++){
  var nRow = ds.addRow();
  ds.setColumn(nRow, sCodeColumn, nYear-i);
  ds.setColumn(nRow, sDataColumn, nYear-i);
 }

 cmb.value = nYear;
}

/**
 * 사용안함
 * _chk타이틀을 클리하면 checked toggle
 * 다른 컬럼을 선택하면 소팅
 *
 * @param  obj:Grid
 * @param  e:GridClickEventInfo
 * @return void
 * @see
 */
function smf_CheckAndSort(obj:Grid, e:GridClickEventInfo, sResetColumns){
 var sColId = obj.getCellProperty("body",e.cell,"text");
 if ("bind:_chk" == sColId){
  utlf_setGridCheckAll(obj, e);
 }else{
  utlf_procGrdSort(obj, e.cell, sResetColumns);
 }
}

/**
 * 공통코드를 필터한다.
 *
 * @param  comboArray
 * @return void
 * @see
 */
function smf_InitChainFilterBase(comboArray) {

    var comboArgs = [[comboArray[0], ""]];

    for(var i=1; i<comboArray.length; i++) {

 

        comboArgs[i] = [comboArray[i],   " upperCdcValue == " + smf_FullId(comboArray[i-1]) + ".value "];

 

    }

 

    smf_InitChainFilter(comboArgs, true);

 

}

/**
 * 코드를 필터한다.
 *
 * @param  aryComboAndOption
 * @param  bSelectFirst : true면 combo.index = 0을 세팅해준다.
 * @return void
 * @see    smf_InitChainFilter([[div_manage.cmb_nara, ""]
              ,[div_manage.cmb_team, " upperClsfCodeValue == div_manage.cmb_nara.value "]
              ,[div_manage.cmb_player, " naraCodeValue == div_manage.cmb_nara.value && upperClsfCodeValue == div_manage.cmb_team.value "]
   ]);

 */
function smf_InitChainFilter(aryComboAndOption, bSelectFirst){
 if(bSelectFirst == undefined || bSelectFirst == null) bSelectFirst = false;
 var nCount = 0;

 //초기작업
 var oPrior = null;
 var oFirstCombo = null;
 //linked list이용
 //master detail관계를 정의
 for(var i=0,size=aryComboAndOption.length; i<size; i++){
  var obj = aryComboAndOption[i][0];
  obj.filterExpr = aryComboAndOption[i][1];
  obj.onitemchanged.insertHandler(0, smf_DoFilterItemChanged);
  obj.chainIndex = i;
  obj.chainCount = size;
  if(i == 0) oFirstCombo = obj;
  if(oPrior != null) oPrior.nextCombo = obj;
  obj.priorCombo = oPrior;
  oPrior = obj;
 }

 //처음 combo를 세팅시킨다.
 if(bSelectFirst){
  oFirstCombo.index = 0;
  smf_DoFilterItemChanged(oFirstCombo);
 }else{
  smf_NextChainFilter(oFirstCombo);
 }
}

/**
 * BindDataset으로 연결된 Dataset.RowPosChange가 발생했을때 호출하는 함수
 *
 * @param  obj : Combo
 * @param  dsBind : Dataset
 * @param  e : DSRowPosChangeEventInfo
 * @return void
 * @see
 */
function smf_NextChainFilter(obj, dsBind:Dataset, e:DSRowPosChangeEventInfo){
 //index 0으로
 while(obj.nextCombo != null){
  obj = obj.nextCombo;
  obj.innerdataset.filter("currow==0 || " + obj.filterExpr);
 }
}

/**
 * (공통만사용) 필터가 연결된 콤보change event
 *
 * @param  obj : Combo
 * @param  e  : ComboChangeEvent
 * @param  bIsNotEvent : 이벤트가아닌 함수호출형태로 호출시 true넘김
 * @return void
 * @see
 */
function smf_DoFilterItemChanged(obj, e, bIsNotEvent){
 if(obj.nextCombo == null) return;
 bIsNotEvent = bIsNotEvent == true;

 var priorObj = obj;
 var priorDs = obj.innerdataset;
 var obj = obj.nextCombo;
 var ds = obj.innerdataset;

 //전체를 선택한 경우 다음 combo가 모두 나오게한다.
 var objFirstCode = smf_Str(priorDs.getConstColumn("firstCode"));
 if(objFirstCode.indexOf("ALL") != -1 && priorObj.index == 0){
  ds.filter("");
 }else{
  ds.filter("currow==0 || " + obj.filterExpr);
 }

 if(! bIsNotEvent){
  obj.index = 0;
 }

 //index 0으로
 while(obj.nextCombo != null){
  priorObj = obj;
  priorDs = obj.innerdataset;
  obj = obj.nextCombo;
  ds = obj.innerdataset;

  //전체를 선택한 경우 다음 combo가 모두 나오게한다.
  var objFirstCode = smf_Str(priorDs.getConstColumn("firstCode"));
  if(objFirstCode.indexOf("ALL") != -1 && priorObj.index == 0){
   ds.filter("");
  }else{
   ds.filter("currow==0 || " + obj.filterExpr);
  }

  if(! bIsNotEvent){
   obj.index = 0;
  }
 }
}

/**
 * 그리드 헤더를 조건을 포함해서 checkbox컬럼을 토글링한다.
 *
 * @param  obj:Grid,
 * @param  e:GridClickEventInfo,
 * @param  fncConditon : function(ds, nRow) return boolean; 형태의 함수
 * @return void
 * @see    smf_ToggleChkHeader(obj, e, function (ds, nRow){
    if(ds.getColumn(nRow,"frmtnYn" )=="Y" || ds.getColumn(nRow,"estblYn" )=="Y"){
     return false;
    }else{
     return true;
    }
   });
 */
function smf_ToggleChkHeader(obj:Grid, e:GridClickEventInfo, fncConditon){
 var celltype = obj.getCellProperty("head",e.cell,"displaytype");
 var utlv_IsGridCheckAll = obj.getCellText(-1,e.cell);

 if (celltype.toUpperCase() != "CHECKBOX") {
  return;
 }

 var dsObj = eval(obj.binddataset);
 var v_Colid = obj.getCellProperty("body", e.cell, "text").replace("bind:", "");

 if (utlv_IsGridCheckAll == 1) {
  utlv_IsGridCheckAll = 0;
 } else {
  utlv_IsGridCheckAll = 1;
 }
 dsObj.enableevent = false;
 for (var i=0; i<dsObj.getRowCount(); i++) {
  if(fncConditon != undefined){
   if( true != fncConditon(dsObj, i)) continue;
  }

  dsObj.setColumn(i, v_Colid, utlv_IsGridCheckAll);
 }
 obj.setCellProperty( "Head", e.cell, "expr", utlv_IsGridCheckAll);
 dsObj.enableevent = true;
}

/**
 * 함수가 있는지 체크
 *
 * @param  sFuncName : 함수명
 * @return boolean
 * @see    smf_HasFunc("test_function");
 */
function smf_HasFunc(sFuncName){
 // 존재하지 않는 함수 체크
 return (typeof this[sFuncName]) == "function";
}

/**
 *
 *
 * @param  sFuncName : 함수명
 * @return boolean
 * @see    smf_HasFunc("test_function");
 */
function smf_BindItemByComponent(comp){
 for(var i=0,size=binds.length; i<size; i++){
  if(binds[i].compid != smf_FullId(comp)) continue;
  if(binds[i].propid != 'value') continue;

  return binds[i];
 }

 return null;
}

/**
 * cmbPivot를 기준으로 combo들을 클리어 한다.
 *
 * @param  cmbPivot 기준이 되는 combo
 * @param  combo와 firstCode >> [cmbDghtCrseScCode, "SEL", cmbMerge, "SEL"]

 * @return void
 * @see    var aryCombo = [cmbDghtCrseScCode, "SEL", cmbMerge, "SEL"];
 *   if(errcd != 0) return;
 *   if(svcid == "code01"){
 *    smf_ClearCombo(cmbDghtCrseScCode, aryCombo);
 *   }else if(svcid == "code02"){
 *    smf_ClearCombo(cmbMerge, aryCombo);
 *   }
 */
function smf_ClearPivotCombo(cmbPivot, aryCombo){
    smf_ClearValueChain(cmbPivot, aryCombo);
}


/**
 * (공통만사용)
 */
function smf_CompByInnerDataset(ds){
    var comp = null;
    //Dataset을 innerdataset으로 사용하는 컴포넌트를 찾는다.
    smf_TravelComponents(this, function (obj, nLvl){
        if(!(obj instanceof Combo || obj instanceof Radio || obj instanceof ListBox)) return;
        var dsInnerDataset = smf_ToDatasetObject(obj.innerdataset);
        if(dsInnerDataset == null) return;
        if(ds != dsInnerDataset) return;
        comp = obj;
    });

    return comp;
}

/**
 * (공통만사용)
 */
function smf_SetFirstCode(ds, sFirstCodeType){
    var sFirstCode = smf_Str(ds.getConstColumn("firstCode"));
 if(isNotNil(sFirstCodeType)) sFirstCode = smf_Str(sFirstCodeType);

    //필터수행
    if(! isNil(ds.getConstColumn("filterExpr"))){
        ds.enableevent = false;
        try{
            var sFilterExpr = ds.getConstColumn("filterExpr");
            ds.filter(sFilterExpr);
        }finally{
            ds.enableevent = true;
        }
    }
    //////////////////////////////////

    if(sFirstCode == "") return false;

    var nAddRow = ds.insertRow(0);

    var sCodeValue = ""; //코드값
    var sDataValue = ""; //

    if(-1 == sFirstCode.indexOf(":")){ //0:SEL, SEL
        sCodeValue = "";
        sDataValue = smf_Str(sFirstCode);
    }else{
        sCodeValue = smf_Str(sFirstCode.split(":")[0]);
        sDataValue = smf_Str(sFirstCode.split(":")[1]);
    }

    var sCodeColumn = "";
    var sDataColumn = "";

    var cmb = smf_CompByInnerDataset(ds); //InnerDataset으로 Component를 얻는다.
    if(cmb == null){
        if(ds.getColID(0) == "_chk"){
            sCodeColumn = ds.getColID(1);
            sDataColumn = ds.getColID(2);
        }else{
            sCodeColumn = ds.getColID(0);
            sDataColumn = ds.getColID(1);
        }
    }else{
        sCodeColumn = cmb.codecolumn;
        sDataColumn = cmb.datacolumn;
    }

    switch(sDataValue){
        case "ALL":
            ds.setColumn(nAddRow, sCodeColumn, sCodeValue);
            ds.setColumn(nAddRow, sDataColumn, "전체");
            break;
        case "SEL":
            ds.setColumn(nAddRow, sCodeColumn, sCodeValue);
            ds.setColumn(nAddRow, sDataColumn, "선택");
            break;
        case "EMP":
            ds.setColumn(nAddRow, sCodeColumn, sCodeValue);
            ds.setColumn(nAddRow, sDataColumn, "");
            break;
  default: return;
    }

    //콤보가 연결되어있는 데이터셋이면
    //
    if(cmb != null){
  var oBindItem = smf_BindItemByComponent(cmb);

  if(oBindItem == null){
   cmb.value = sCodeValue;
  }else{
   var controlBindDs = eval(oBindItem.datasetid);
   if(controlBindDs instanceof Dataset){
    if(controlBindDs.rowcount == 0 || controlBindDs.rowposition == -1){
     cmb.index = 0;
    }else{
     var binddatasetColumnValue = controlBindDs.getColumn(controlBindDs.rowposition, oBindItem.columnid);
     var nFindRow = ds.findRow(sCodeColumn, binddatasetColumnValue);
     if( nFindRow >= 0){
      binddatasetColumnValue = controlBindDs.getColumn(controlBindDs.rowposition, oBindItem.columnid);
      cmb.enableredraw = false;
      cmb.value = undefined;
      cmb.value = binddatasetColumnValue;
      cmb.enableredraw = true;
     }else{
      cmb.value = sCodeValue;
     }
    }
   }
  }
 }

    return true;
}

// 개발중 사용불가
function smf_DghtCrseScCode(id, dsIn, dsDghtCrseScCode, sBindingContorls){
 smf_DService({
  id              : id
    ,url             : "svc_default::/his_cdm_cc00_dqm_001.xp"
    ,inDatasets      : " dsHisCdmCc00M01SVO=" + dsIn.name + ":A"
    ,outDatasets     : " dsDghtCrseScCode=" + dsDghtCrseScCode.name
    ,svctp           : "R"
    ,bindingControls : sBindingContorls
 });
}

function smf_CreateVariable(){
 var ds = smf_GetOrCreateDataset("dsSmVariable");
 return ds;
}

/**
 *
 *
 * @param  jParams :
 * @return boolean
 * @see    smf_VariableDataset("test_function");
 */
function smf_VariableDataset(jParams){
 var ds = dsSmVariable;
 ds.clear();
 ds.addColumn("_chk", "STRING", 255);
 ds.addRow();

 for(var id in jParams){
  var sValue = jParams[id];
  ds.addColumn(id, "String", 255);
  ds.setColumn(0, id, sValue);
 }

 return ds;
}

/**
 *
 *
 * @param  ds, nRow
 * @return boolean
 * @see    smf_HasFunc("test_function");
 */
function smf_CopyRowVariable(dsSource, nRow){
 var ds = dsSmVariable;

 ds.clear();
 for(var i=0,size=dsSource.colcount; i<size; i++){
        var sColumnName = dsSource.getColID(i);
        ds.addColumn(sColumnName, "String", 255);
 }

 ds.addRow();
 ds.copyRow(0, dsSource, nRow);
 return ds;
}

 

/**
 * 객체의 full id 를 리턴한다. >> "div_search.cmb_select"
 *
 * @param  obj 콤포넌트
 * @return full id
 * @see    smf_FullId(obj);
 */
function smf_FullId(obj){
 //activex component는 obj.parent가 없음..
    if(smf_Str(obj) == "[object ActiveX]") return obj.name;

    var sId = "";
    do{
        sId = obj.name + "." +sId;
        obj = obj.parent;
    }while((obj+"") != "[object Form]");

    return sId.substring(0, sId.length-1);
}

/**
 * form을 리턴한다.
 *
 * @param  obj 콤포넌트
 * @return Form Object
 * @see    smf_ParentForm(this); //this는 Div or Tab or Tabpage 등등..
 *         Form에서 smf_ParentForm(this);호출하면 자기자신을 리턴함.

 *         ** 초등학교 참고 **
 *            초등학교는 탭으로 메뉴를 관리하기때문에 Tab id를
 *            "tabElsPrimary"라고 정의해야 원하는 ParentForm을 찾을 수 있다
 *
 */
function smf_ParentForm(obj){
 var aryLinkedParent = [obj];
 var idx = 1;
    while((obj+"") != "[object Form]"){
  if(obj.name == "tabElsPrimary"){
   return aryLinkedParent[idx-2];
  }
        obj = obj.parent;
        aryLinkedParent.push(obj);
        idx++;
    }
    return obj;
}

/**
 * form을 리턴한다.
 *
 * @param  obj 콤포넌트
 * @return Form Object
 * @see    smf_ForceParentForm(this); //this는 Div or Tab or Tabpage 등등..
 *         Form에서 smf_ParentForm(this);호출하면 자기자신을 리턴함.
 */
function smf_ForceParentForm(obj){
 var aryLinkedParent = [obj];
 var idx = 1;
    while((obj+"") != "[object Form]"){
        obj = obj.parent;
        aryLinkedParent.push(obj);
        idx++;
    }
    return obj;
}


/**
 * MainForm을 리턴한다.
 *
 * @param  obj 콤포넌트
 * @return Form Object
 * @see    smf_ForceMainForm(this); //this는 Div or Tab or Tabpage 등등..
 *         MainForm에서 smf_ParentForm(this);호출하면 자기자신을 리턴함.
 */
function smf_ForceMainForm(obj){
 var nLimitCount = 0;
    while((obj+"") != "[object Form]"){
  obj = obj.parent;
  //무한루프의 두려움
  if(100 <= nLimitCount++) break;
    }

    return obj;
}

/**
 * OwnerComponent를 리턴한다.
 *
 * @param  obj 콤포넌트
 * @return Component
 * @see    smf_OwnerComponent(this); //this는 Div or Tab or Tabpage 등등..
 */
function smf_OwnerComponent(obj){
    while((obj+"") != "[object Form]"){
  obj = obj.parent;
  if(isNotNil(obj.url)) return obj;
    }

    return obj;
}

/**
 * pageid을 리턴한다.
 *
 * @param  oForm Form
 * @return pageid
 */
function smf_FormPageId(oForm){
 var sUrl = oForm.getOwnerFrame().formurl;
 var sDelemiter = "";
 var sDelemiterSize;
 if(-1 != sUrl.lastIndexOf("\\")){
  sDelemiter = "\\";
  sDelemiterSize = 1;
 }else{
  sDelemiter = "::";
  sDelemiterSize = 2;
 }
 sUrl = sUrl.substr(sUrl.lastIndexOf(sDelemiter)+sDelemiterSize);
 return sUrl.substr(0, sUrl.length-5);
}

/**
 * menuid를 리턴한다.
 *
 * @param  oForm Form
 * @return MenuId
 */
function smf_FormMenuId(oForm){
 return smf_Str(oForm.pv_menuId);
}


/**
 * 금액을 format  "123,456,789"
 *
 * @param  sMoney 금액
 * @return format화된 금액 문자열
 * @see    smf_FormatMoney(123456789, "원");--> "123,456,789원"
 */
function smf_FormatMoney(sMoney, sSuffix){
    var sMoney = smf_Str(sMoney);

    var sNum1;
    var sNum2;
    if(sMoney.indexOf(".") != -1){
        sNum1 = sMoney.substring(0, sMoney.indexOf("."));
        sNum2 = sMoney.substr(sMoney.indexOf("."));
    }else{
        sNum1 = sMoney;
        sNum2 = "";
    }
    var sResult = "";
    var nLen = sNum1.length;

    var nSeq = 0;
    for(var i=sNum1.length-1; i>=1; i--){
        nSeq++;
        sResult = sNum1.charAt(i) + sResult;
        if((nSeq % 3) == 0){
            sResult =  ',' + sResult;
        }
    }
    sResult = sNum1.charAt(i-1) + sResult + sNum2;

    if(sResult != "" && sSuffix != undefined && sSuffix != null && sSuffix != "") sResult += sSuffix;
    return sResult;
}

/**
 * nRow를 Reset한다
 *
 * @param  ds : Dataset
 * @param  nRow : reset할 row
 * @return nRow
 * @see    smf_ResetRow(dsMain, dsMain.rowposition);
 */
function smf_ResetRow(ds, nRow){
    for(var i=0,size=ds.colcount-ds.getConstCount(); i<size; i++){
        ds.setColumn(nRow, i, ds.getOrgColumn(nRow, i));
    }

    return nRow;
}

/**
 * ds의 모든 row를 리셋한다.
 *
 * @param  ds : Dataset
 * @param  fnc : reest할지의 callback함수 return boolean으로 reset여부를 결정.
 * @return nRow
 * @see    //check 안된 row는 모두 리셋
           smf_ResetRows(dsMain, function(ds, nRow){
         return "1" != ds.getColumn(i, "_chk");
           });
 */
function smf_ResetRows(ds, fnc){
 for(var i=0,size=ds.rowcount; i<size; i++){
  if(isNotNil(fnc)){
   if(fnc(ds, i) != true) continue;
  }

  smf_ResetRow(ds, i);
 }
}

/**
 * 상위컴포넌트의 값이 변경되면 하위컴포넌트들의 값을 연쇄적으로 Clear한다.
 *
 * @param  obj : Component
 * @param  aryCop : Array로 구성되어있는 연결되어있는 컴포넌트
 * @return nRow
 * @see     function cmb_onitemchanged(obj, e){
                var aryComp = [div_insert.edt_otrSchulCode,
                                div_insert.cmb_OtrSchulDghtCrseCode,
                                div_insert.cmb_OrdScCode,
                                div_insert.cmb_GradeCode,
                                div_insert.cmb_DddepCode,
                                [div_insert.cmb_ClassCode], // []로 감싸면 innerdataset이 clear되지 않는다.
                                div_insert.ta_atnlcStdntNote
                                ];
                //obj의 하위 컴포넌트들을 clear시킨다.
                smf_ClearValueChain(obj, aryComp);

                if(obj == null) return;

                //indataset 파라메터 생성
                smf_VariableDataset({
                    schulCode      : dsMain.getColumn(0, "schulCode")
                   ,ay             : dsMain.getColumn(0, "ay")
                   ,dghtCrseScCode : dsMain.getColumn(0, "otrSchulDghtCrseCode")
                   ,ordScCode      : dsMain.getColumn(0, "otrOrdScCode")
                   ,grade          : dsMain.getColumn(0, "otrGrade")
                   ,dddepCode      : dsMain.getColumn(0, "otrDddepCode")
                });

                switch(obj.name){
                    case "edt_otrSchulCode":
                        swf_DghtCrseScCode('codeDghtCrseScCode', dsSmVariable, dsDghtCrseScCode);
                        break;
                    case "cmb_OtrSchulDghtCrseCode":
                        swf_OrdScCode("codeOrdScCode", dsSmVariable, dsOrdScCode); //계열
                        break;
                    case "cmb_OrdScCode":
                        swf_Grade("codeGrade", dsSmVariable, dsGrade); //학년
                        break;
                    case "cmb_GradeCode":
                        smf_DddepCode("codeDddepCode", dsSmVariable, dsDddepCode); //학년
                        break;
                    case "cmb_DddepCode":
                        smf_ClassCode("codeClassCode", dsSmVariable, dsClassCode); //반
                        break;
                }
            } */
function smf_ClearValueChain(objPivot, aryComponent){
 ////////////////////검증시작////////////////////
 var sErrorMessage = "";
 for(var i=0,size=aryComponent.length; i<size; i++){
  var cbm = aryComponent[i];
  if(cbm == null) continue;

  if(cbm instanceof Array){
            cbm = cbm[0];
  }

  if(cbm instanceof Combo || cbm instanceof Radio){
            var ds = smf_ToDatasetObject(cbm.innerdataset);
            if((! (ds instanceof Dataset)) || (ds == undefined) || (ds == null)){
                sErrorMessage += format("aryComponent의 {0}/{1}번째 항목은 Combo {2}의 innerdataset의 데이터셋을 잘못됬습니다.\n", [i+1, aryComponent.length, cbm.name]);
            }

            var sFirstCode = smf_Str(ds.getConstColumn("firstCode"));
            if(notIn(sFirstCode, ['SEL', 'ALL', 'EMP', ''])){
                sErrorMessage += format("aryComponent의 {0}/{1}번째 항목은 은\n(ALL,SEL,EMP, '')←(전체,선택,비어있게,'') 중 하나를 선택하십시오."
                , [i+1, aryComponent.length]);
            }
  }else{

  }
 }

 if(! isNil(sErrorMessage)){
  alert("smf_ClearValueChain설정에러 \n" + sErrorMessage);
  return;
 }

 ////////////////////검증종료////////////////////
 var nObjIndex = -1;
 for(var i=0,size=aryComponent.length; i<size; i++){
        var comp = aryComponent[i];
        if(comp instanceof Array) comp = comp[0];
        if(objPivot == comp){
            nObjIndex = i;
            break;
        }
 }

 for(var i=nObjIndex+1,size=aryComponent.length; i<size; i++){
        var comp = aryComponent[i];
        if(comp instanceof Array){
            comp = comp[0];
            comp.value = "";
        }else if(comp instanceof Combo || comp instanceof Radio){
            var ds = smf_ToDatasetObject(comp.innerdataset);
            ds.clearData();
            smf_SetFirstCode(ds, comp);
        }else if(comp instanceof Static){
            comp.text = "";
        }else{
            comp.value = "";
        }
 }
}

/**
 * fnc함수의 명을 리턴
 *
 * @param  fnc : function
 * @return String
 * @see    var sFuncName = smf_FuncName(lfn_MyFunction);
*/
function smf_FuncName(fnc){
    if(fnc == undefined || fnc == null) return "";
    if((typeof fnc) != "function") return "";

    var sName = fnc.toString().substr(9);
    return sName.substring(0, sName.indexOf("("));
}

/**
 * Grid의 체크박스를 활성화/비활성화 한다.
 *
 * @param  grid : Grid
 * @param  nCell : Grid Cell Index
 * @param  fncRender : 활성화 비활성화 할 callback
 * @return void
 * @see    smf_UpdateRednerGridCheckbox(Grid00, 0, lfn_RenderGridCheckbox);
*/
function smf_UpdateRednerGridCheckbox(grid, nCell, fncRender){
    var sGridId = smf_FullId(grid);
    var sFormat = "expr:smf_RednerGridCheckbox(eval('" + eval(grid.binddataset).name + "'), '{0}', currow, " + smf_FuncName(fncRender) + ")";

    grid.setCellProperty("body", nCell, "displaytype", smf_Format(sFormat, ['displaytype']));
    grid.setCellProperty("body", nCell, "edittype", smf_Format(sFormat, ['edittype']));
    grid.setCellProperty("body", nCell, "controlbackground", smf_Format(sFormat, ['controlbackground']));
    grid.setCellProperty("body", nCell, "cursor", smf_Format(sFormat, ['cursor']));
}

/**
 * (공통만사용)Grid의 체크박스를 하는 function
*/
function smf_RednerGridCheckbox(ds, sPropName, nRow, fncRender){
    var bChecked = fncRender(ds, nRow);

    switch(sPropName){
        case "displaytype": return "checkbox";
        case "edittype":
            if(bChecked == true){
                return "checkbox";
            }else if(bChecked == false){
                return "";
            }
            break;
        case "controlbackground":
            if(bChecked == true){
                return "";
            }else if(bChecked == false){
                return "#c4c0cdff";
            }
            break;

        case "cursor":
            if(bChecked == true){
                return "";
            }else if(bChecked == false){
                return "arrow";
            }
            break;
    }

    return "";
}

/**
 * 입력중인 값을 데이터셋에 Apply시킨다.
 *
 * @return void
 * @see
*/
function smf_ApplyData(){
    // Create Object
    var objEdit = new Edit("_Edit_smf_ApplyData", -1000, -1000, 1, 1);
    try{
        // Add Object to Parent Form
        this.addChild(objEdit.name, objEdit);

   var mainForm = smf_ForceMainForm(this);
   var focusedComponent = mainForm.getFocus();

        // Show Object
  objEdit.accessibility.enable = false;
        objEdit.show();
        objEdit.setFocus(); // 포커스를 줘서 입력중이던 값이 강제로 Dataset에 적용되도록 유도.

   if(isNotNil(focusedComponent)){
   //원래 가지고 있는 포커스가 가지고 있는 컴포넌트에 다시 포커스를 준다. 그때
   //kill,setFocus이벤트가 발생하지 않게 enableevent를 비활성화, 웹접근성도 비활성화
   //시킨후 다시 포커스를 준다.
   var bEnableevent = focusedComponent.enableevent;
   if(isNotNil(focusedComponent.accessibility)){
    var bAccessibilityEnable = focusedComponent.accessibility.enable;

    focusedComponent.enableevent = false;
    focusedComponent.accessibility.enable = false;
    try{
     focusedComponent.setFocus();
    }finally{
     focusedComponent.enableevent = bEnableevent;
     focusedComponent.accessibility.enable = bAccessibilityEnable;
    }
   }
   }
        // Remove Object form Parent Form
        this.removeChild(objEdit.name);
    }finally{
        // Destroy Object
        objEdit.destroy();
    }
}

/**
 * Row를 복사한다.
 *
 * @return boolean
 * @see
*/
function smf_CopyRows(dsSource, dsDesc, fncCondition){
    for(var i=0,size=dsSource.rowcount; i<size; i++){
        if( true != fncCondition(i)) continue;
        dsDesc.copyRow(dsDesc.addRow(), dsSource, i);
    }
}

/**
 * 해당 서비스가 호출되었는지
 *
 * @return boolean
 * @see hasBeenService("selectMain");
*/
function smf_HasBeenService(sServiceId){
    return (new RequestService(this, sServiceId)).hasBeenService();
}

/**
 * sServiceId 의 프로퍼티명
 *
 * @return boolean
 * @see hasBeenService("selectMain");
*/
function smf_GetServiceProperty(sServiceId, sPropertyName){
    var nRow = dsService.findRow('id', sServiceId);
    if(nRow == -1) return "";

    return smf_Str(dsService.getColumn(nRow, sPropertyName));
}

/**
 * sServiceId 의 프로퍼티명
 *
 * @return boolean
 * @see hasBeenService("selectMain");
*/
function smf_SetServiceProperty(sServiceId, sPropertyName, sValue){
    var nRow = dsService.findRow('id', sServiceId);
    if(nRow == -1) return false;

    return dsService.setColumn(nRow, sPropertyName, sValue);
}

/**
 * oComposite의 Combo, List, Radio의 index를 0으로 변경한다.(index 가 -1인 것만)
 * service call후 callback에서 사용함.
 *
 * @return void
 * @see smf_ResetComboIndex(div_manage)
*/
function smf_ResetComboIndex(oComposite){
    for(var i=0,size=oComposite.components.length; i<size; i++){
        var oComponent = oComposite.components[i];

        if(! (oComponent instanceof Combo ||
              oComponent instanceof List ||
              oComponent instanceof Radio)) continue;
        //index
        if(oComponent.index == -1){
            oComponent.value = "";
        }
    }
}


function smf_SortByTabOrder(oComposite, fncValid, bIgnoreTravel){
    if(bIgnoreTravel == undefined) bIgnoreTravel = false;
    var aryUnsorted = [];
    var _fncValid = fncValid;

 if(bIgnoreTravel){
  for(var i=0,size=oComposite.components.length; i<size; i++){
   var comp = oComposite.components[i];
   comp._sort = (1) + ((comp.taborder+1) / Math.pow(10, 1));
   aryUnsorted.push(comp);
  }
 }else{
  smf_TravelComponents(oComposite, function(comp, nLvl, owner){
   if(comp.taborder == undefined) return;
   if(_fncValid != undefined && _fncValid != null){
    if(_fncValid(comp) == false) return;
   }
   if(nLvl == 1){
    owner._sort = 1;
   }

   comp._sort = (owner._sort) + ((comp.taborder+1) / Math.pow(10, nLvl));
   aryUnsorted.push(comp);
  }, bIgnoreTravel);
 }

    smf_SortArray(aryUnsorted, function(a, b){
        return a._sort > b._sort;
    });

    for(var i=0,size=aryUnsorted.length; i<size; i++){
        aryUnsorted[i]._sort = i+1;
    }

    return aryUnsorted;
}

function smf_SortArray(a, fncCompare){
    for (var i = 1; i < a.length; i++) {
        var j = i;
        var tmp = a[i];

        while ((j > 0) && fncCompare(a[j - 1], tmp)) {
            a[j] = a[j - 1];
            j--;
        }

        a[j] = tmp;
    }

}

/**
 * check된 row의 rowtype을 delete로 변경한다.
 *
 * @return void
 * @see smf_CheckToDeleteType(dsMain);
*/
function smf_CheckToDeleteType(ds){
    ds.updatecontrol = false;
    try{
        for(var i=0,size=ds.rowcount; i<size; i++){
            if(ds.getColumn(i, "_chk") != "1") continue;

            ds.setRowType(i, Dataset.ROWTYPE_DELETE);
        }
    }finally{
        ds.updatecontrol = true;
    }
}


/**
 * 공통코드를 복사한다.
 * 필요파라메터 : atptOfcdcOrgCode
 *
 * @param  dsSource : source Dataset
 * @param  dsDest   : 대상 Dataset
 * @return void
 * @see    smf_CopyCode(dsSourceOfDataset, dsDestOfDataset);
 */
function smf_CopyCode(dsSource, dsDest){
    dsDest.clearData();

    for(var i=0,size=dsSource.rowcount; i<size; i++){
        dsDest.addRow();
        dsDest.copyRow(i, dsSource, i);
    }

 if(! isNil(dsSource.getConstColumn('firstCode'))){
  dsDest.deleteRow(0);
 }

    dsDest.applyChange();
    smf_SetFirstCode(dsDest);
}

/**
 * 조직콤보
 * 필요파라메터 : atptOfcdcOrgCode
 *
 * @param  sServiceId 서비스아이디
 * @param  dsIn parameter dataset
 * @param  dsOut result dataset
 * @return boolean
 * @see    smf_OrgCombo("codeOrg", dsSmVariable, dsOrg);
 */
function smf_OrgCombo1(sServiceId, dsIn, dsOut, jOption){
    if(isNil(jOption)) jOption = {};
 smf_DService({
   id              : sServiceId
     ,url             : "svc_neis::/sym_ocm_oc00_001.xp"
     ,inDatasets      : "dsSymOcmOc00M00SVO=" + dsIn.name
     ,outDatasets     : dsOut.name + "=dsSymOcmOc00M00SVO_orgList1"
     ,svctp           : "R"
     ,bindingControls : jOption.bindingControls
     ,asynctp         : jOption.asynctp
 });
}

/**
 * 조직콤보
 * 필요파라메터 : atptOfcdcOrgCode
 *
 * @param  sServiceId 서비스아이디
 * @param  dsIn parameter dataset
 * @param  dsOut result dataset
 * @return boolean
 * @see    smf_OrgCombo("codeOrdGradeDddep", dsSearch, dsOrdGradeDddep);
 */
function smf_OrgCombo2(sServiceId, dsIn, dsOut, jOption){
    if(isNil(jOption)) jOption = {};
 smf_DService({
   id              : sServiceId
     ,url             : "svc_neis::/sym_ocm_oc00_002.xp"
     ,inDatasets      : "dsSymOcmOc00M00SVO=" + dsIn.name
     ,outDatasets     : dsOut.name + "=dsSymOcmOc00M00SVO_orgList2"
     ,svctp           : "R"
     ,bindingControls : jOption.bindingControls
     ,asynctp         : jOption.asynctp
 });
}

/**
 * Tree 확장/축소한다.
 *
 * @param  grd : Grid
 * @param  ds : Dataset
 * @param  nRow : Row
 * @param  sCheckColumn : Checkbox로 쓸 컬럼아이디
 * @param  fnc :
 * @return boolean
 * @see    smf_OrgCombo("codeOrdGradeDddep", dsSearch, dsOrdGradeDddep);
 */
function smf_ExpendTree(grd, ds, nRow, sCheckColumn, fnc){
    var sSelectedValue = ds.getColumn(nRow, sCheckColumn);

    smf_HandleTreeChilds(grd, ds, nRow, sCheckColumn, sSelectedValue, fnc);
    smf_HandleTreeParents(grd, ds, nRow, sCheckColumn, sSelectedValue, fnc);
}

/**
 * Tree Child Handling
 *
 * @param  grd : Grid
 * @param  ds : Dataset
 * @param  nPRow : Parent Row
 * @param  fnc : function
 * @return boolean
 * @see    smf_OrgCombo("codeOrdGradeDddep", dsSearch, dsOrdGradeDddep);
 */
function smf_HandleTreeChilds(grd, ds, nPRow, sCheckColumn, sSelectedValue, fnc){
    if(fnc == undefined) fnc = smf_DefaultExpendTreeCallback;

 fnc(grd, ds, nPRow, nPRow, sCheckColumn, sSelectedValue);

    for(var i=0,size=grd.getTreeChildCount(nPRow); i<size; i++){
  var nRow = grd.getTreeChildRow(nPRow, i);
  fnc(grd, ds, nPRow, nRow, sCheckColumn, sSelectedValue);
  smf_HandleTreeChilds(grd, ds, nRow, sCheckColumn, sSelectedValue, fnc);
    }
}

/**
 * Tree Parent Handling
 *
 * @param  grd : Grid
 * @param  ds : Dataset
 * @param  nPRow : Parent Row
 * @param  fnc : function
 * @return boolean
 * @see    smf_OrgCombo("codeOrdGradeDddep", dsSearch, dsOrdGradeDddep);
 */
function smf_HandleTreeParents(grd, ds, nChildRow, sCheckColumn, sSelectedValue, fnc){
    if(fnc == undefined) fnc = smf_DefaultExpendTreeCallback;

    var sSelectedValue = ds.getColumn(nChildRow, sCheckColumn);
    if(sSelectedValue != "1") return;

    var nParentRow = grd.getTreeParentRow(nChildRow);
    if(nParentRow == -1 ) return;

 fnc(grd, ds, nParentRow, nParentRow, sCheckColumn, sSelectedValue);
 smf_HandleTreeParents(grd, ds, nParentRow, sCheckColumn, sSelectedValue, fnc);
}

/**
 *
 *
 * @param  grd : Grid
 * @param  ds : Dataset
 * @param  nPRow : Parent Row
 * @param  fnc : function
 * @return boolean
 * @see    smf_OrgCombo("codeOrdGradeDddep", dsSearch, dsOrdGradeDddep);
 */
function smf_DefaultExpendTreeCallback(grd, ds, nPRow, nRow, sCheckColumn, sSelectedValue){
    ds.setColumn(nRow, sCheckColumn, sSelectedValue);
    grd.setTreeStatus(grd.getTreeRow(nRow), "1" == sSelectedValue);
}


/**
 * 학교코드를 넘긴다.
 *
 * @return 학교코드
 * @see
 */
function smf_SubSysCode(){
 switch(utlf_getSession("schulKndScCode")){
  case "01": return "";//유치원
  case "02": return "ELS";//초등학교
  case "03": return "MIS";//일반중학교
  case "04": return "HIS";//고등학교
  case "05": return "SPS";//특수학교
  case "12": return "MIS";//"CMS";//평생중학교
  case "16": return "MIS";//"CMS";//평생중학교
  case "13": return "HIS";//"CHS";//평생고등학교
  case "17": return "HIS";//"CHS";//평생고등학교
 }
}

/**
 * 조회조건 권한타입(system,unit)인지
 *
 * @return string("system", "unit")
 * @see    smf_AuthSystemUnitType();
 */
function smf_AuthSystemUnitType(){
 var ds = smf_GetOrCreateDataset("_dsAuthReturn");
 return smf_Str(ds.getColumn(0, "authSystemUnitType"));
}


/**
 * (공통만 사용)
 *
 */
function smf_GetOrAddServiceId(form, sServiceId){
 var nRow = form.dsService.findRow("id", sServiceId);
 
 if(nRow == -1){
  nRow = form.dsService.addRow();
 }else{
 
 }
 
 return nRow;
}

/**
 * (공통만사용)권한 처리에서 사용하는 callback함수
 *
 * @param  sServiceId : callback함수에서 받을 서비스명
 * @param  aryDataset : 공통코드로 넘길 Array 데이터셋
 * @param  bSync      : 비동기로 넘길지 default false
 * @return void
 * @see    smf_AuthList('auth', [dsAy, dsOrdScCode, dsClassCode]);
 */
function smf_AuthCallback(sId, nReason, sMessage, jParams){
    if(nReason == -1 ) return;
 var _dsAuthReturn = objects["_dsAuthReturn" + sId];
 if(_dsAuthReturn == null) return;
 var nRow = dsService.findRow('id', sId);
 var service = (new RequestService(this, sId, jParams));
 var aryInDatasets = service.toArrayByInDatasets();
 var aryOutDatasets = service.toArrayByOutDatasets();
  
 for(var i=0,size=aryOutDatasets.length; i<size; i++){
  var ds = aryOutDatasets[i];
  var combo = smf_CompByInnerDataset(ds);
  if(!(combo instanceof Combo)) continue;

  
  //0번째 값으로 세팅한다.
  combo.value = ds.getColumn(0, combo.codecolumn);
   }
  
   //직접권한접근이 허용된 경우
   //년도Combo를 Edit로 변경해준다.
   if("Y" == _dsAuthReturn.getColumn(0, "directAuthYn")){
    for(var i=0,size=aryOutDatasets.length; i<size; i++){
    var combo = smf_CompByInnerDataset(aryOutDatasets[i]);
   var cmbType = aryOutDatasets[i].getConstColumn('cmbType');
   var _check = cmbType.toUpperCase().lastIndexOf('AY');
   if(_check != -1 && combo.visible == true){
    var _edit = new Edit('edit' + cmbType);
    combo.parent.addChild(_edit.name, _edit);
    _edit.position = combo.position;
    _edit.value = aryOutDatasets[i].getColumn(0,0);
    _edit.show();
    combo.visible = false;
    combo.innerdataset = null;
   }
   }
  }
}

/**
 * 권한 처리
 *
 * @param  sServiceId : callback함수에서 받을 서비스명
 * @param  aryDataset : 공통코드로 넘길 Array 데이터셋
 * @param  bSync      : 비동기로 넘길지 default false
 * @return void
 * @see    smf_AuthList('auth', [dsAy, dsOrdScCode, dsClassCode]);
 */
function smf_AuthList(obj, sServiceId, aryDataset, bSync, jParams){
 if(isNil(jParams)) jParams = {};
 bSync = bSync == true;

 ////////////////////////검증 //////////////////////////////

 sServiceId = smf_Str(sServiceId);

 //service id는 고정
//  if(sServiceId.indexOf("auth") != 0){
//   trace(format("sServiceId '{0}'는 잘못된 아이디입니다.\nauth로 시작해야합니다.", [sServiceId]));
//   alert(format("sServiceId '{0}'는 잘못된 아이디입니다.\nauth로 시작해야합니다.", [sServiceId]));
//   return false;
//  }

 if(sServiceId.indexOf("authOnload") != 0 && sServiceId.indexOf("authRowChange") != 0){
  trace(format("sServiceId '{0}'는 잘못된 아이디입니다.\nauthOnload나 authRowChange로 시작해야합니다.", [sServiceId]));
  alert(format("sServiceId '{0}'는 잘못된 아이디입니다.\nauthOnload나 authRowChange로 시작해야합니다.", [sServiceId]));
  return false;
 }

 if(! (aryDataset instanceof Array)){
  trace("파라메터aryDataset는 Array이어야 합니다");
  alert("파라메터aryDataset는 Array이어야 합니다");
  return false;
 }

 //todo 검증 detail
 ////////////////////////검증끝//////////////////////////////

 //_dsAuthCodeList Dataset생성
 var _dsAuthCodeList = smf_GetOrCreateDataset('_dsAuthCodeList' + sServiceId);
 _dsAuthCodeList.clearData();

 //_dsAuthReturn Dataset생성
 var _dsAuthReturn = smf_GetOrCreateDataset('_dsAuthReturn' + sServiceId);
 _dsAuthReturn.clearData();

 //컬럼생성
 _dsAuthCodeList.addColumn("userId", "STRING",256);
 _dsAuthCodeList.addColumn("sysCode", "STRING",256);
 _dsAuthCodeList.addColumn("subSysCode", "STRING",256);
 _dsAuthCodeList.addColumn("routingSubSysCode", "STRING",256);
 _dsAuthCodeList.addColumn("pageId", "STRING",256);
 _dsAuthCodeList.addColumn("schulCode", "STRING",256);
 _dsAuthCodeList.addColumn("customRefValues", "STRING",256);
 _dsAuthCodeList.addColumn("necessaryComboType", "STRING",256);
 _dsAuthCodeList.addColumn("necessaryComboAuth", "STRING",256);
 _dsAuthCodeList.addColumn("necessaryTenseType", "STRING",256);

 //row 추가
 _dsAuthCodeList.addRow();

 var customRefValues = "";
 for(var sColumnName in jParams){
  customRefValues += format("{0}={1},", [sColumnName, nvl(jParams[sColumnName], '-')]);
 }
 //customRefValues = customRefValues.substr(1);
 _dsAuthCodeList.setColumn(0,"customRefValues", customRefValues);
 _dsAuthCodeList.setColumn(0,"userId",utlf_getSession("userid"));
 _dsAuthCodeList.setColumn(0,"pageId",smf_FormPageId(smf_ForceMainForm(obj)));
 _dsAuthCodeList.setColumn(0,"sysCode", "SW");
 _dsAuthCodeList.setColumn(0,"routingSubSysCode", smf_SubSysCode());
 if(isNotNil(jParams.subSysCode)){
  _dsAuthCodeList.setColumn(0,"subSysCode", jParams.subSysCode);
 }else{
  _dsAuthCodeList.setColumn(0,"subSysCode", smf_SubSysCode());
 }
 _dsAuthCodeList.setColumn(0,"schulCode", utlf_getSession("insttCode"));

 var  necessaryComboType = "";
 var  necessaryComboAuth = "";
 var  necessaryTenseType = "";
 
 
 var sSchulKndScCode = utlf_getSession("schulKndScCode");
 for(var i=0,size=aryDataset.length; i<size; i++){
  var ds = aryDataset[i];
  
  if(In(sSchulKndScCode, ["12", "16", "13", "17"]) && ds.getConstColumn("cmbTense") == "ALL"){
   //평생고,중은 ALL이 없음
   ds.setConstColumn("cmbTense", "PRESENT");
  }

  necessaryComboType += ds.getConstColumn("cmbType") + ",";
  necessaryComboAuth += (isNotNil(ds.getConstColumn("cmbAuth")) ? ds.getConstColumn("cmbAuth") : "YES") + ",";
  necessaryTenseType += (isNotNil(ds.getConstColumn("cmbTense")) ? ds.getConstColumn("cmbTense") : "PRESENT") + ",";
 }
 _dsAuthCodeList.setColumn(0,"necessaryComboType",necessaryComboType);
 _dsAuthCodeList.setColumn(0,"necessaryComboAuth",necessaryComboAuth);
 _dsAuthCodeList.setColumn(0,"necessaryTenseType",necessaryTenseType);

 var nSrvRow = smf_GetOrAddServiceId(this, sServiceId);

 var sOutDatasets = "";
 for(var i=0,size=aryDataset.length; i<size; i++){
  var ds = aryDataset[i];
  sOutDatasets += ds.name + "="+"dsSymAumSa01M00SVO_"+ ds.getConstColumn("cmbType") + "List ";
 }

 var sBindingCtrls = "";
 for(var i=0,size=aryDataset.length; i<size; i++){
  var ds = aryDataset[i];
  if(isNil(ds.getConstColumn("firstCode"))) continue;
  sBindingCtrls += " " + ds.name + "=" + ds.getConstColumn("firstCode");
 }

 dsService.setColumn(nSrvRow, "id",sServiceId);
 dsService.setColumn(nSrvRow, "inDatasets","dsSymAumSa01M00SVO=_dsAuthCodeList" + sServiceId);
 dsService.setColumn(nSrvRow, "outDatasets", sOutDatasets.trim() + " _dsAuthReturn" + sServiceId + "=dsSymAumSa01M00SVO");

 dsService.setColumn(nSrvRow, "package", "edusys.cm.sym.aum.sa.svc");
 dsService.setColumn(nSrvRow, "vo", "SymAumSa01M00SVO");
 dsService.setColumn(nSrvRow, "service", "SymAumSa01M00SVC");
 dsService.setColumn(nSrvRow, "method", "authorization");
 dsService.setColumn(nSrvRow, "svctp", "");
 dsService.setColumn(nSrvRow, "bindingControls", sBindingCtrls.trim());
 dsService.setColumn(nSrvRow, "bizMessage", "");
 dsService.setColumn(nSrvRow, "callback", "lfn_trans_callback");

 //form onload에서만 callback사용
 if(sServiceId.indexOf("authOnload") == 0){
  dsService.setColumn(nSrvRow, "innerCallback", "smf_AuthCallback");
 }

 dsService.setColumn(nSrvRow, "url", "svc_neis::/sym_aum_sa00_001.xp");

 dsService.setColumn(nSrvRow, "state", "");
 dsService.setColumn(nSrvRow, "reason", "");
 dsService.setColumn(nSrvRow, "message", "");
 dsService.setColumn(nSrvRow, "asynctp", iif(bSync, "false", "true"));
 dsService.setColumn(nSrvRow, "bintp", "");
 dsService.setColumn(nSrvRow, "comtp", "");

 //dsService를 통해 호출 한다.
 smf_Service(sServiceId);
}

 

/**
 * 권한 처리
 * @param  sServiceId : callback함수에서 받을 서비스명
 * @param  aryDataset : 공통코드로 넘길 Array 데이터셋
 * @param  bSync      : 비동기로 넘길지 default false
 * @return void
 * @see    smf_AuthChain('authChain', obj,
       [dsOrgAy,dsOrgOrdScCode,dsOrgGrade,dsOrgClassCode], //전체 콤보순서에 따른 dataset들
       [cmbOrgAy.value,cmbOrgOrdScCode.value,cmbOrgGrade.value,cmbOrgClassCode.value] //데이터셋에 매핑되어 있는 콤보의value
       );

*/
function smf_AuthChain(sServiceId, obj, aryDataset, aryCmb, bSync, jParams){
 if(isNil(jParams)) jParams = {};
 bSync = bSync == true;

 sServiceId = smf_Str(sServiceId);
 ////////////////////////검증 //////////////////////////////
 if(sServiceId.indexOf("auth") != 0){
  trace(format("sServiceId '{0}'는 잘못된 아이디입니다.\nauth로 시작해야합니다.", [sServiceId]));
  alert(format("sServiceId '{0}'는 잘못된 아이디입니다.\nauth로 시작해야합니다.", [sServiceId]));
  return false;
 }

 if(isNil(obj)){
  trace("obj를 입력하세요.");
  alert("obj를 입력하세요.");
  return false;
 }

 /* aryDataset체크 */
 if(! (aryDataset instanceof Array)){
  trace("파라메터aryDataset는 Array이어야 합니다");
  alert("파라메터aryDataset는 Array이어야 합니다");
  return false;
 }
 /* refValues 체크 */
 if(! (aryCmb instanceof Array)){
  trace("파라메터aryCmb는 Array이어야 합니다");
  alert("파라메터aryCmb는 Array이어야 합니다");
  return false;
 }

 //입력한 애들이 실제 존재하는 데이터 셋들인가
 //aryDataset.
 for(var i=0; i < aryDataset.length; i++){
  if(!(aryDataset[i] instanceof Dataset)){
   alert(format("함수의 3번째 인자 aryDataset[{0}]번째 인자가 Dataset 객체이어야 합니다.",[i]));
   return false;
  }
 }

 if(aryDataset.length != aryCmb.length){
  alert("지정한 Dataset과 Combo 값들의의 갯수가 다릅니다.");
  return false;
 }
 ////////////////////////검증끝//////////////////////////////

 //TODO: 페이지에서 정보를 가져오도록 고쳐야 함 + validation 처리


 //현재 콤보의 위치 파악
 var cmbPositionCnt = 0;
 for(var i=0; i < aryDataset.length; i++){
  if((smf_ToDatasetObject(obj.innerdataset).name ==  aryDataset[i].name)){
   cmbPositionCnt = i;
   break;
  }
 }
 
 


 //마지막 콤보박스면 서버로 auth요청을 할필요가 없다.
 if(cmbPositionCnt == (aryDataset.length-1)) return;

 /*cmbPositionCnt기준으로 이전 콤보의 값들을 input으로 가져가고
   cmbPositionCnt기준으로 이후 콤보들이 서비스의 out으로 채워질 대상들이다.*/
 //in
 var refValues = "";
 for(var i=0, size = cmbPositionCnt + 1; i < size; i++){
  var cmbType = aryDataset[i].getConstColumn('cmbType');
  var result = cmbType.toUpperCase().lastIndexOf('AY');
  var combo = smf_CompByInnerDataset(aryDataset[i]);
  //cmbType이 XXXAy인 경우
  if(combo == null && result != -1){
   var edit = eval('obj.parent.edit'+cmbType + '.value');
   refValues  += aryDataset[i].getConstColumn("cmbType") + "=" + nvl(edit, '-') +  (i==size-1?"":",");
  }else{
   refValues  += (aryDataset[i].getConstColumn("cmbType") + "="+ nvl(aryCmb[i], '-')) + (i==size-1?"":",");
  }
 }

 var _dsAuthCodeList = smf_GetOrCreateDataset("_dsAuthCodeList" + sServiceId);
 _dsAuthCodeList.clear();
 _dsAuthCodeList.addColumn("userId", "STRING",256);
 _dsAuthCodeList.addColumn("sysCode", "STRING",256);
 _dsAuthCodeList.addColumn("subSysCode", "STRING",256);
 _dsAuthCodeList.addColumn("routingSubSysCode", "STRING",256);
 _dsAuthCodeList.addColumn("pageId", "STRING",256);
 _dsAuthCodeList.addColumn("refValues", "STRING",256);
 _dsAuthCodeList.addColumn("schulCode", "STRING",256);
 _dsAuthCodeList.addColumn("necessaryComboType", "STRING",256);
 _dsAuthCodeList.addColumn("necessaryComboAuth", "STRING",256);
 _dsAuthCodeList.addColumn("necessaryTenseType", "STRING",256);
 _dsAuthCodeList.addColumn("customRefValues", "STRING",256);


 //row 추가
 _dsAuthCodeList.addRow();


 var customRefValues = "";
 
 //년도Dataset에 ayAuthType을 만들어놓으면 ayAuthType을 알수 있다.
 //년도Dataset에 cmbTense를 NO_ITRT나 NO_SCA_ITRT로 했을경우 반드시 ayAuthType컬럼이 있어야 한다.
 if(-1 != smf_Str(aryDataset[0].getConstColumn('cmbType')).indexOf("Ay")){
  var ayAuthType = smf_Str(aryDataset[0].lookup("ay", aryCmb[0], "ayAuthType"));
  if(isNotNil(ayAuthType)){
   customRefValues += format("{0}={1},", ['ayAuthType', ayAuthType]);
  }
 }

 for(var sColumnName in jParams){
  customRefValues += format("{0}={1},", [sColumnName, jParams[sColumnName]]);
 }
 
 

 _dsAuthCodeList.setColumn(0,"customRefValues", customRefValues);
 _dsAuthCodeList.setColumn(0,"userId", utlf_getSession("userid"));
 _dsAuthCodeList.setColumn(0,"pageId",smf_FormPageId(smf_ForceMainForm(obj)));
 _dsAuthCodeList.setColumn(0,"sysCode","SW");
 _dsAuthCodeList.setColumn(0,"routingSubSysCode", smf_SubSysCode());
 if(isNotNil(jParams.subSysCode)){
  _dsAuthCodeList.setColumn(0,"subSysCode", jParams.subSysCode);
 }else{
  _dsAuthCodeList.setColumn(0,"subSysCode", smf_SubSysCode());
 }
 _dsAuthCodeList.setColumn(0,"schulCode", utlf_getSession("insttCode"));
 _dsAuthCodeList.setColumn(0,"refValues",refValues);

 var  necessaryComboType = "";
 var  necessaryComboAuth = "";
 var  necessaryTenseType = "";

 var sSchulKndScCode = utlf_getSession("schulKndScCode");
 for(var i=cmbPositionCnt+1, size=aryDataset.length; i<size; i++){
  var ds = aryDataset[i];
  
  if(In(sSchulKndScCode, ["12", "16", "13", "17"]) && ds.getConstColumn("cmbTense") == "ALL"){
   alert(ds.name + '');
   //평생고,중은 ALL이 없음
   ds.setConstColumn("cmbTense", "PRESENT");
  }

  necessaryComboType += ds.getConstColumn("cmbType") + (i==size-1?"":",");
  necessaryComboAuth += (isNotNil(ds.getConstColumn("cmbAuth")) ? ds.getConstColumn("cmbAuth") : "YES") + (i==size-1?"":",");
  necessaryTenseType += (isNotNil(ds.getConstColumn("cmbTense")) ? ds.getConstColumn("cmbTense") : "PRESENT") + (i==size-1?"":",");
 }
 _dsAuthCodeList.setColumn(0,"necessaryComboType",necessaryComboType);
 _dsAuthCodeList.setColumn(0,"necessaryComboAuth",necessaryComboAuth);
 _dsAuthCodeList.setColumn(0,"necessaryTenseType",necessaryTenseType);

 var nSrvRow = smf_GetOrAddServiceId(this, sServiceId);

 var _dsAuthReturn = smf_GetOrCreateDataset("_dsAuthReturn" + sServiceId);
 var sOutDatasets = "_dsAuthReturn" + sServiceId + "=dsSymAumSa01M00SVO ";
 for(var i=cmbPositionCnt+1; i<aryDataset.length; i++){
  var ds = aryDataset[i];
  sOutDatasets += ds.name + "="+"dsSymAumSa01M00SVO_"+ ds.getConstColumn("cmbType") + "List ";
 }


 var sBindingCtrls = "";
 for(var i=cmbPositionCnt; i<aryDataset.length; i++){
  if(isNil(aryDataset[i].getConstColumn("firstCode"))) continue;
  sBindingCtrls += " " + aryDataset[i].name + "=" + aryDataset[i].getConstColumn("firstCode");
 }

 dsService.setColumn(nSrvRow, "id",sServiceId);
 dsService.setColumn(nSrvRow, "inDatasets","dsSymAumSa01M00SVO=_dsAuthCodeList" + sServiceId);
 dsService.setColumn(nSrvRow, "outDatasets", sOutDatasets.trim());

 dsService.setColumn(nSrvRow, "package", "edusys.cm.sym.aum.sa.svc");
 dsService.setColumn(nSrvRow, "vo", "SymAumSa01M00SVO");
 dsService.setColumn(nSrvRow, "service", "SymAumSa01M00SVC");
 dsService.setColumn(nSrvRow, "method", "authorization");
 dsService.setColumn(nSrvRow, "svctp", "");
 dsService.setColumn(nSrvRow, "bindingControls", "");
 dsService.setColumn(nSrvRow, "bizMessage", "");
 dsService.setColumn(nSrvRow, "callback", "lfn_trans_callback");
 dsService.setColumn(nSrvRow, "innerCallback", "smf_AuthCallback");
 dsService.setColumn(nSrvRow, "url", "svc_neis::/sym_aum_sa00_001.xp");

 dsService.setColumn(nSrvRow, "state", "");
 dsService.setColumn(nSrvRow, "reason", "");
 dsService.setColumn(nSrvRow, "message", "");
 dsService.setColumn(nSrvRow, "asynctp", iif(bSync, "false", "true"));
 dsService.setColumn(nSrvRow, "bintp", "");
 dsService.setColumn(nSrvRow, "comtp", "");

 //dsService를 통해 호출 한다.
 var jServiceParam = {sender : obj};
 smf_Service(sServiceId, jServiceParam);
}

/**
 * 실행되어있는 모든 폼에 observe이벤트를 호출
 *
 * @param  sender   이벤트를 발생한 폼
 * @param  sEventId event id
 * @param  jParams  parameter
 * @return void
 * @see    function Button00_onclick(obj:Button,  e:ClickEventInfo)
           {
               smf_NotifyEvent(this, "event01", {test1 : dsMain.getColumn(dsMain.rowposition, "test")
                                                ,test2 : dsMain.getColumn(dsMain.rowposition, "test2")});
           }
 *
 */
function smf_NotifyEvent(sender, sEventId, jParams){
    if(gv_AppFramePath == null) return;

 for(var i=0,size=gv_AppFramePath.frames.length; i<size; i++){
        var frame = gv_AppFramePath.frames[i];
        if(frame == undefined || frame == null ) continue;
        var form = frame.form;
        if(form == null) continue;

        if(form.lfn_observe != undefined && form.lfn_observe != null){
            form.lfn_observe(sender, sEventId, jParams);
        }
 }
}

/**
 * focus를 준다.
 *
 * @param  obj
 * @return void
 *
 */
function smf_SetFocus(obj){
    var aryParents = [];
    do{
        aryParents.push(obj);
        obj = obj.parent;
    }while((obj+"") != "[object Form]");

    for(var i=aryParents.length-1; i>=0; i--){
        if(aryParents[i] instanceof Tabpage){
            var oTab = aryParents[i+1]; //tab임
            //tabindex를 찾음
            for(var n=0,sizen=oTab.tabpages.length; n<sizen; n++){
                if(oTab.tabpages[n] == aryParents[i]){
                    oTab.tabindex = n;
                    break;
                }
            }

            aryParents[i].setFocus();
        }
    }
}

/**
 * aryObj중 obj를 제외하고 값을 비운다.
 *
 * @param  obj
 * @param  aryObj
 * @return void
 *
 */

function smf_ClearExcludeValue(obj, aryObj){
 for(var i=0,size=aryObj.length; i<size; i++){
  var comp = aryObj[i];
  if(obj != comp){
   comp.value = "";
  }
 }
}

/**
 * dsA와 dsB의 의 column값을 비교
 *
 * @param  obj
 * @param  aryObj
 * @return json object
 * @exsample//데이터 비교
   if(smf_DiffDataset(dsMaster, dsDetail).isDiff){
    alert("값이 서로 다르다");
    return;
   }
 */

function smf_DiffDataset(dsA, dsB, jOption){
 if(isNil(jOption)) jOption = {};
 //원본으로 비교할지

 if(jOption.isOriginData != false) jOption.isOriginData = true;
 if(isNil(jOption.columns)) jOption.columns = [];

 if(jOption.isOriginData == true){
  var _dsDiff = smf_GetOrCreateDataset('_dsDiff');
  _dsDiff.assign(dsA);
  _dsDiff.clearData();

  //원본데이터를 채워넣는다.
  for(var i=0,size=dsA.rowcount; i<size; i++){
   if(dsA.getRowType(i) == Dataset.ROWTYPE_INSERT) continue;

   var nAddRow = _dsDiff.addRow();
   for(var n=0,sizen=dsA.colcount; n<sizen; n++){
    _dsDiff.setColumn(nAddRow, n, dsA.getOrgColumn(i, n));
   }
  }

  dsA = _dsDiff;
 }

 var result = {};
 result.isDiff = false;
 result.isDiffRowCount = false;
 result.isDiffColCount = false;

 result.isDiffValue = false; //column value가 다른지
 result.diffValue = {}; //다른 column value 정보
 result.diffValue.columnName = "";
 result.diffValue.rowIndex   = -1;

 //컬럼 갯수를 비교한다.
 if(dsA.colcount != dsB.colcount){
  result.isDiff = true;
  result.isDiffColCount = true;
  return result;
 }

 //rowcount를 비교한다.
 if(dsA.rowcount != dsB.rowcount){
  result.isDiff = true;
  result.isDiffRowCount = true;
  return result;
 }

 //0개면 전체를 비교
 if(jOption.columns.length == 0){
  for(var n=0,sizen=dsA.colcount; n<sizen; n++){
   var sColumnName = dsA.getColID(n);
   if(sColumnName == "_chk") continue;
   jOption.columns.push(sColumnName);
  }
 }

 //컬럼 데이터를 비교한다.
 for(var i=0,size=dsA.rowcount; i<size; i++){
  for(var n=0,sizen=jOption.columns.length; n<sizen; n++){
   var sColumnName = jOption.columns[n];
   if(dsA.getColumn(i, sColumnName) != dsB.getColumn(i, sColumnName)){
    result.isDiff = true;
    result.isDiffValue = true;
    result.diffValue.columnName = sColumnName;
    result.diffValue.rowIndex = i;
    break;
   }
  }
  if(result.isDiffValue == true) break;
 }

 return result;
}


function smf_DescComponent(obj){
 var sDesc = "";
 var idx = 0;
 var arySortedPropName = [];
 for(var sPropName in obj){
  arySortedPropName.push(sPropName);
 }

 arySortedPropName = arySortedPropName.sort();
 for(var i=0,size=arySortedPropName.length; i<size; i++){
  var propValue = obj[arySortedPropName[i]];

  if(propValue != undefined) {
   sDesc += format("\n{0} = {1}", [arySortedPropName[i], propValue]);
  }
 }

 if(obj instanceof Form){
  sDesc = obj + " " + obj.name + sDesc;
 }else{
  sDesc = obj + " " + smf_FullId(obj) + sDesc;
 }
 return sDesc;
}

/**
 * (공통만사용)권한 처리에서 사용하는 callback함수
 *
 * @param  sServiceId : callback함수에서 받을 서비스명
 * @param  aryDataset : 공통코드로 넘길 Array 데이터셋
 * @param  bSync      : 비동기로 넘길지 default false
 * @return void
 * @see    smf_AuthList('auth', [dsAy, dsOrdScCode, dsClassCode]);
 */
function smf_ScoreAuthCallback(sId, nReason, sMessage, jParams){
    if(nReason == -1 ) return;
 var nRow = dsService.findRow('id', sId);
 var service = (new RequestService(this, sId, jParams));
 var aryInDatasets = service.toArrayByInDatasets();
 var aryOutDatasets = service.toArrayByOutDatasets();
  
 for(var i=0,size=aryOutDatasets.length; i<size; i++){
  var ds = aryOutDatasets[i];
  var combo = smf_CompByInnerDataset(ds);
  if(!(combo instanceof Combo)) continue;

  
  //0번째 값으로 세팅한다.
  combo.value = ds.getColumn(0, combo.codecolumn);
   }
  
   //직접권한접근이 허용된 경우
   //년도Combo를 Edit로 변경해준다.
   if("Y" == _dsAuthReturn.getColumn(0, "directAuthYn")){
    for(var i=0,size=aryOutDatasets.length; i<size; i++){
    var combo = smf_CompByInnerDataset(aryOutDatasets[i]);
   var cmbType = aryOutDatasets[i].getConstColumn('cmbType');
   var _check = cmbType.toUpperCase().lastIndexOf('AY');
   if(_check != -1 && combo.visible == true){
    var _edit = new Edit('edit' + cmbType);
    combo.parent.addChild(_edit.name, _edit);
    _edit.position = combo.position;
    _edit.value = aryOutDatasets[i].getColumn(0,0);
    _edit.show();
    combo.visible = false;
    combo.innerdataset = null;
   }
   }
  }
}

/**
 * 성적용 권한 처리
 *
 * @param  sServiceId : callback함수에서 받을 서비스명
 * @param  aryDataset : 공통코드로 넘길 Array 데이터셋
 * @param  bSync      : 비동기로 넘길지 default false
 * @return void
 * @see    smf_ScoreAuthList('auth', [dsAy, dsOrdScCode, dsClassCode]);
 */
function smf_ScoreAuthList(obj, sServiceId, aryDataset, bSync){
 bSync = bSync == true;

 ////////////////////////검증 //////////////////////////////

 sServiceId = smf_Str(sServiceId);

 if(sServiceId.indexOf("scoreAuthOnload") != 0 && sServiceId.indexOf("authRowChange") != 0){
  trace(format("sServiceId '{0}'는 잘못된 아이디입니다.\nauthOnload나 authRowChange로 시작해야합니다.", [sServiceId]));
  alert(format("sServiceId '{0}'는 잘못된 아이디입니다.\nauthOnload나 authRowChange로 시작해야합니다.", [sServiceId]));
  return false;
 }

 if(! (aryDataset instanceof Array)){
  trace("파라메터aryDataset는 Array이어야 합니다");
  alert("파라메터aryDataset는 Array이어야 합니다");
  return false;
 }

 //todo 검증 detail
 ////////////////////////검증끝//////////////////////////////

 // @example utlf_sysGbnNm("mis_sdlca00_m00", "NM")
 //페이지에서 가져온 정보를 세팅해준다
 var arrSysNm = new Array();
 //임의의 페이지 id 값을 세팅해줌
 //arrSysNm = utlf_sysGbnNm("his_edcoh02_m00","CD");
 arrSysNm = utlf_sysGbnNm(smf_ParentForm(this).name,"CD");

 // 소스명에 사용할 서브시스템명
 // 예) His / Mis / Els / Sps
 var _tmp_SysNm = arrSysNm[1].substring(0,1) + arrSysNm[1].substring(1,3).toLowerCase();

 

 //_dsAuthCodeList Dataset생성
 var _dsAuthCodeList = objects['_dsAuthCodeList'];
 if( _dsAuthCodeList  == null){
  _dsAuthCodeList = new Dataset();
  _dsAuthCodeList.name = "_dsAuthCodeList";
  this.addChild(_dsAuthCodeList.name, _dsAuthCodeList);
 }
 _dsAuthCodeList.clearData();

 //_dsAuthReturn Dataset생성
 var _dsAuthReturn = objects['_dsAuthReturn'];
 if( _dsAuthReturn == null){
  _dsAuthReturn = new Dataset();
  _dsAuthReturn.name = "_dsAuthReturn";
  _dsAuthReturn.useclientlayout = true;
  this.addChild(_dsAuthReturn.name, _dsAuthReturn);
 }
 _dsAuthReturn.clearData();

 //컬럼생성
 _dsAuthCodeList.addColumn("sysCode"  , "STRING", 256);
 _dsAuthCodeList.addColumn("subSysCode" , "STRING", 256);
 _dsAuthCodeList.addColumn("pageId"  , "STRING", 256);

 for(var i=0,size=aryDataset.length; i<size; i++){
  var ds = aryDataset[i];
  ds.getConstColumn("cmbType");
  _dsAuthCodeList.addColumn("necessaryComboType", "STRING", 256);
  _dsAuthCodeList.addColumn("necessaryTenseType", "STRING", 256);
 }


 //row 추가
 _dsAuthCodeList.addRow();
 _dsAuthCodeList.setColumn(0, "pageId"  , arrSysNm[4]);
 _dsAuthCodeList.setColumn(0, "sysCode"  , "SW");
 _dsAuthCodeList.setColumn(0, "subSysCode" , smf_SubSysCode());
 _dsAuthCodeList.setColumn(0, "schulCode" , utlf_getSession("insttCode") );

 

 var  necessaryComboType = "";
 var  necessaryTenseType = "";
 for(var i=0,size=aryDataset.length; i<size; i++){
  var ds = aryDataset[i];
  necessaryComboType += ds.getConstColumn("cmbType") + ",";
  necessaryTenseType += (isNotNil(ds.getConstColumn("cmbTense")) ? ds.getConstColumn("cmbTense") : "PRESENT") + ",";
 }
 _dsAuthCodeList.setColumn(0,"necessaryComboType",necessaryComboType);
 _dsAuthCodeList.setColumn(0,"necessaryTenseType",necessaryTenseType);
 //trace(_dsAuthCodeList.saveXML());
 //dsService에 혹시 같은 아이디가 있으면 삭제 해준다.
 if(dsService.findRow("id", sServiceId) > -1){
  var nDeleteRow = dsService.findRow("id", sServiceId);
  dsService.deleteRow(nDeleteRow);
 }

 // dsService를 통해 넘긴다
 var nSrvRow = dsService.addRow();

 var sOutDatasets = "";
 for(var i=0,size=aryDataset.length; i<size; i++){
  var ds = aryDataset[i];
  //dataSet 명으로부터 이름을 추출하여 사용함
  var svoReturnListNm = ds.name.substring(2,3).toLowerCase() + ds.name.substring(3, ds.name.length);
  sOutDatasets += ds.name + "="+"ds" + _tmp_SysNm + "ScrEt01M00SVO_"+ svoReturnListNm + "List ";

 }

 var sBindingCtrls = "";
 for(var i=0,size=aryDataset.length; i<size; i++){
  var ds = aryDataset[i];
  if(isNil(ds.getConstColumn("firstCode"))) continue;
  sBindingCtrls += " " + ds.name + "=" + ds.getConstColumn("firstCode");
 }

 dsService.setColumn(nSrvRow, "id"    , sServiceId);
 dsService.setColumn(nSrvRow, "inDatasets"  , "ds" + _tmp_SysNm + "ScrEt01M00SVO=_dsAuthCodeList");
 dsService.setColumn(nSrvRow, "outDatasets"  , sOutDatasets.trim() + " _dsAuthReturn=_dsAuthReturn");

 dsService.setColumn(nSrvRow, "package"   , "edusys.sw." + _tmp_SysNm.toLowerCase() + ".scr.et.svc");
 dsService.setColumn(nSrvRow, "vo"    , _tmp_SysNm + "ScrEt01M00SVO");
 dsService.setColumn(nSrvRow, "service"   ,  _tmp_SysNm + "ScrEt01M00SVC");
 dsService.setColumn(nSrvRow, "method"   , "authorization");
 dsService.setColumn(nSrvRow, "svctp"   , "");
 dsService.setColumn(nSrvRow, "bindingControls" , sBindingCtrls.trim());
 dsService.setColumn(nSrvRow, "bizMessage"  , "");
 dsService.setColumn(nSrvRow, "callback"   , "lfn_trans_callback");

 //form onload에서만 callback사용
 if(sServiceId.indexOf("scoreAuthOnload") == 0){
  dsService.setColumn(nSrvRow, "innerCallback", "smf_ScoreAuthCallback");
 }

 dsService.setColumn(nSrvRow, "url", "svc_neis::/" + _tmp_SysNm.toLowerCase() + "_scr_et00_000.xp");

 dsService.setColumn(nSrvRow, "state", "");
 dsService.setColumn(nSrvRow, "reason", "");
 dsService.setColumn(nSrvRow, "message", "");
 dsService.setColumn(nSrvRow, "asynctp", iif(bSync, "false", "true"));
 dsService.setColumn(nSrvRow, "bintp", "");
 dsService.setColumn(nSrvRow, "comtp", "");

 //trace(_dsAuthCodeList.saveXML());
 //dsService를 통해 호출 한다.
 smf_Service(sServiceId);
}

/**
 * 권한 처리
 * @param  sServiceId : callback함수에서 받을 서비스명
 * @param  aryDataset : 공통코드로 넘길 Array 데이터셋
 * @param  bSync      : 비동기로 넘길지 default false
 * @return void
 * @see    smf_AuthChain('authChain', obj,
       [dsOrgAy,dsOrgOrdScCode,dsOrgGrade,dsOrgClassCode], //전체 콤보순서에 따른 dataset들
       [cmbOrgAy.value,cmbOrgOrdScCode.value,cmbOrgGrade.value,cmbOrgClassCode.value] //데이터셋에 매핑되어 있는 콤보의value
       );

*/
function smf_ScoreAuthChain(sServiceId, obj, aryDataset, aryCmb){

 

 var bSync = bSync == true;
 sServiceId = smf_Str(sServiceId);
 ////////////////////////검증 //////////////////////////////
 if(sServiceId.indexOf("scoreAuth") != 0){
  trace(format("sServiceId '{0}'는 잘못된 아이디입니다.\nauth로 시작해야합니다.", [sServiceId]));
  alert(format("sServiceId '{0}'는 잘못된 아이디입니다.\nauth로 시작해야합니다.", [sServiceId]));
  return false;
 }

 if(isNil(obj)){
  trace("obj를 입력하세요.");
  alert("obj를 입력하세요.");
  return false;
 }

 /* aryDataset체크 */
 if(! (aryDataset instanceof Array)){
  trace("파라메터aryDataset는 Array이어야 합니다");
  alert("파라메터aryDataset는 Array이어야 합니다");
  return false;
 }
 /* refValues 체크 */
 if(! (aryCmb instanceof Array)){
  trace("파라메터aryCmb는 Array이어야 합니다");
  alert("파라메터aryCmb는 Array이어야 합니다");
  return false;
 }

 //입력한 애들이 실제 존재하는 데이터 셋들인가
 //aryDataset.
 for(var i=0; i < aryDataset.length; i++){
  trace("#"+i + ":"+ aryDataset[i].name);
  if(!(aryDataset[i] instanceof Dataset)){
   alert(format("함수의 3번째 인자 aryDataset[{0}]번째 인자가 Dataset 객체이어야 합니다.",[i]));
   return false;
  }
 }

 if(aryDataset.length != aryCmb.length){
  alert("지정한 Dataset과 Combo 값들의의 갯수가 다릅니다.");
  return false;
 }
 ////////////////////////검증끝//////////////////////////////

 arrSysNm = utlf_sysGbnNm(smf_ParentForm(this).name,"CD");

 // 소스명에 사용할 서브시스템명
 // 예) His / Mis / Els / Sps
 var _tmp_SysNm = arrSysNm[1].substring(0,1) + arrSysNm[1].substring(1,3).toLowerCase();


 //현재 콤보의 위치 파악
 var cmbPositionCnt = 0;
 for(var i=0; i < aryDataset.length; i++){
  if((smf_ToDatasetObject(obj.innerdataset).name ==  aryDataset[i].name)){
   cmbPositionCnt = i;
   break;
  }
 }


 /*cmbPositionCnt기준으로 이전 콤보의 값들을 input으로 가져가고
   cmbPositionCnt기준으로 이후 콤보들이 서비스의 out으로 채워질 대상들이다.*/
 //in
 var refValues = "";
 for(var i=0, size = cmbPositionCnt + 1; i < size; i++){
  var cmbType = aryDataset[i].getConstColumn('cmbType');
  var result = cmbType.toUpperCase().lastIndexOf('AY');
  var combo = smf_CompByInnerDataset(aryDataset[i]);
  //cmbType이 XXXAy인 경우
  if(combo == null && result != -1){
   var edit = 'obj.parent.edit'+cmbType + '.value';
   refValues  += aryDataset[i].getConstColumn("cmbType") + "="+ eval(edit) +  (i==size-1?"":",");
  }else{
   refValues  += aryDataset[i].getConstColumn("cmbType") + "="+aryCmb[i] +  (i==size-1?"":",");
  }
 }

 trace('####'+refValues);


 _dsAuthCodeList.clear();
 //컬럼생성
 _dsAuthCodeList.addColumn("sysCode", "STRING",256);
 _dsAuthCodeList.addColumn("subSysCode", "STRING",256);
 _dsAuthCodeList.addColumn("pageId", "STRING",256);
 _dsAuthCodeList.addColumn("refValues", "STRING",256);
 _dsAuthCodeList.addColumn("necessaryComboType", "STRING",256);
 _dsAuthCodeList.addColumn("necessaryTenseType", "STRING",256);


 //row 추가
 _dsAuthCodeList.addRow();

 _dsAuthCodeList.setColumn(0,"pageId", arrSysNm[4]);
 _dsAuthCodeList.setColumn(0,"sysCode", "SW");
 _dsAuthCodeList.setColumn(0,"subSysCode", smf_SubSysCode());
 _dsAuthCodeList.setColumn(0,"schulCode", utlf_getSession("insttCode"));

 _dsAuthCodeList.setColumn(0,"refValues",refValues);

 

 

 var necessaryComboType = "";
 var necessaryTenseType = "";
 trace("cmbPositionCnt=" + cmbPositionCnt);
 trace("aryDataset.length=" + aryDataset.length);

 for(var i=cmbPositionCnt+1, size=aryDataset.length; i<size; i++){
  necessaryComboType += aryDataset[i].getConstColumn("cmbType") + (i==size-1?"":",");
  necessaryTenseType += (isNotNil(aryDataset[i].getConstColumn("cmbTense")) ? aryDataset[i].getConstColumn("cmbTense") : "PRESENT") + (i==size-1?"":",");
 }
 _dsAuthCodeList.setColumn(0,"necessaryComboType",necessaryComboType);
 _dsAuthCodeList.setColumn(0,"necessaryTenseType",necessaryTenseType);

 //dsService에 혹시 같은 아이디가 있으면 삭제 해준다.
 if(dsService.findRow("id", sServiceId) > -1){
  var nDeleteRow = dsService.findRow("id", sServiceId);
  dsService.deleteRow(nDeleteRow);
 }

 // dsService를 통해 넘긴다
 var nSrvRow = dsService.addRow();

 var sOutDatasets = "";
 for(var i=cmbPositionCnt+1; i<aryDataset.length; i++){
  var ds = aryDataset[i];
  var svoReturnListNm = ds.name.substring(2,3).toLowerCase() + ds.name.substring(3, ds.name.length);
  sOutDatasets += ds.name + "="+"ds" + _tmp_SysNm + "ScrEt01M00SVO_"+ svoReturnListNm + "List ";

 }


 var sBindingCtrls = "";
 for(var i=cmbPositionCnt; i<aryDataset.length; i++){
  if(isNil(aryDataset[i].getConstColumn("firstCode"))) continue;
  sBindingCtrls += " " + aryDataset[i].name + "=" + aryDataset[i].getConstColumn("firstCode");
 }

 dsService.setColumn(nSrvRow, "id",sServiceId);
 dsService.setColumn(nSrvRow, "inDatasets","ds" + _tmp_SysNm + "ScrEt01M00SVO=_dsAuthCodeList");
 dsService.setColumn(nSrvRow, "outDatasets", sOutDatasets.trim());

 dsService.setColumn(nSrvRow, "package", "edusys.sw." + _tmp_SysNm.toLowerCase() + ".scr.et.svc");
 dsService.setColumn(nSrvRow, "vo", _tmp_SysNm + "ScrEt01M00SVO");
 dsService.setColumn(nSrvRow, "service", _tmp_SysNm + "ScrEt01M00SVC");
 dsService.setColumn(nSrvRow, "method", "authorization");
 dsService.setColumn(nSrvRow, "svctp", "");
 dsService.setColumn(nSrvRow, "bindingControls", "");
 dsService.setColumn(nSrvRow, "bizMessage", "");
 dsService.setColumn(nSrvRow, "callback", "lfn_trans_callback");
 dsService.setColumn(nSrvRow, "innerCallback", "smf_ScoreAuthCallback");
 dsService.setColumn(nSrvRow, "url", "svc_neis::/" + _tmp_SysNm.toLowerCase() + "_scr_et00_000.xp");

 dsService.setColumn(nSrvRow, "state", "");
 dsService.setColumn(nSrvRow, "reason", "");
 dsService.setColumn(nSrvRow, "message", "");
 dsService.setColumn(nSrvRow, "asynctp", iif(bSync, "false", "true"));
 dsService.setColumn(nSrvRow, "bintp", "");
 dsService.setColumn(nSrvRow, "comtp", "");
 //dsService를 통해 호출 한다.


 //trace(_dsAuthCodeList.saveXML());
 smf_Service(sServiceId);
}

/**
 * 등록/수정된 Row중, _chk가 1이 아닌 row를 NORMAL로 변경
 *
 * @param  ds : Dataset
 * @return void
 * @see
*/

function smf_UnCheckRowToNormalType(ds){
    ds.updatecontrol = false;
    try{
  for(var i=0,size=ds.rowcount; i<size; i++){
   if(ds.getColumn(i, "_chk") == "1") continue;
   ds.setRowType(i, Dataset.ROWTYPE_NORMAL);
  }
 }finally{
  ds.updatecontrol = true;
 }
}

/**
 * 엔터값을(\n)-> sTo로 변경
 * smf_ReplaceLineBreak("ab\ncd", "/") --> ab/cd
 * smf_ReplaceLineBreak("ab\ncd", " ") --> ab cd
 * @param  sText : 바꾸려고하는 String
 * @param  sTo   : 엔터값을 변경하려는 String
 * @return void
 * @see
*/
function smf_ReplaceLineBreak(sText, sTo){
 return smf_Str(sText).replace("\r\n", "\n").replace("\n", sTo);
}

function smf_HandleList(list, fnc){
 for(var index in list){
  fnc(index, list[index]);
 }

 return list;
}

function smf_HandleDataset(ds : Dataset, fncHandler){
 if(fncHandler == undefined) return;
 for(var i=0,size=ds.rowcount; i<size; i++){
  fncHandler(ds, i);
 }
}

/**
 * Dataset을 이름으로 찾는다. 없으면 생성.
 * @param  sName : Dataset Name
 * @return Dataset
*/
function smf_GetOrCreateDataset(sName){
 var ds = objects[sName];
 if(ds == null){
   ds = new Dataset();
  ds.name = sName;
  this.addChild(ds.name, ds);
 }

 ds.clearData();
 return ds;
}

/**
 * Text파일을 Dataset으로 import한다.
 * @param  fileStream : 열려있는 VirtualFile
 * @param  dsDesc     : Dataset
 * @return result
 * @see
*/
function smf_FixedTextToDataset(fileStream, dsDesc : Dataset){
 var result = {};
 result.isSucc = true;
 result.errorMessage = "";
 result.errorLine = -1;

 var aryDoubleByteColumn = [];
 var nColCount = dsDesc.colcount;

 //
 var nExpectedLength = 0;
 for(var i=0; i<nColCount; i++){
  var colInfo = dsDesc.getColumnInfo(i);
  nExpectedLength += colInfo.size;
 }

 var sLineText = "";
 var nLineCount = 0;
 var nCurrentLine = 0;
 while((sLineText=fileStream.readLine("\r\n")) != ""){
  nCurrentLine++;

  if(smf_Length(sLineText, 2) != nExpectedLength){
   result.isSucc = false;
   result.errorLine = nCurrentLine;
   result.errorMessage = format("유효하지 않은 데이터가 있습니다.\n {0}줄의 사이즈가 {1}입니다.\n유효한 사이즈인 {2}으로 수정하세요.",
                                [nCurrentLine, smf_Length(sLineText, 2), nExpectedLength]);

   return result;
  }
  var nAddedRow = dsDesc.addRow();
  for(var i=0; i<nColCount; i++){
   var colInfo = dsDesc.getColumnInfo(i);
   var sColumnValue = smf_SubStr(sLineText, 0, colInfo.size);
   dsDesc.setColumn(nAddedRow, i, sColumnValue.trim());
   sLineText = sLineText.substr(sColumnValue.length);
  }

  if(((nLineCount++) % 50) == 0){
   this.redraw();
   this.resetScroll();
  }
 }

 dsDesc.rowposition = 0;

 return result;
}

/**
 * String의 Length를 구한다.
 * @param  sText : 문자열
 * @param  nDoublebyteSize : (Default : 2) 한글이나 한문의 size로 계산할지
 * @return Integer
*/
function smf_Length(sText, nDoublebyteSize){
 if(isNil(nDoublebyteSize)) nDoublebyteSize = 2;

 if(isNil(sText)) return 0;
 sText = smf_Str(sText);
 var nLength = 0;
    for (var i=0,size=sText.length; i<size; i++){
  if (sText.charCodeAt(i) > 127) nLength += nDoublebyteSize;
  else nLength += 1;
    }
 return nLength;
}

function smf_SubStr(s, nStart, nSize){
 if(s == undefined || s == null || s == "") return "";
 s = (s + "");

 var nInc = 0;

 for(var i=nStart; i<nSize; i++){
  if(s.charCodeAt(i) > 255){
   nSize -= 1;
  }
  nInc += 1;
 }

 return s.substr(nStart, nInc);
}

/**
 * Button을 Enable/Disable 시킨다.
 * @param  composite : Button을 담고있는 객체(this, Div, Tabpage등)
 * @param  bEnabled  : true/false
 * @param  bInclude  : (Default : false) true/false Div가 있다면 내부까지 포함시킬지
 * @param  fncAccept : (Default : undefined) 비교함수 function(btn){};

 * @example smf_EnableButtonList(Div00, true); //Div00내부 버튼 활성화

 * @example smf_EnableButtonList(Div00, true, true); //Div00내부 버튼 비활성화및 Div00내부의 Div를 모두 찾아 Button 활성화

 * @example smf_EnableButtonList(Div01, false, false, function (btn){
          return btn.name != "Button00";
         });
         //Button00은 제외하고 Div01내부 버튼 활성화
 * @return void
*/
function smf_EnableButtonList(composite, bEnabled, bInclude, fncAccept){
 if(bInclude != true) bInclude = false;

    smf_TravelComponents(composite, function (oComp, fnc, nLvl){
  if(bInclude == false){
   if(oComp.parent != composite) return;
  }

  if(!(oComp instanceof Button)) return;

  if(fncAccept != undefined){
   if(fncAccept(oComp) != true) return;
  }
  oComp.enable = bEnabled;
    });
}


/**
 * 강제로 ds의 nRow의 타입을 normal->update로 변경한다.
 * setRowType이 안될때 사용하는 함수.
 * @param  ds : Dataset
 * @param  nRow : update할 Row.생략시 모든 row
 * @return void
*/
function smf_ForceUpdateRows(ds : Dataset, nRow){
 if(null == ds.getColumnInfo("_forceUpdate")){
  var colinfo = new ColumnInfo();
  colinfo.type = 2; // "STRING"
  colinfo.size = 255;
  ds.addColumnInfo("_forceUpdate",colinfo);
 }

 //nRow를 받으면 nRow만 설정
 if(nRow != undefined){
  var nSeq = isNil(ds.getColumn(nRow, "_forceUpdate")) ? 0 : parseInt(ds.getColumn(nRow, "_forceUpdate"));
  ds.setColumn(nRow, "_forceUpdate", nSeq+1);
  return;
 }

 //nRow를 안받으면 전체Row 설정
 for(var i=0,size=ds.rowcount; i<size; i++){
  var nSeq = isNil(ds.getColumn(i, "_forceUpdate")) ? 0 : parseInt(ds.getColumn(i, "_forceUpdate"));
  ds.setColumn(i, "_forceUpdate", nSeq+1);
 }
}

/**
 * checkbox를 radio처럼 하나만 check되게
 * @param  ds          : Dataset
 * @param  nCheckedRow : (Default : rowposition) check할 Row
 * @param  sColumnName : (Default : _chk) check할 columnName
 * @return void
*/
function smf_CheckColumnAsRadio(ds:Dataset, nCheckedRow, sColumnName){
 if(isNil(nCheckedRow)) nCheckedRow = ds.rowposition;
 if(isNil(sColumnName)) sColumnName = "_chk";
 for(var i=0,size=ds.rowcount; i<size; i++){
  if(i == nCheckedRow){
   ds.setColumn(i, sColumnName, "1");
  }else{
   ds.setColumn(i, sColumnName, "0");
  }
 }
}

/**
 * 눈에보기쉬운 텍스트로 보여준다.
 * @param  sText        : Text
 * @param  nLength      : 보여줄 텍스트의 수
 * @return String
*/
function smf_EasyOnEyesText(sText, nLength){
 sText = smf_Str(sText);
 if(sText.length > nLength){
  sText = sText.substring(0, nLength) + "[생략...]";
 }

 return sText;
}

/**
 * 그리드에서 눈에보기쉬운 텍스트로 보여준다.
 * @param  sDatasetName : String
 * @param  nRow         : currow
 * @param  sText        : Text
 * @param  nLength      : 보여줄 텍스트의 수
 * @param  bShowFullTextWhenCurrow : (Default : true) 선택한 Row의 메시지는 다 보여주기

 * @return String
*/
function smf_GridEasyOnEyesText(sDatasetName, nRow, sText, nLength, bShowFullTextWhenCurrow){
 if(isNil(bShowFullTextWhenCurrow)) bShowFullTextWhenCurrow = true;
 var ds = eval(sDatasetName);

 if(bShowFullTextWhenCurrow == true){
  if(ds.rowposition == nRow) return smf_Str(sText);
 }

 return smf_EasyOnEyesText(sText, nLength);
}

/**
 * 그리드 선택된셀은 full Text를 아니면 눈에보기쉬운 텍스트로 보여준다.
 * @param  grd          : grid name
 * @param  nCell        : cell
 * @param  nRow         : currow
 * @param  sText        : Text
 * @param  nLength      : 보여줄 텍스트의 수

 * @return String
*/
function smf_GridCellText(grd, nCell, nRow, sText){
    var ds = eval(grd.binddataset);
    if(ds.rowposition == nRow) return smf_Str(sText);
    var nLength = parseInt(grd.getRealColSize(nCell) / 18);
    return smf_Str(smf_EasyOnEyesText(sText, nLength));
}

/**
 * 그리드 선택된셀은 full Text를 아니면 눈에보기쉬운 텍스트로 보여준다.
 * @param  grd          : grid name
 * @param  nCell        : cell
 * @param  nRow         : currow
 * @param  sText        : Text
 * @param  nLength      : 보여줄 텍스트의 수

 * @return String
*/
function smf_GridCellEasyOnEyesText(grd, nCell, nRow, sText, nLength){
 if(grd.currentcell != nCell) return smf_EasyOnEyesText(sText, nLength);
 if(grd.currentrow != nRow) return smf_EasyOnEyesText(sText, nLength);

 return smf_Str(sText);
}


/**
 * (팝업만 사용)컴포넌트들의 간격을 왼쪽정렬한다.
 * 중간에 visible false된 컴포넌트들의 간격을 자동으로 조절해준다.
 * @param  aryComp : array of component
 * @param  sAlign : (Default "right") 왼쪽, 오른쪽 정렬
 * @exsample
 Button00.visible = true;
 Button01.visible = false;
 Button02.visible = true;
 smf_AlignComponents([Button00, Button01, Button02]); //Button01.visible이 안보이고 그 자리에 Button00이 오게 됨.

 * @return void
*/
function smf_LeftAlignComponents(aryComp){
 var nSpace = 0;

 if(isNil(aryComp[0].nSpace)){
  for(var i=aryComp.length-1; i>=0; i--){
   aryComp[i]._AbsLeftPositionX = aryComp[i].position.x;
  }

  nSpace = aryComp[1].position.x - (aryComp[0].position.x + aryComp[0].position.width);
  aryComp[0].nSpace = nSpace;
 }else{
  nSpace = aryComp[0].nSpace;
 }

 for(var i=aryComp.length-1; i>=0; i--){
  aryComp[i].position.x = aryComp[i]._AbsLeftPositionX;
 }

 var fncMove = function(oPivot){
  for(var i=aryComp.length-1; i>=0; i--){
   if(oPivot == aryComp[i]) return;

   aryComp[i].position.x -= oPivot.position.width + nSpace;
  }
 }

 for(var i=0,size=aryComp.length; i<size; i++){
  var comp = aryComp[i];
  if(! comp.visible){
   fncMove(comp);
  }
 }
}

/**
 * (POPUP만 사용)컴포넌트들의 간격을 오른쪽정렬한다.
 * 중간에 visible false된 컴포넌트들의 간격을 자동으로 조절해준다.
 * @param  aryComp : array of component
 * @param  sAlign : (Default "right") 왼쪽, 오른쪽 정렬
 * @exsample
 Button00.visible = true;
 Button01.visible = false;
 Button02.visible = true;
 smf_AlignComponents([Button00, Button01, Button02]); //Button01.visible이 안보이고 그 자리에 Button00이 오게 됨.

 * @return void
*/
function smf_RightAlignComponents(aryComp){
 var nSpace = 0;

 if(isNil(aryComp[0].nSpace)){
  for(var i=0,size=aryComp.length; i<size; i++){
   aryComp[i]._AbsRightPositionX = aryComp[i].position.x;
  }

  nSpace = aryComp[1].position.x - (aryComp[0].position.x + aryComp[0].position.width);
  aryComp[0].nSpace = nSpace;
 }else{
  nSpace = aryComp[0].nSpace;
 }

 for(var i=0,size=aryComp.length; i<size; i++){
  aryComp[i].position.x = aryComp[i]._AbsRightPositionX;
 }

 var fncMove = function(oPivot){
  for(var i=0,size=aryComp.length; i<size; i++){
   if(oPivot == aryComp[i]) return;

   aryComp[i].position.x += oPivot.position.width + nSpace;
  }
 }

 for(var i=aryComp.length-1; i>=0; i--){
  var comp = aryComp[i];
  if(! comp.visible){
   fncMove(comp);
  }
 }
}

/**
 * resize safe하게 Div나 Tabpage의 url을 변경한다.
 *
 * @param  oDivOrTabpage : div나 tabpage
 * @param  sUrl          : url
 * @param  bClearUrl     : url을 비우고 세팅할지
 * @exsample
 smf_LoadDiv(divManage, "sw.com.title:test.xfdl");

 * @return void
*/
function smf_LoadDiv(oDivOrTabpage, sUrl, bClearUrl){
 if(isNil(bClearUrl)) bClearUrl = true;
 var mainForm = smf_ForceMainForm(oDivOrTabpage); 
 var fullId = smf_FullId(oDivOrTabpage);
//  if(isNotNil(mainForm.smv_Positions[fullId])){
//   oDivOrTabpage.position.width = mainForm.smv_Positions[fullId].width;
//   oDivOrTabpage.position.height = mainForm.smv_Positions[fullId].height;
//  }
 smf_BeginResize(this);
 if(bClearUrl){
  oDivOrTabpage.url = "";
 }
 oDivOrTabpage.url = sUrl;
}

/**
 * 사용자가 호출하는 리사이즈를 위한  초기화작업
 *
 * @return sizes
*/
function smf_BeginResize(mainForm){
 smv_Resize = true;
 mainForm = smf_ForceMainForm(mainForm); 
 try{
  mainForm.position.width = 831;
  mainForm.position.height = 553;
 }catch(e){
  var gv_initWidth  = 831;  //업무화면 초기 width
  var gv_initHeight = 553;  //업무화면 초기 height
  mainForm.position.width = gv_initWidth;
  mainForm.position.height = gv_initHeight;
 }
}

/**
 * 사용자가 호출하는 리사이즈를 위한 종료작업
 *
 * @return sizes
*/
function smf_EndResize(obj){
 var mainForm = smf_ForceMainForm(obj);
 var nFrameWidth = mainForm.getOwnerFrame().position.width;
 var nFrameHeight = mainForm.getOwnerFrame().position.height;

 //frame과 form의 간격의 차이
  mainForm.position.width = nFrameWidth - 2;
  mainForm.position.height = nFrameHeight - 25;
}


/**
 * 폼을 강제로 닫는다. cancloseevent 발생안함.
 *
 * @return void
*/
function smf_ForceFormClose(form){
 form.enablevent = false;
 form.close();
}