PageSourceSearch

https://mobile.dkc.ac.kr/pnsadm2/public/sz.full.js

js dkc.ac.kr collected 2026-10-06 12:30:43 UTC 76,247 bytes, 2,733 lines download raw bytes

1/**
2 * 사이트 정보를 표시한다.<p>
3 */
4$(function() {
5	var site = {
6		headerTitle : "::PNS ADMIN-@::"
7		, mainTitle : "PNS Admin Page"
8		, copyright : "Copyright ⓒ 2018 - 2021 SOFTZAM CO., LTD. "
9		, telNo : "02-1544-7898" //문의 전화 
10		, pnsShortName : "PNS"	// 제품명 
11		, pnsVer : "v1.3"	// 제품 버전 
12		, topLogo : "../images/logo_lefttop.png"	// 로고 이미지 	
13		, loginLogo : "../images/login_logo.png"	// 로그인 로고 	
14		, siteId : ''
15		, env : {
16			// msg_01_01 메시지 등록 화면 관련 설정 정보 
17			'msg_01_01.file.use' : 'Y'	//첨부 파일 사용 여부 
18			, 'msg_01_01.file.maxsize' : 1024*1024*3	// 3MB 
19			, 'msg_01_01.file.allow' : ''	// 허용 파일 타입 
20			, 'msg_01_01.sel.org.use' : 'Y'	// 조직 검색 사용 
21			, 'msg_01_01.sel.user.menu' : [
22				{text:'교직원 조직도에서 사용자 선택', url:'../trgt/MSG_90_01.html'}
23				,{text:'학생 조직도에서 사용자 선택', url:'../trgt/MSG_90_06.html'}
24				, {text:'사용자 검색', url:'../trgt/MSG_90_03.html'}
25				, {text:'엑셀 업로드', url:'../trgt/MSG_90_05.html'}
26			]
27			, 'msg_01_01.sel.org.menu' : [
28				{text:'교직원 조직도에서 조직 선택', url:'../trgt/MSG_90_02.html'}
29				,{text:'학생 조직도에서 조직 선택', url:'../trgt/MSG_90_07.html'}
30				, {text:'조직 검색', url:'../trgt/MSG_90_04.html'}
31			]
32			, 'msg_01_01.sel.user.maxcnt' : 0	// 최대 선택 가능 사용자 
33			, 'msg_01_01.sel.org.maxcnt' : 0	// 최대 선택 가능 조직  
34		}	// 개별 사이트 환경 설정 
35	};	// end site
36	$.site = site;
37});
38
39/*!
40 * 제이쿼리를 확장한 공통 스크립트 파일 
41 */
42(function( $ ) {
43	/**
44	 * 콤보를 생성한다. <p>
45	 */
46	$.fn.makeCombo = function(cdGrpId, options) {
47		var cdGrp = cdGrpId || $(this).attr("cdGrp");
48		if( !cdGrp ) {
49			return;
50		}
51		var codes = fsp.code.getCode(cdGrp);
52		if( !codes ) {
53			return;
54		}
55		
56		var newCodes = codes.slice();
57		// select box 처리 
58		if( this.prop("tagName").toLowerCase() == "select") {
59			// 기존 정보 모두 삭제 
60			$(this).empty();
61			
62			if( options ) {
63				if( options.all !== undefined ) {
64					if ( options.all != "N" ) {
65					//	$(this).append("<option value=''>" + options.all + "</option>");
66						newCodes.insert(0, {CD : '', CD_NM : options.all});
67					}
68				} 
69			} else if( $(this).attr("all") !== undefined ){
70				if( $(this).attr("all") != "N" ) {
71					newCodes.insert(0, {CD : '', CD_NM : $(this).attr("all") });
72				}
73			} 
74						
75			$(this).kendoComboBox({
76				dataTextField : "CD_NM"
77        		, dataValueField: "CD"
78        		, dataSource : newCodes
79        		, valuePrimitive: true
80			});	// combo 생성 
81			
82		} else if( this.prop("tagName").toLowerCase() == "ul" ) {
83			$(this).html("");
84			
85			if( codes.length ) {
86				for(var i = 0; i < codes.length; i++ ) {
87					$(this).append("<li><input type='checkbox' value='" +  codes[i].CD + "'>" + codes[i].CD 
88							+ "-" + codes[i].CD_NM  + "</li>");
89				}
90			}
91		}
92	};// makeCombo
93	
94	/**
95	 * Form 객체를 입력받아서 JSON 객체로 생성한다. 
96	 */
97	$.fn.bindJSON = function(json) {
98		var self = this
99			patterns = {
100	            "key":      /[a-zA-Z0-9_]+|(?=\[\])/g
101	        };
102		
103		$(this)
104			.find("input:text, input:password, input:file, input:hidden, textarea, span, select")
105			.not("input:radio, input:checkbox")	// checkbox가 걸리는 문제 있음. 
106			.each(function() {
107				var n = $(this).attr("name");	// span때문에 this.name으로 사용하지 않음. 
108				if( n !== undefined ) {
109					var k, keys = n.match(patterns.key);
110					
111					if( keys && (k = keys.pop()) !== undefined) {
112						var str = json[0][k];
113						if( str == undefined || str == "null" || str == null ) {
114							str = "";
115						}
116						
117						// 2015.06.19 자리수 추가 
118						var len = $(this).attr("dispLen");
119						str = fsp.common.getFormatText(n, str, len);
120						
121						if( $(this).attr('data-role') == 'dropdowntree') {
122							$(this).data("kendoDropDownTree").value(str);
123						} else if( $(this).hasClass('sz-number') ) {
124							var kobj = $(this).data("kendoNumericTextBox");
125							kobj.value(str);
126						} else if( $(this).hasClass('sz-colorpicker') ) {
127							try {
128								var kobj = $(this).data("kendoColorPicker");
129								kobj.value(str);
130							} catch( e ) {
131								// 무시 
132							}		
133						} else if( this.tagName.toLowerCase() == "span") {
134							// 코드명 정보를  표시한다. 
135							if( $(this).attr("cdGrp")) {
136								// 추가한다. 
137								$(this).attr("orgVal", str);
138								$(this).text(fsp.code.getCodeName($(this).attr("cdGrp"), str));
139							} else {
140								$(this).text(str);
141							}
142						} else if ( this.tagName.toLowerCase() == 'select') {
143							$(this).val(str);
144							
145							var t = $(this).find('option:selected').text();
146							var combobox = $(this).data("kendoComboBox");
147							if ( combobox ) {
148								$('input[name="' + k + '_input"]').val( t );	// 명칭 화면 표시 위해 강제로. 버그??
149								combobox.value(str);
150							}	
151						} else {
152							$(this).val(str);
153						}
154					}
155				}
156			});
157		
158		
159		$(this)
160			.find("input:radio, input:checkbox")
161			.each(function() {
162				var k, keys = this.name.match(patterns.key);
163				if( keys && (k = keys.pop()) !== undefined) {
164					$(this).prop("checked", false);
165					// Y 면 true로 설정 value의 값이 지워지는 현상이 있음. 
166					if( $(this).val() == json[0][k] || 
167							(fsp.common.isEmpty($(this).val()) && json[0][k] == "1") ) {
168						// 값이 같거나, 값 설정이 안되어 있는 경우만 true 
169						$(this).prop("checked", true);
170					}
171				}
172			});
173	};	// end bindJSON
174		
175	$.fn.getJSON = function(options) {
176		var self = this,
177        json = {},
178        push_counters = {},
179        patterns = {
180            "validate": /^[a-zA-Z][a-zA-Z0-9_]*(?:\[(?:\d*|[a-zA-Z0-9_]+)\])*$/,
181            "key":      /[a-zA-Z0-9_]+|(?=\[\])/g,
182            "push":     /^$/,
183            "fixed":    /^\d+$/,
184            "named":    /^[a-zA-Z0-9_]+$/
185        };
186	    this.build = function(base, key, value){
187	        base[key] = value;
188	        return base;
189	    };
190	    this.push_counter = function(key){
191	        if(push_counters[key] === undefined){
192	            push_counters[key] = 0;
193	        }
194	        return push_counters[key]++;
195	    };
196	    var failed = false;
197	  
198	    $(this).find('input:text, input:password, input:file, input:hidden, select, textarea')
199	    	.each(function() {
200	    	var spanName = $(this).attr("name");
201	    	if( spanName !== undefined ) {
202	    		if( $(this).hasClass('radio') ) {	// 라디오box 제외.
203					return;
204	        	}
205				// 규칙이 맞는지 확인하여, 맞지 않을 경우 skip한다. 
206		        if(!patterns.validate.test(spanName)){
207		            return;
208		        }
209		        var k,
210		            keys = spanName.match(patterns.key),
211		            merge = '', 	// 원본 값 
212		            reverse_key = spanName;
213		      
214		        while((k = keys.pop()) !== undefined){
215		            reverse_key = reverse_key.replace(new RegExp("\\[" + k + "\\]$"), '');
216		            
217		            // 체크 옵션이 존재하는 경우 
218		            if( !options ||  ( options.validation && options.validation !== false) ) {
219		            	if( !$(this).isValid() ) {
220		            		failed = true;
221		            		return false;
222		            	}
223		            }
224		            
225		            merge = $(this).getValue();
226		            
227		            if(k.match(patterns.push)){ // push
228		                merge = self.build([], sel.push_counter(reverse_key), merge);
229		            } else if(k.match(patterns.fixed)){ // fixed
230		                merge = self.build([], k, merge);
231		            } else if(k.match(patterns.named)){ // named
232		                merge = self.build({}, k, merge);
233		            }
234		        }
235		        
236		        json = $.extend(true, json, merge);
237			}
238	    });
239	    
240	    // 폼 객체 아래의 정보를 추가한다. span은 form에서 제공하지 않아서 추가한다. 
241	    $(this).find("span, p, strong").each(function() {
242			var spanName = $(this).attr("name");	 
243			if( spanName !== undefined ) {
244				// 규칙이 맞는지 확인하여, 맞지 않을 경우 skip한다. 
245		        if(!patterns.validate.test(spanName)){
246		            return;
247		        }
248		
249		        var k,
250		            keys = spanName.match(patterns.key),
251		            merge = $(this).getValue(),
252		            reverse_key = spanName;
253				
254		        while((k = keys.pop()) !== undefined){
255		            reverse_key = reverse_key.replace(new RegExp("\\[" + k + "\\]$"), '');
256				            
257		            if(k.match(patterns.push)){ // push
258		                merge = self.build([], self.push_counter(reverse_key), merge);
259		            } else if(k.match(patterns.fixed)){ // fixed
260		                merge = self.build([], k, merge);
261		            } else if(k.match(patterns.named)){ // named
262		                merge = self.build({}, k, merge);
263		            }
264		        }
265		        json = $.extend(true, json, merge);
266			}
267		});
268	    
269	    // 폼 객체 아래의 정보를 추가한다. checkbox 값은 처리하지 않는다. 
270	    $(this).find("input:checkbox, input:radio").each(function() {
271			var spanName = $(this).attr("name");	 
272			if( spanName !== undefined ) {
273				// 규칙이 맞는지 확인하여, 맞지 않을 경우 skip한다. 
274		        if(!patterns.validate.test(spanName)){
275		            return;
276		        }
277		        
278		        var k,
279		            keys = spanName.match(patterns.key),
280		            merge = $(this).is(":checked") ? 
281		            			( fsp.common.isEmpty($(this).val()) ? "1" : $(this).val())
282		            			: ($(this).attr("false") ? $(this).attr("false"): "0"), 
283		            reverse_key = spanName;
284		            
285		        while((k = keys.pop()) !== undefined){
286		            reverse_key = reverse_key.replace(new RegExp("\\[" + k + "\\]$"), '');
287		            
288		            if(k.match(patterns.push)){ // push
289		                merge = self.build([], self.push_counter(reverse_key), merge);
290		            } else if(k.match(patterns.fixed)){ // fixed
291		                merge = self.build([], k, merge);
292		            } else if(k.match(patterns.named)){ // named
293		                merge = self.build({}, k, merge);
294		            }
295		        }
296		        json = $.extend(true, json, merge);
297			}
298		});
299		
300	    return failed ? null : json;
301	};	// end getJSON
302		
303	// 값을 초기화 한다. 
304	$.fn.clearValue = function() {
305		if( !$(this).length ) {
306			return;
307		}
308		var defValue = $(this).attr('default');
309		if( fsp.common.isEmpty(defValue)) {
310			defValue = null;
311		}
312		$(this).setValue(defValue ? defValue : '');
313	};
314	
315	// 값을 설정한다. 
316	$.fn.setValue = function(value) {
317		var len = $(this).attr("dispLen");
318		var fmt = $(this).attr('fmt');
319		if( fmt ) {
320			value = fsp.common.getFormatText(fmt, value, len);	
321		}
322						
323		var tagname = $(this).prop("tagName").toLowerCase();
324		if( tagname == "p" || tagname == "span" || tagname == "strong" ) {
325			if( $(this).attr("cdGrp") ) {	// 코드 값 존재시 검색 후 코드명으로 변경 처리 
326				$(this).attr("orgVal", value);
327				value = fsp.code.getCodeName($(this).attr("cdGrp"), value);
328			} else if( $(this).attr("trueValue") || $(this).attr("falseValue") ) {
329				$(this).attr("orgVal", value);
330				
331				let hidden = $(this).attr("show") == 'Y';
332				
333				if( value == "1" || value == "Y" ) {
334					value = $(this).attr("trueValue");
335					if( hidden ) {
336						$(this).show();
337					}
338				} else {
339					value = $(this).attr("falseValue");
340					if( hidden ) {
341						$(this).hide();
342					}
343				}
344			}
345			$(this).html(value);
346		} else {			
347			$(this).val(value);
348		}	
349	};
350	
351	//  값을 가져온다. 
352	$.fn.getValue = function() {	
353		var tagname = $(this).prop("tagName").toLowerCase();
354		var value = null;
355		
356		if( tagname == "p" || tagname == "span" || tagname == "strong" ) {
357			value = ( ( $(this).attr("cdGrp") || $(this).attr("trueValue") || $(this).attr("falseValue")) 
358							&& $(this).attr("orgVal"))
359		            	? $(this).attr("orgVal") :  $(this).html();	// $(this).text()
360		} else {			
361			value = $(this).val();
362		}	
363		var fmt = $(this).attr('fmt');
364		if( fmt ) {
365			value = fsp.common.getOriginText(fmt, value);	
366		} else if( $(this).hasClass('datepicker') || $(this).hasClass('monthpicker')) {
367			// 포맷 없이 날짜인 경우 
368			value = fsp.common.getOriginText('ymd', value);
369		} 
370		
371		return value;
372	};
373	
374	$.fn.isValid = function() {	
375		var value = $(this).val();
376		var required = $(this).attr('required') !== undefined;
377		// 1.필수 입력 확인 
378        if( required  ) {
379        	if( fsp.common.isEmpty(value) ) {
380        		var spanName = $(this).attr("name");
381        		var labelObj = spanName !== undefined ? 
382        				$("label[for='" + spanName + "'], label[for='" + spanName + "'] *")
383        				: undefined;
384        		if( labelObj && labelObj.text()) {
385        			$(this).showWarn(fsp.message.getMessage('CW00001', labelObj.text()));
386        		} else {
387        			$(this).showWarn(fsp.message.getMessage('CW00002'));
388        		}
389        		$(this).focus();
390        		return false;
391        	}
392        }
393        
394        // 2. 규격에 맞는 데이터 입력 검증 
395        if( $(this).attr('bmaxlength') !== undefined ) {	//최대자리수
396        	var max = -1;
397        	try {
398        		max =  parseInt($(this).attr('bmaxlength'));
399        	}catch( e ){}
400        	if( max > 0 && fsp.common.blength(value) > max) {
401        		$(this).showWarn(fsp.message.getMessage('CW00011', max+""));
402        		$(this).focus();
403        		return false;
404        	}
405        }
406        if( $(this).attr('bminlength') !== undefined ) {	//최소자리수
407        	var min = -1;
408        	try {
409        		min =  parseInt($(this).attr('bminlength'));
410        	}catch( e ){}
411        	if( min > 0 && fsp.common.blength(value) < min) {
412        		$(this).showWarn(fsp.message.getMessage('CW00014',min+""));
413        		$(this).focus();
414        		return false;
415        	}
416        }
417        if( $(this).attr('fmt') !== undefined ) {	//형식
418        	if( !fsp.common.isValidFormat(value, $(this).attr('fmt'))) {
419        		$(this).showWarn(fsp.message.getMessage('CW00010', ''));
420        		$(this).focus();
421        		return false;
422        	}
423        }
424        return true;
425	};
426		
427	/**
428	 * 입력 정보를 모두 초기화한다. 
429	 */
430	$.fn.clearData = function() {
431		var self = this
432			patterns = {
433	            "key":      /[a-zA-Z0-9_]+|(?=\[\])/g
434	        };
435		$(this)
436			.find("input:text, input:password, input:file, input:hidden, select, textarea, span")
437			.not("input:radio, input:checkbox")	// checkbox가 걸리는 문제 있음. 
438			.each(function() {
439				var n = $(this).attr("name");	// span때문에 this.name으로 사용하지 않음. 
440				if( n !== undefined ) {
441					var k, keys = n.match(patterns.key);
442					if( keys && (k = keys.pop()) !== undefined) {
443						if( this.tagName.toLowerCase() == "span") {
444							$(this).html("");
445						} else if( $(this).hasClass('sz-number') ) {
446							var kobj = $(this).data("kendoNumericTextBox");
447							kobj.value("0");
448						} else {
449							$(this).val("");
450						}
451					}
452				}
453		});
454		
455		$(this)
456			.find("input:radio, input:checkbox")
457			.each(function() {
458				var k, keys = this.name.match(patterns.key);
459				if( keys && (k = keys.pop()) !== undefined) {
460					// attr로 처리하면 안됨. 
461					$(this).prop("checked", false);
462				}
463			});
464	};	// end clearData
465		
466	/**
467	 * 정보성 메시지 창을 표시한다. 
468	 */
469	$.fn.showInfo = function(msg, options) {
470		fsp.message.showMsg2(msg, 'info');
471	};
472	/**
473	 * 정보성 메시지 창을 표시한다. 
474	 */
475	$.fn.showWarn = function(msg, options) {
476		fsp.message.showMsg2(msg, 'warning');
477	};
478	
479	/**
480	 * 정보성 메시지 창을 표시한다. 
481	 */
482	$.fn.showError = function(msg, options) {
483		fsp.message.showMsg2(msg, 'error');
484	};
485	
486	/**
487	 * 정보성 메시지 창을 표시한다. 
488	 */
489	$.fn.showOk = function(msg, options) {
490		fsp.message.showMsg2(msg, 'success');
491	};
492
493}( jQuery ));
494/**
495 * fsp 사용 준비.
496 */
497if ( typeof fsp === "undefined") {
498	var fsp = {		
499	};
500}
501
502/** 
503 * 처리 요청 목록 정보.<p>
504 */
505fsp.fsp_ds_cmd = [];
506
507/**
508 * 처리할 서버 url.<p>
509 */
510fsp.serverUrl = '/padmsvc';
511
512/**
513*	Biz Name 
514**/
515fsp.FSP_CURR_BIZ = "";
516
517/**
518 * 접속하는 서버 경로를 변경한다.<p>
519 */
520fsp.changeServerUrl = function(strUrl) {
521	if( strUrl ) {
522		fsp.serverUrl = strUrl;
523	}
524};
525
526// context path 고려해서 경로 수정 21.1.22
527if( window.location.pathname ) {
528	var currPath = window.location.pathname;
529	if( !currPath.startsWith('/pnsadm2')	) {
530		var contextPath = currPath.substr(0, currPath.indexOf('/pnsadm2'));
531		fsp.serverUrl = contextPath + fsp.serverUrl;
532	}
533}
534
535// 초기화 
536fsp.clear = function() {
537	fsp.FSP_CURR_BIZ = "";
538	fsp.fsp_ds_cmd = [];
539};
540fsp.setBiz = function(newBizName) {	// biz 설정 
541	fsp.FSP_CURR_BIZ = newBizName;
542};
543fsp.clearBiz = function() {
544	fsp.FSP_CURR_BIZ = "";
545};
546/**
547	* 자바 코딩 없이 사용할 경우 서버에서 처리하기 위한 Action 호출 
548	* 정보를 추가
549	* @param txType 호출하는 Transaction 타입 
550	         S - Insert, Update , Delete 동시 처리 
551	         M - 주어진 SQL 정보에 대해 Dataset이 존재하는 건 만큼 반복처리 
552	         CSV - 조회용 SQL 만 가능하며, 데이터 조회 후 CSV 형태로 데이터를 넘겨 줌.
553	* @param sqlName 처리할 Main SQL ID
554	                 txType이 'S'인 경우에는 해당 sql은 사용되지 않는다. 
555	* @param keySqlName PK 조회용 SQL ID 
556	* @param keyIncrement keySqlName이 존재할 경우만 사용되며, 
557	                      반복적인 처리 Sql의 경우 다음 정보 key 증가 값 
558	                      현재는 integer 형의 숫자형 key만 처리 가능 
559	* @param callbackSql 처리가 완료된 후 결과로써 반환할 Sql Id 
560	* @param insertSql  txType이 'S'인 경우 또는 sqlName 파라메터가 존재하면서 해당하는 
561	                    main sql에 대해서 master-detail 구조일 경우 
562	                    insert를 처리할 SQL ID 
563	* @param updateSql  txType이 'S'인 경우 또는 sqlName 파라메터가 존재하면서 해당하는 
564	                    main sql에 대해서 master-detail 구조일 경우 
565	                    update를 처리할 SQL ID            
566	* @param deleteSql  txType이 'S'인 경우 또는 sqlName 파라메터가 존재하면서 해당하는 
567	                    main sql에 대해서 master-detail 구조일 경우 
568	                    delete를 처리할 SQL ID     
569	* @param saveFlagColumn  insertSql, updateSql, deleteSql이 존재하여 신규, 수정, 삭제를 
570	                         처리할 경우 기본적으로 Dataset의 getRowStates()로 판단하여 처리
571	                         하나 이를 Flag 컬럼으로 대체하고 싶은 경우 사용
572	                         해당 Flag 컬럼의 값은 신규 - I , 수정 - U , 삭제 - D의 값을 
573	                         가져야 한다. 
574	* @return 없음
575 */
576fsp.add = function(txType
577 		, sqlName
578 		, keySqlName
579 		, keyIncrement
580 		, callbackSql
581	    , insertSql
582	    , updateSql
583	    , deleteSql
584	    , saveFlagColumn
585	    , keyZeroLen
586	    , execType
587	    , otherOptions ) {
588	var userInput = 'N';
589	var userOrder = 'Y';
590	if( otherOptions ) {
591		if( otherOptions.isUseInput ) {
592			userInput = "Y";
593		}
594		if( otherOptions.isNotUseOrder) {
595			userOrder = "N";
596		}
597	}
598	var obj = {
599			TYPE : txType
600			, SQL_ID : sqlName
601			, KEY_SQL_ID : keySqlName
602			, KEY_INCREMENT : keyIncrement
603			, CALLBACK_SQL_ID : callbackSql
604			, INSERT_SQL_ID : insertSql
605			, UPDATE_SQL_ID : updateSql
606			, DELETE_SQL_ID : deleteSql
607			, SAVE_FLAG_COLUMN : saveFlagColumn
608			, KEY_ZERO_LEN : keyZeroLen
609			, EXEC_TYPE : ( fsp.isUndefined(execType), "B", execType)
610			, USE_INPUT : userInput
611			, USE_ORDER : userOrder
612			, BIZ_NAME : fsp.FSP_CURR_BIZ	//20.11.08
613			// return 되는 정보 
614			, EXEC : ""
615			, FAIL : ""
616			, FAIL_MSG : ""
617			, EXEC_CNT : 0
618			, MSG : ""
619			, READ_ALL : ''
620			, PAGE_NO : ''
621			, PAGE_SIZE: ''
622	};
623	
624	fsp.fsp_ds_cmd.push(obj);
625};
626
627/*****************************************************************
628* 조회용 Action 정보 생성 ( 자바 코드 없이 사용할 경우 )
629* @param sqlName 조회할 SQL ID 
630* @param isUseInput 조회한 결과는 Input Dataset으로 사용할지 여부 
631* @param isNotUseOrder Dataset 컬럼 생성시 정렬할지 여부  
632* @return 없음
633******************************************************************/
634fsp.addSearch = function (sqlName, isUseInput, isNotUseOrder, rtnDsId) {
635	var otherOptions = {
636			isUseInput : ( !isUseInput || isUseInput == false ? false : true )
637			, isNotUseOrder :( !isNotUseOrder || isNotUseOrder == false ? false : true)
638	};
639    
640	fsp.add("N" // txType
641			, sqlName // sqlName 
642			, "" // keySqlName	
643	        , ""	// keyIncrement
644	        , ""	// callbackSql
645	        , ""	// insertSql
646	        , ""	// updateSql
647	        , ""	// deleteSql
648	        , rtnDsId	// saveFlagColumn
649	        , null 		// keyZeroLen
650	        , null		// execType
651	        , otherOptions
652	);
653};
654/*****************************************************************
655* 페이지 단위 조회용 Action 정보 생성 ( 자바 코드 없이 사용할 경우 )
656* @param sqlName 조회할 SQL ID 
657* @param pageNo 페이지 번호 
658* @param pageSize 페이지 사이즈 
659* @param isAllRead 주어진 페이지 이후로 전체 데이터를 읽을지 유무 
660* @param isUseInput 조회한 결과는 Input Dataset으로 사용할지 여부 
661* @param isNotUseOrder Dataset 컬럼 생성시 정렬할지 여부  
662* @return 없음
663******************************************************************/
664fsp.addPageSearch = function( sqlName, pageNo, pageSize, isAllRead, isUseInput, isNotUseOrder ) {
665	fsp.addSearch(sqlName, isUseInput, isNotUseOrder );
666	
667	var obj = fsp.fsp_ds_cmd[fsp.fsp_ds_cmd.length-1];
668	if( isAllRead === true ) {
669		obj["READ_ALL"] = "Y";
670	} else {
671		obj["READ_ALL"] = "N";
672	}
673	
674	obj["PAGE_NO"] = pageNo;
675	obj["PAGE_SIZE"] = pageSize;
676	fsp.fsp_ds_cmd[fsp.fsp_ds_cmd.length-1] = obj;
677};
678
679/*****************************************************************
680* 단일 처리 Action 정보 생성 
681* @param sqlName 단일 처리 SQL ID 
682* @param keySqlName PK 조회용 SQL ID 
683* @param keyIncrement keySqlName이 존재할 경우만 사용되며, 
684                      반복적인 처리 Sql의 경우 다음 정보 key 증가 값 
685                      현재는 integer 형의 숫자형 key만 처리 가능 
686* @param callbackSql 처리가 완료된 후 결과로써 반환할 Sql Id 
687* @param execType 처리해야 하는 실행타입 정의 ( B - 배치 , N - 한건 씩 처리 )
688* @return 없음
689******************************************************************/
690fsp.addSingle = function(
691		sqlName
692		, keySqlName
693		, keyIncrement
694		, callbackSql
695		, execType) {
696	
697	fsp.add("N" // txType
698			, sqlName // sqlName 
699			, keySqlName // keySqlName	
700	        , keyIncrement	// keyIncrement
701	        , callbackSql	// callbackSql
702	        , ""	// insertSql
703	        , ""	// updateSql
704	        , ""	// deleteSql
705	        , ""	// saveFlagColumn
706	        , "" 		// keyZeroLen
707	        , execType		// execType
708	);
709};
710
711/*****************************************************************
712* 주어진 SQL을 Dataset 건수 만큼 반복적으로 처리하는 action 정보 생성 
713* @param sqlName  처리할 SQL 
714* @param keySqlName  Key SQL이 존재할 경우 SQL ID          
715* @param keyIncrement  Key 증가 값 
716* @param callbackSql  처리 완료 후 반환할 정보를 조회할 SQL ID   
717* @param keyZeroLen  Key 에 0 채워야 할 경우 0 의 갯수 
718* @param execType 처리해야 하는 실행타입 정의 ( B - 배치 , N - 한건 씩 처리 )
719* @return 없음
720******************************************************************/
721fsp.addMulti = function(
722		sqlName
723		, keySqlName
724		, keyIncrement
725		, callbackSql
726		, keyZeroLen
727		, execType) {
728	
729	fsp.add("M" // txType
730			, sqlName // sqlName 
731			, keySqlName // keySqlName	
732	        , keyIncrement	// keyIncrement
733	        , callbackSql	// callbackSql
734	        , ""	// insertSql
735	        , ""	// updateSql
736	        , ""	// deleteSql
737	        , ""	// saveFlagColumn
738	        , keyZeroLen 		// keyZeroLen
739	        , execType		// execType
740	);
741};
742
743/*****************************************************************
744* SaveAction 정보 생성 ( getRowType() 또는 주어진 Flat 컬럼의 값을 읽어 
745  등록, 수정, 삭제를 처리는 SQL 요청 정보 생성 )
746* @param insertSql  txType이 'S'인 경우 또는 sqlName 파라메터가 존재하면서 해당하는 
747                    main sql에 대해서 master-detail 구조일 경우 
748                    insert를 처리할 SQL ID 
749* @param updateSql  txType이 'S'인 경우 또는 sqlName 파라메터가 존재하면서 해당하는 
750                    main sql에 대해서 master-detail 구조일 경우 
751                    update를 처리할 SQL ID            
752* @param deleteSql  txType이 'S'인 경우 또는 sqlName 파라메터가 존재하면서 해당하는 
753                    main sql에 대해서 master-detail 구조일 경우 
754                    delete를 처리할 SQL ID     
755* @param saveFlagColumn  insertSql, updateSql, deleteSql이 존재하여 신규, 수정, 삭제를 
756                         처리할 경우 기본적으로 Dataset의 getRowStates()로 판단하여 처리
757                         하나 이를 Flag 컬럼으로 대체하고 싶은 경우 사용
758                         해당 Flag 컬럼의 값은 신규 - I , 수정 - U , 삭제 - D의 값을 
759                         가져야 한다. 
760* @param keySqlName PK 조회용 SQL ID 
761* @param keyIncrement keySqlName이 존재할 경우만 사용되며, 
762                      반복적인 처리 Sql의 경우 다음 정보 key 증가 값 
763                      현재는 integer 형의 숫자형 key만 처리 가능 
764* @param callbackSql 처리가 완료된 후 결과로써 반환할 Sql Id      
765* @param keyZeroLen  Key 에 0 채워야 할 경우 0 의 갯수         
766* @param execType 처리해야 하는 실행타입 정의 ( B - 배치 , N - 한건 씩 처리 )
767* @return 없음
768******************************************************************/
769fsp.addSave = function(
770		insertSql
771		, updateSql
772	    , deleteSql
773	    , saveFlagColumn
774	    , keySqlName
775	    , keyIncrement
776	    , callbackSql
777	    , keyZeroLen
778	    , execType) {
779	
780	fsp.add("S" // txType
781			, "" // sqlName 
782			, keySqlName // keySqlName	
783	        , keyIncrement	// keyIncrement
784	        , callbackSql	// callbackSql
785	        , insertSql	// insertSql
786	        , updateSql	// updateSql
787	        , deleteSql	// deleteSql
788	        , saveFlagColumn	// saveFlagColumn
789	        , keyZeroLen 		// keyZeroLen
790	        , execType		// execType
791	);
792};
793/**
794 * 
795 * @param actionName
796 * @param cmdName
797 * @param data
798 * @param callbackFnc
799 * @param isErrorNotCall
800 * @param isNotShow
801 * @param isSync
802 * @param trId
803 */
804fsp.callService = function (
805 		actionName
806        , cmdName
807        , data
808        , callbackFnc
809        , isErrorNotCall 
810        , isNotShow
811        , isSync) {
812	
813	// 데이터를 추가한다. 
814	if( fsp.fsp_ds_cmd.length > 0 ) {
815		data["fsp_ds_cmd"] = fsp.fsp_ds_cmd;
816	}
817	
818	if( !actionName || actionName == "") {
819		actionName = "pns#jdefault";
820	}
821	if( !cmdName || cmdName == "") {
822		cmdName = "execute";
823	}
824
825	data["fsp_action"] = actionName;
826	data["fsp_cmd"] = cmdName;
827	$.ajax({
828		type : 'POST'
829		, url : fsp.serverUrl
830		, cache : false
831		, data : $.toJSON(data)
832		, dataType : 'json'
833		, timeout : 60 * 1000	// 60 초 
834		, contentType : 'application/x-www-form-urlencoded; charset=utf-8'
835	//	, success : successFnc// end success
836	//	, error : errorFnc // end error 
837		, complete : function(xhr,textStatus) {
838			fsp.watingLoad(false);
839			var status =  xhr.status;
840			
841			if( status == 200 ) {
842				eval("var msg = " +  xhr.responseText + ";");
843				var errorCode =  msg.ErrorCode;
844				if( errorCode != 0 ) {
845					if( errorCode <= -100 && errorCode >= -199 ) {
846						// Business Exception 처리 : 메시지 처리하지 않음. 
847					} else if( errorCode == -200) {
848						// 로그인이 session 이 끈어진 경우 
849			            fsp.fn_notLogin();
850			            return;
851					} else {
852						if(msg.errorTrace) {
853						//	fsp.fn_showSysError( msg.errorTrace );
854							fsp.fn_showSysError("처리 도중 오류가 발생했습니다.\n잠시후 다시 처리해주십시오.");
855						} else {
856						  if( errorCode == "1001" ) {
857						    fsp.fn_notLogin();
858						  } else {
859							  fsp.fn_showSysError( "코드:" + errorCode + "\n메시지:" + msg.ErrorMsg );
860						  } 
861						} 
862					}
863					
864					// 호출하기 
865					if( isErrorNotCall !== true ) {
866						if( callbackFnc ) {
867							callbackFnc(errorCode, msg);
868						}
869					}
870				} else {
871					// 처리 성공 
872					if( callbackFnc ) {
873						callbackFnc(errorCode, msg);
874					}
875				}	
876			} else {
877				// 처리 실패 
878				if( status == 405 ) {
879					fsp.fn_notLogin();
880				} else if( status == 12029 ) { // 12029 : server shutdown 
881					alert("서버에 접속할 수 없습니다. 잠시 후 다시 요청해 주십시오.");
882				} else if( status == 404 ) {
883					alert("서버 접속 경로가 잘못 되었습니다. 경로를 확인해 주십시오.");
884				} else {
885					alert("서버 접속 중 처리에 실패했습니다.:" + status);
886				}
887			}	
888		}	// end compelte
889		, beforeSend : function() {
890			if( !isNotShow ) {
891				fsp.watingLoad(true);	
892			}	
893		}	// end beforeSend
894	});
895};
896
897
898/**
899 * 
900 * @param actionName
901 * @param cmdName
902 * @param data
903 * @param callbackFnc
904 * @param isErrorNotCall
905 * @param isNotShow
906 * @param isSync
907 * @param trId
908 */
909fsp.callPage = function (
910 		actionName
911        , cmdName
912        , data
913        , source
914        , target
915        , callbackFnc
916        , isDataAdd
917        , isNotShow
918) {
919	if(isDataAdd) {
920		target.empty();
921	}
922	
923	// 데이터를 추가한다. 
924	if( fsp.fsp_ds_cmd.length > 0 ) {
925		data["fsp_ds_cmd"] = fsp.fsp_ds_cmd;
926	}
927	
928	if( !actionName || actionName == "") {
929		actionName = "cocoa#jDefaultAction";
930	}
931	
932	if( !cmdName || cmdName == "") {
933		cmdName = "execute";
934	}
935
936	data["fsp_action"] = actionName;
937	data["fsp_cmd"] = cmdName;
938	data["fsp_webPage"] = source;
939	$.ajax({
940		type : 'POST'
941		, url : fsp.serverUrl
942		, cache : false
943		, data : $.toJSON(data)
944		, dataType : 'html'
945		, contentType : 'application/x-www-form-urlencoded; charset=utf-8'
946		, success: function(data){ 
947			fsp.watingLoad(false);
948			if(isDataAdd){
949				target.html(data);	
950			}
951			if( callbackFnc ) {
952				callbackFnc(data);
953			}
954		}
955    	,error: function(data) { 
956    		fsp.watingLoad(false);
957    		var status = data.status;
958    		if( status == 405 ) {
959				fsp.fn_notLogin();
960			} else if( status == 12029 ) { // 12029 : server shutdown 
961				alert("서버에 접속할 수 없습니다. 잠시 후 다시 요청해 주십시오.");
962			} else if( status == 404 ) {
963				alert("서버 접속 경로가 잘못 되었습니다. 경로를 확인해 주십시오.");
964			} else {
965				alert("서버 접속 중 처리에 실패했습니다.:" + status);
966			} 
967    	}, beforeSend : function() {
968    		if( !isNotShow ) {
969    			fsp.watingLoad(true);
970    		}	
971		}	// end beforeSend
972	});
973};
974
975
976/******************************************************
977*  로그인이 안되어 있을 경우 처리 
978******************************************************/
979fsp.fn_notLogin = function() {
980	try {
981		if( fsp.message ) {
982			fsp.message.alert("LI00001");
983		} else {
984			alert('로그인이 필요합니다. 로그인 후 사용해 주십시오.');
985		}
986		
987		// 기본 경로로 이동한다. 
988		window.location.href = '../main/login.html';
989	} catch( e ) {
990		window.location = "/index.html";
991	}	
992};
993
994/******************************************************
995* 서버에서 처리한 후의 에러 메시지 창 표시 
996******************************************************/
997fsp.fn_showSysError = function( sMsg ) {
998	try {
999		eval("fsp_fn_showSysError( sMsg );");
1000	} catch( e ) {
1001		alert( sMsg );
1002	}
1003};
1004
1005/**
1006 * 처리중입니다. 메시지 표시.<p>
1007 * 
1008 * @param state true 처리중입니다. 표시 
1009 */
1010fsp.watingLoad = function(state) {
1011	var wating = $("#div_watingLoad");
1012	if(!wating.html()) {
1013		var obj = $("<div id='div_watingLoad' style='position:fixed; left:0; top:0; background-color:rgba(0,0,0,0.20); z-index:9999; width:100%; height:100%; display:none;'>" + 
1014					"<p style='text-align:center; padding:0 0 0 0; left:45%; top:45%; position:absolute;'>" + 
1015					"<i class='fas fa-spinner fa-lg'></i> 처리중입니다." + 
1016					"</p>" +
1017					"</div>");
1018		
1019		$(document.body).prepend(obj);
1020		wating = $("#div_watingLoad");
1021	}
1022	
1023	// 처리 중 이미지를 표시한다. 
1024	if (state) {
1025		/*
1026		let oldZ = wating.css('z-index');
1027		var maxZ = fsp.common.getTopZindex();
1028		if( (oldZ + 10) != maxZ ) {	// 다르면
1029			wating.css('z-index', maxZ);
1030		}		
1031		*/
1032		wating.show(); 
1033	} else {
1034		wating.hide();
1035	}
1036};	// end watingLoad www
1037
1038/**
1039 * 쿠키 정보를 가져온다. 
1040 */
1041fsp.getCookie = function(cname) {
1042    var name = cname + "=";
1043    var ca = document.cookie.split(';');
1044    for(var i = 0; i <ca.length; i++) {
1045        var c = ca[i];
1046        while (c.charAt(0)==' ') {
1047            c = c.substring(1);
1048        }
1049        if (c.indexOf(name) == 0) {
1050            return c.substring(name.length,c.length);
1051        }
1052    }
1053    return "";
1054}
1055
1056/**
1057 * 주어진 정보가 null 인지를 확인한다.<p>
1058 * @param obj
1059 * @return null 일 경우  true 
1060 * @returns {Boolean}
1061 */
1062fsp.isUndefined = function(obj) {
1063	return obj === undefined;
1064};
1065/**
1066 * 객체를 추가한다.<p>
1067 * @param func
1068 */
1069fsp.addConstructor = function(func) {    
1070    try {
1071        func();
1072    } catch(e) {
1073        //console.log("Failed to run constructor: " + e);
1074    }
1075};
1076/**
1077*	메시지 관련 JS.
1078**/
1079var fsp_message = function() {
1080
1081};
1082/**
1083 * 메시지 정보.<p>
1084 */
1085fsp_message.codes = {
1086	CI00001 : "저장 하시겠습니까?"
1087	, CI00002 : "삭제 하시겠습니까?"
1088	, CI00003 : "승인 하시겠습니까?"
1089	, CI00004 : "신규 입력 상태입니다. 자료를 입력해 주십시오."
1090	, CI00005 : "처리 하시겠습니까?"	
1091	, CI00011 : "화면을 초기화했습니다."
1092	, CI00021 : "조회를 완료했습니다."
1093	, CI00022 : "조회를 완료했습니다.[조회건수 : ${}]"
1094	, CI00023 :	"처리를 완료했습니다."
1095	, CI00024 :	"삭제를 완료했습니다."	
1096	, CI00025 :	"처리를 요청했습니다. 잠시 후 재조회하여 주십시오."
1097	, CI00030 :	"메시지 발송을 완료했습니다.[메시지번호:${}]"
1098	, CI00031 :	"전송 대상 [${}]\n메시지를 발송하시겠습니까?"
1099	, CI00032 :	"시간[${}]으로 메시지 발송을 예약 하시겠습니까?"
1100	, CI00033 :	"알림통에 사용자[${} 명]을 추가 하시겠습니까?"
1101	, CI00034 :	"사용자[${}]를 알림통 구독자에서 삭제 하시겠습니까?"
1102	, CI00035 :	"하위 컬럼이 존재합니다. 컬럼을 먼저 삭제 후 처리해 주십시오."
1103	, CI00036 :	"알림통 [${}]\n메시지를 발송하시겠습니까?"
1104	, CW00001 : "[${}] 값을 입력하여 주십시오."
1105	, CW00002 : "값을 입력하여 주십시오."
1106	, CW00003 : "처리할 항목을 선택해주십시오."
1107	, CW00004 : "${} 자리로 입력해 주십시오."
1108	, CW00005 : "변경된 정보가 없습니다."
1109	, CW00006 : "선택한 항목이 없습니다. 선택하여 주십시오."
1110	, CW00007 : "이미 선택한 항목은 추가히지 않습니다:${}"
1111	, CW00008 : "숫자만 입력 가능합니다."
1112	, CW00009 : "${}~${} 사이의 값만 입력 가능합니다."	
1113	, CW00010 : "값의 형식이 잘못되었습니다.${}와 같은 형태로 입력해 주십시오."	
1114	, CW00011 : "문자길이(${}) 이하의 값을 입력하세요."	
1115	, CW00012 : "허용되지 않는 파일 확장자입니다. \n올바른 확장자 : [${}] "
1116	, CW00013 : "처리할 정보가 없습니다."
1117	, CW00014 : "문자길이(${}) 이상의 값을 입력하세요."	
1118	, CW00021 : "해당 데이터가 존재하지 않습니다."
1119	, CW00022 : "표시할 정보가 없습니다."
1120	, CW00030 : "메시지 내용을 작성해 주십시오."	
1121	, CW00031 : "메시지 수신 대상자(사용자,조직)을 선택해 주십시오."	
1122	, CW00032 : "전송할 알림통을 선택해 주십시오."	
1123	, CW00033 : "지원하지 않는 파일 형식입니다. 다른 파일ì
1123„ 첨부해 주십시오."	
1124	, CW00034 : "첨부하는 파일 최대 크기는 (${} KB) 입니다."
1125	, LI00001 : "로그인 정보가 없습니다. 로그인 후 이용해 주십시오."
1126	, LI00002 :	"로그인을 완료했습니다."	
1127	, CI00100 : "잘못된 접근입니다. 메뉴정보를 확인해 주십시오.[${}]"
1128	, CWORG01 : "하위 조직정보가 존재합니다. 하위 조직정보를 삭제 또는 사용불가 처리 먼저 해주십시오."
1129	, CWMNU01 : "하위 메뉴가 존재합니다. 하위 메뉴를 삭제 또는 사용불가 처리 먼저 해주십시오."
1130	, CWMNU02 : "사용불가 처리된 메뉴는 하위 메뉴 추가를 할 수 없습니다. 메뉴를 사용가능 상태로 수정 해주십시오."	
1131	, CWMNU03 : "선택된 하위메뉴가 있습니다. 하위메뉴를 먼저 체크해제 해야합니다."			
1132	, CWUSR01 : "입력한 사용자 아이디는 이미 사용중입니다."
1133	, CWUSR02 : "입력한 이전 비밀번호가 일치하지 않습니다. 확인하여 주십시오."
1134	, CWCOM01 : "입력한 아이디는 이미 사용중입니다."
1135	, CWNTC01 : "공지대상 클럽을 선택해 주십시오."
1136	, CWMSG01 : "메시지 수신 사용자를 선택해 주십시오."	
1137	, SW00001 : "서비스 처리 상세 정보를 최소 1건 이상 입력하여 주십시오."	
1138	, SW00002 : "서비스 처리 유형을 먼저 선택해 주십시오."
1139	, SW00003 : "저장 유형의 경우 등록,수정,삭제 중 최소 하나 이상의 SQL명을 입력해 주십시오."	
1140	, SI00001 : "토큰에 서비스 [${} 건] 처리 권한을 추가하시겠습니까?"	
1141};
1142
1143/**
1144 * 주어진 메시지 아이디에 해당하는 메시지를 가져온다.<p>
1145 * @param messageId
1146 */
1147fsp_message.prototype.getMessage = function(messageId, arr) {
1148	var msg = fsp_message.codes[messageId];
1149	if( !msg ) {
1150		return null; //messageId;
1151	}
1152	
1153	if( !arr ) {
1154		return msg;
1155	}
1156	//  ${} 포함 문자열 대체
1157	var oMsg = msg;
1158	var cnt = 0;
1159	var p = -1;
1160	var rMsg = "";
1161	while(true) {
1162		p = oMsg.indexOf("${}");
1163		if ( p == -1 ) {
1164			break;
1165		}
1166		rMsg = rMsg + oMsg.substr(0, p);
1167		if ( arr.constructor == Array) {
1168			rMsg = rMsg + arr[cnt];
1169		} else {
1170			rMsg = rMsg + arr;
1171		}
1172		
1173		oMsg = oMsg.substr(p+3);
1174		cnt++;
1175	}
1176
1177	rMsg = rMsg + oMsg;
1178	return rMsg;	
1179};
1180
1181/**
1182 * Alert 창을 표시한다.<p>
1183 * 
1184 * @param {String} messageId 메시지아이디 
1185 * @param arr 메시지 대체 문자열 또는 배열 
1186 * @param defMessage messageId 아이디가 존재하지 않을 경우 사용할 기본 메시지 
1187 */
1188fsp_message.prototype.alert = function(messageId, arr, defMessage) {
1189	var formatMsg = this.getMessage(messageId, arr);
1190	if( formatMsg ) {
1191		alert( formatMsg );
1192	} else if( defMessage ) {
1193		alert( defMessage);
1194	} else {
1195		alert( messageId );
1196	}
1197};
1198
1199fsp_message.prototype.showMsg = function(messageId, arr, defMessage, msgObj) {
1200	var formatMsg = this.getMessage(messageId, arr);
1201
1202	if( formatMsg ) {
1203		this.showMsg2(formatMsg, "info");
1204	} else if( defMessage ) {
1205		this.showMsg2(defMessage, "info"); //"warning");
1206	} else {
1207		this.showMsg2(messageId, "info");
1208	}
1209};
1210
1211/*
1212*	Notification으로 메시지를 표시한다. 
1213*	@param  msg 표시힐 메시지 내용 
1214*/
1215fsp_message.prototype.showMsg2 = function(msg, type) {
1216	var popupNotification = $("#sp_popupNotification");
1217	if( !popupNotification.length ) {
1218		$('body').append('<span id="sp_popupNotification"></span>');
1219	}
1220	$("#sp_popupNotification").kendoNotification({
1221				position: {
1222		        	top: 20
1223		    }
1224		    , autoHideAfter: 1500
1225	});
1226	popupNotification = $("#sp_popupNotification").getKendoNotification();
1227	
1228	var showType = type || 'info';
1229	popupNotification.show(msg, showType);
1230};
1231
1232/**
1233 * confirm 창을 표시한다.<p>
1234 * 
1235 * @param {String} messageId 메시지아이디 
1236 * @param arr 메시지 대체 문자열 또는 배열 
1237 * @param defMessage messageId 아이디가 존재하지 않을 경우 사용할 기본 메시지 
1238 * @return 처리결과 
1239 */
1240fsp_message.prototype.confirm = function(messageId, arr, defMessage) {
1241	var formatMsg = this.getMessage(messageId, arr);
1242	if( formatMsg ) {
1243		return confirm( formatMsg );
1244	} else if( defMessage ) {
1245		return confirm(defMessage)
1246	} else {
1247		return confirm( messageId );
1248	}
1249};
1250
1251/**
1252 * fsp 객체에 추가한다.<p>
1253 */
1254fsp.addConstructor(function() {
1255    if (typeof fsp.message === "undefined") {
1256        fsp.message = new fsp_message();
1257    }
1258});
1259//오른쪽 마우스 막기
1260document.oncontextmenu = function(e){
1261	if(e){
1262		e.preventDefault();
1263	} else {
1264		event.keyCode = 0;
1265		event.returnValue = false;
1266	}
1267};
1268// endsWith 폴리필
1269if (!String.prototype.endsWith) {
1270  String.prototype.endsWith = function(searchString, position) {
1271      var subjectString = this.toString();
1272      if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) {
1273        position = subjectString.length;
1274      }
1275      position -= searchString.length;
1276      var lastIndex = subjectString.indexOf(searchString, position);
1277      return lastIndex !== -1 && lastIndex === position;
1278  };
1279}
1280
1281Array.prototype.insert = function ( index, item ) {
1282    this.splice( index, 0, item );
1283};
1284
1285/**
1286 * 공통 모듈.<p>
1287 */
1288var fsp_common = function() {
1289};
1290/**
1291 * 화면을 초기화한다.<p>
1292 */
1293fsp_common.prototype.init = function() {
1294	// 메뉴 정보를 표시한다.  
1295	// cache false 처리
1296	$.ajaxSetup({ cache: false });
1297		
1298	// 페이지 제목 변경 
1299	this.changeTitle();
1300};
1301// 서브 컨텐츠 초기화 
1302fsp_common.prototype.initContents= function(obj) {
1303	if( !obj ) {
1304		obj = "#div_ctnt";
1305	}
1306	
1307	// 메뉴 정보를 표시한다.  
1308	try {
1309		// cache false 처리
1310		$.ajaxSetup({ cache: false });
1311				
1312		// 공통코드 처리 : cdGrp 항목이 존재하는 경우 콤보 생성 
1313		$(obj).find( "select[cdGrp]" ).each(
1314			function(idx, ele){
1315				$(this).makeCombo();
1316			}
1317		);
1318		
1319		$(obj).find( "input[type='text']" ).each(
1320			function(idx, ele){
1321				$(this).attr('autocomplete', 'off');
1322			}
1323		);
1324		
1325		$(obj + ' .datepicker').kendoDatePicker({
1326			format: "yyyy-MM-dd"
1327			, culture: "ko-KR"
1328		});
1329		
1330		$(obj + ' .switch').kendoSwitch({
1331            messages: {
1332                checked: "YES",
1333                unchecked:"NO"
1334            }
1335        });
1336        
1337	} catch( e ) {
1338		console.log("#### initContents failed:" + e);
1339	}
1340};
1341
1342/**
1343 * 페이지의 제목을 변경한다. 
1344 */
1345fsp_common.prototype.changeTitle = function(title) {
1346	try {
1347		if( !title ){
1348			title = document.title;
1349		}
1350		if( $.site && $.site.headerTitle ) {
1351			if( this.isEmpty(title) ) {
1352				document.title = $.site.headerTitle;
1353			} else {
1354				// 타이틀 존재 
1355				document.title = $.site.headerTitle.replace(/@/gi, title);
1356			}
1357		}
1358	} catch( e ) {
1359		console.log(e);
1360	}	
1361};
1362/**
1363 * URL의 파라메터를 가져온다.<p>
1364 * 
1365 * @returns 키 값 형태의 파라메터 배열 
1366 */
1367fsp_common.prototype.getParamStrings = function() {
1368	var vars = {};
1369	var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi,    
1370		function(m,key,value) {
1371		  vars[key] = value;
1372		}
1373	);
1374	
1375	return vars;
1376};
1377// 한글 기준 사이즈 
1378fsp_common.prototype.blength = function(value) {
1379	var length = value.length;
1380	for(var i = 0; i <value.length; i++){
1381	    if(value.charCodeAt(i) > 127){
1382	        length++;
1383	    }
1384	}
1385	return length;
1386};
1387// 한글 기준 사이즈 
1388fsp_common.prototype.bsubstr = function(value, max) {
1389	var newVal = '';
1390	var totLen = 0;
1391	for(var i = 0; i < value.length; i++) {
1392		totLen++;
1393	    if(value.charCodeAt(i) > 127){
1394	        totLen++;
1395	    }
1396	    if ( totLen <= max ) {
1397	    	newVal += value.charAt(i);
1398	    } else {
1399	    	break;
1400	    }
1401	}
1402	return newVal;
1403};
1404
1405/**
1406 * 메뉴 정보를 가져온다. 
1407 */
1408fsp_common.prototype.getMenu = function(menuID) {
1409	var menuIds = menuID.split('/');
1410	let ds = fsp.session.menu;
1411	var menuObj = null;
1412	for(var i = 0; i < menuIds.length; i++ ) {
1413		var mId = menuIds[i];
1414		
1415		ds.forEach(function(item) {
1416			if( item.ID == mId ) {
1417				ds = item.SUB;
1418				if( i == menuIds.length - 1) {
1419					menuObj = item;
1420				}
1421				return false;
1422			}
1423			return true;
1424		});
1425	}
1426	
1427	return menuObj;
1428} 	
1429
1430/**
1431 * 메뉴를 이동시킨다. 
1432 */
1433fsp_common.prototype.goMenu = function(menuID) {
1434	var menuObj = this.getMenu(menuID);
1435	if( !menuObj ) {
1436		fsp.message.alert("CI00100", menuID);
1437		return;
1438	}
1439	
1440	var data = {};
1441	var menuUrl = fsp.session.getLinkPath(menuObj.URL);
1442	var menuCls = menuObj.CLS;
1443	try {
1444		$.ajax({
1445			type : 'GET'	// ios webview 412 error 
1446			, url : menuUrl
1447			, cache : false
1448			, data : data
1449			, dataType : 'html'
1450			, contentType : 'application/x-www-form-urlencoded; charset=utf-8'
1451			, success: function(data){ 
1452				if( !menuCls && menuCls == 'P') {	// 팝업 
1453				} else {
1454					kendo.destroy(document.body);	// kendo 내용 삭제 
1455					
1456					$('#div_ctnt').attr('u' , menuUrl);
1457					$('#div_ctnt').html(data);
1458					
1459					// 현재 메뉴 선택 
1460					// fsp.session.selectMenu(menuID);
1461				}
1462			}
1463			,error: function(data) { 
1464				var status = data.status;
1465				if( status == 405 ) {
1466					fsp.fn_notLogin();
1467				} else if( status == 404 ) {
1468					fsp.message.alert("CI00100", menuID + "-" + menuUrl);
1469					return;
1470				}
1471			}
1472		});
1473	} catch( e ) {
1474		console.log(e);
1475	}	
1476};
1477/**
1478 * 주소에 해당하는 페이지를 가져와서 표시한다.  
1479 * 
1480 * @param url {String} 페이지주소 
1481 * @param divName {String} 표시할 디비전 명 없을 경우 기본 메인 페이지에 표시
1482 */
1483fsp_common.prototype.goPage = function(url, divName) {
1484	try {
1485		$.ajax({
1486			type : 'GET'	// ios webview 412 error 
1487			, url : url
1488			, cache : true
1489			, dataType : 'html'
1490			, contentType : 'application/x-www-form-urlencoded; charset=utf-8'
1491			, success: function(data){ 
1492				kendo.destroy(document.body);	// kendo 내용 삭제 
1493				
1494				if( divName ) {
1495					$('#' + divName).html(data);
1496				} else {
1497					$('#div_ctnt').html(data);
1498					// 현재 메뉴 선택 
1499					// fsp.session.selectMenu(null);
1500				}
1501			}
1502			,error: function(data) { 
1503				var status = data.status;
1504				if( status == 405 ) {
1505					fsp.fn_notLogin();
1506				} else if( status == 404 ) {
1507					fsp.message.alert("CI00100", url);
1508					return;
1509				}
1510			}
1511		});
1512	} catch( e ) {
1513		console.log(e);
1514	}	
1515};
1516
1517/**
1518 * 화면간 데이터 전달이 필요할때 key/value 형식으로 담겨질 Parameter Map
1519 * (주의) key 값은 화면간 규약이므로 연계 되는 화면끼리 맞춰야함
1520 */
1521fsp_common.prototype.screenParam = {};
1522fsp_common.prototype.screenCallback = null;
1523
1524/**
1525 * screenParam 에 key/value 값을 설정
1526 * 중복허용
1527 * @param key
1528 * @param value
1529 */
1530fsp_common.prototype.setScreenParam = function(key, value) {
1531	try {
1532		this.screenParam.put(key,value);
1533	} catch( e ) {
1534		fsp.common.errorLog(e, {"fnName" : "fsp.common.setScreenParam"});
1535	}
1536};
1537
1538/**
1539 * screenParam 에서 key 값에 해당하는 value 를 return
1540 * @param key
1541 * @returns
1542 */
1543fsp_common.prototype.getScreenParam = function(key) {
1544	try {
1545		return this.screenParam[key];
1546	} catch( e ) {
1547		fsp.common.errorLog(e, {"fnName" : "fsp.common.getScreenParam"});
1548	}
1549};
1550
1551/**
1552 * screenParam 에 설정된 key 값을 모두 초기화 한다.
1553 * 특별한 경우가 아니면, getScreenParam() 을 호출한 후에는 clearScreenParam() 을 호출하여
1554 * 화면간 전달시 사용된 정보를 초기화 시킨다
1555 */
1556fsp_common.prototype.clearScreenParam = function() {
1557	try {
1558		this.screenParam = {};
1559	} catch( e ) {
1560		fsp.common.errorLog(e, {"fnName" : "fsp.common.clearScreenParam"});
1561	}
1562};
1563
1564/**
1565 * screenParam 에 설정된 key 값을 초기화 한다.
1566 */
1567fsp_common.prototype.removeScreenParam = function(key) {
1568	try {
1569		this.screenParam.remove(key);
1570	} catch( e ) {
1571		fsp.common.errorLog(e, {"fnName" : "fsp.common.removeScreenParam"});
1572	}
1573};
1574
1575/**
1576 * callback 이 함수인지 여부 체크후 함수로 리턴한다
1577 * @param callback
1578 * @returns
1579 */
1580fsp_common.prototype.getPopupCallback = function(callback) {
1581	try {
1582		if( this.screenCallback ) {
1583			return this.screenCallback;
1584		}
1585		
1586		if( !this.isEmpty(callback) && $.isFunction(eval(callback)) ) {
1587			return eval(callback);
1588		} else {
1589			//alert(callback + '는 함수가 아니거나 지정된 Scope 에서 찾을수 없는 함수입니다');
1590			return false;
1591		}
1592	} catch( e ) {
1593		fsp.common.errorLog(e, {"fnName" : "fsp.common.fn_callback"});
1594	}
1595};
1596
1597/**
1598 * 팝업호출[검색용 팝업]
1599 * @param screenPath
1600 * @param screenId
1601 */
1602fsp_common.prototype.showPopup = function(divName, url, closeEventFn, params) {
1603	try {
1604		if( params ) {
1605			this.screenParam = params;
1606		}
1607		
1608		this.screenCallback = closeEventFn;
1609		
1610		//팝업에 필요한 div 를 동적으로 생성하도록 한다.
1611		$('#' + divName).remove();
1612		$('#div_ctnt').append($('<div id="' + divName + '"></div>'));
1613
1614		$.ajax({
1615     		url: url
1616     		, dataType : 'html'
1617			, contentType : 'application/x-www-form-urlencoded; charset=utf-8'
1618     		, success:function(data){
1619         		$('#' + divName).html(data);
1620
1621         		$('#' + divName + ' .modal').modal({
1622    				backdrop: 'static'
1623    			}).on('hidden.bs.modal', function(event) {
1624    				$('#' + divName).empty();
1625    				kendo.destroy($('#' + divName));	// kendo 내용 삭제 
1626    			});
1627				
1628				if( $('.modal.show').length && $('.modal.show').length > 0 ) {	// 팝업에 팝업 
1629					var max = $('.modal.show').css('z-index');
1630					var idx = $('.modal-backdrop').length;
1631					
1632					$('.modal-backdrop').eq(idx-1).css('z-index', parseInt(max)+1);	// 마지막 css 올려주기 
1633         			$('#' + divName + ' .modal').css('z-index', parseInt(max)+2);
1634				}
1635				
1636		   		// $('#' + divName + ' .modal-backdrop').css('z-index', fsp.common.getTopZindex());
1637         		// $('#' + divName + ' .modal').css('z-index', fsp.common.getTopZindex());
1638     		}
1639     		, error : function(data) { 
1640				var status = data.status;
1641				if( status == 405 ) {
1642					fsp.fn_notLogin();
1643				} else if( status == 404 ) {
1644					fsp.message.alert("CI00100", url);
1645					return;
1646				}
1647			}
1648 		});
1649	} catch( e ) {
1650		fsp.common.errorLog(e, {"fnName" : "fsp.common.searchPopup"});
1651	}
1652};	// end showPopup
1653
1654/**
1655 * Top z-index 얻어오기.
1656 */
1657fsp_common.prototype.getTopZindex = function(){
1658	try {
1659	    var maxZ = Math.max.apply(
1660	    			null,
1661	    			$.map( $('body > *'),
1662	    			function(e,n) {
1663	    				return parseInt($(e).css('z-index')) || 1;
1664	    			})
1665	    );
1666	    return Number(maxZ) + 10;
1667
1668	} catch( e ) {
1669		fsp.common.errorLog(e, {"fnName" : "fsp.common.getTopZindex"});
1670	}
1671}
1672
1673/**
1674 * Environment Value return
1675 * @returns
1676 */
1677fsp_common.prototype.getEnvValue = function(key, defValue) {
1678	try {
1679		var env = $.site.env;
1680		var rtn = null;
1681		if( env ) {
1682			rtn = env[key];
1683		}
1684		
1685		if( !rtn ) {
1686			return defValue;
1687		}
1688		
1689		if( typeof rtn == 'object') {
1690			return rtn;
1691		}
1692		if(fsp.common.isEmpty(rtn) ) {
1693			return defValue;
1694		}
1695		
1696		return rtn;
1697	} catch( e ) {
1698		fsp.common.errorLog(e, {"fnName" : "fsp.common.getEnvValue"});
1699	}
1700};
1701
1702/**
1703 * ErrorLog
1704 * @param msg
1705 * @param options
1706 */
1707fsp_common.prototype.errorLog = function(msg, options) {
1708	try {
1709		console.log("-----------------------------------------------------------");
1710		console.log("[Error]");
1711		if( options ) {
1712			var paramsKeys = Object.keys(options);
1713			if( paramsKeys && paramsKeys.length > 0 ) {
1714				// url 값으로 변경한다.
1715				for(var i = 0; i < paramsKeys.length; i++) {
1716					console.log(paramsKeys[i] + " : " + options[paramsKeys[i]]);
1717				}
1718			}
1719		}
1720		console.log(msg);
1721		console.log("-----------------------------------------------------------");
1722
1723		var envVal = fsp.common.getEnvValue('ui.script.error.alert');
1724		var isAlertView = (!fsp.common.isEmpty(envVal) && envVal ==='Y') ? true : false;
1725		if( isAlertView ) {
1726			fsp.message.alert(msg);
1727		}
1728	} catch( e ) {
1729		console.log(e);
1730	}
1731};
1732
1733/**
1734 * DebugLog
1735 * @param msg
1736 * @param options
1737 */
1738fsp_common.prototype.debugLog = function(msg, options) {
1739	try {
1740		if( this.logLevel == "DEBUG" ) {
1741			console.log("-----------------------------------------------------------");
1742			console.log("[Debug]");
1743			if( options ) {
1744				var paramsKeys = Object.keys(options);
1745				if( paramsKeys && paramsKeys.length > 0 ) {
1746					// url 값으로 변경한다.
1747					for(var i = 0; i < paramsKeys.length; i++) {
1748						console.log(paramsKeys[i] + " : " + options[paramsKeys[i]]);
1749					}
1750				}
1751			}
1752			console.log(msg);
1753			console.log("-----------------------------------------------------------");
1754		}
1755	} catch( e ) {
1756		console.log(e);
1757	}
1758};
1759
1760// 동일한 값을 가지는 항목 존재 유무 
1761fsp_common.prototype.findRow = function(ds, colName, value) {
1762	var rowIdx = -1;
1763	ds.forEach(function(item, index) {
1764		if( item[colName] == value ) {
1765			rowIdx = index;
1766			return false;
1767		}
1768		
1769		return true;
1770	});
1771	return rowIdx;
1772};	
1773
1774/**************************************************************************************
1775* 입력된 객체의 값이 Null인지 유무 확인하기
1776* @param  chkObj  확인할 객체
1777* @param  isShowMsg 메시지창 전시 여부
1778* @param  lbTxt 해당 항목 명칭
1779* @param  해당 객체가 아니라 Focus를 받을 별도의 객체가 존재할 경우 해당 객체
1780* @return null 인 경우 - true, 그외 false
1781**************************************************************************************/
1782fsp_common.prototype.isNull = function(chkObj, isShowMsg, lbTxt, focusObj) {
1783	var str = null;
1784	if( typeof chkObj == "string") {
1785		str = chkObj;
1786	} else {
1787		try {
1788			str = chkObj.val();
1789		} catch( e ) {
1790			str = null;
1791		}	
1792		
1793		if( !focusObj ) {
1794			focusObj = chkObj;
1795		}
1796	}
1797	// 값이 존재하지 않는다. 
1798	if( str !== null && str !== "" && str !== undefined ) {
1799		return false;
1800	} 
1801	
1802	// 메시지를 표시한다. 
1803	if( isShowMsg ) {
1804		var msg = "";
1805		if( lbTxt ) {
1806			// [${}] 값을 입력하여 주십시오.
1807			msg = fsp.message.getMessage("CW00001", lbTxt);
1808		} else {
1809			// "값을 입력하여 주십시오."
1810			msg = fsp.message.getMessage("CW00002");
1811		}
1812		
1813		chkObj.showWarn(msg);
1814		
1815		if( focusObj ) {
1816			focusObj.focus();
1817		}
1818	}
1819	
1820	return true;
1821};
1822
1823/**
1824 * 주어진 객체의 값이 존재하는지를 확인한다.<p>
1825 * @param obj
1826 */
1827fsp_common.prototype.isEmpty = function(obj) {
1828	if( !obj ) {
1829		return true;
1830	}
1831	
1832	var str = null;
1833	if( typeof obj == "string") {
1834		str = obj;
1835	} else if( typeof obj == "number") {
1836		return false;
1837	}  else {
1838		try {
1839			str = obj.val();
1840		} catch( e ) {
1841			return false;
1842		}	
1843	}
1844	
1845	if( !str ) {
1846		return true;
1847	}
1848	
1849	if( str === null || str === "") {
1850		return true;
1851	}
1852	
1853	return false;
1854};
1855
1856/**
1857 * 자바스크립트 에러를 기록한다.<p>
1858 * 
1859 * @param caller
1860 * @param e
1861 */	
1862fsp_common.prototype.jsError = function( caller, e) {
1863	alert( "javascript 예외\n" + caller + "\n" + e );
1864};
1865
1866/**
1867 * json객체를 파라메터로 사용할 수 있는 문자열로 변환한다.<p>
1868 * @param obj
1869 * @returns
1870 */
1871fsp_common.prototype.json = function(obj) {
1872	return JSON.stringify(obj);
1873}
1874
1875/**
1876 * 해당 값이 올바른지 확인한다.  <p>
1877 * 
1878 * @param format 포맷 
1879 * @param val 값
1880 */
1881fsp_common.prototype.isValidFormat = function(format, value) {
1882	if( !format || !value) {	// 형식 또는 값이 없는 경우 
1883		return true;
1884	}
1885	try {
1886		if( format == "eng_number") {
1887			return /^[a-zA-Z\d]+$/.test(value);
1888		} else if( format == "amt" ) {	// 금액
1889			return /^[0-9:,]*$/.test(value);
1890		} else if( format == "phone") {
1891			return /^[0-9]{2,3}[-]{0,1}[0-9]{3,4}[-]{0,1}[0-9]{4}$/.test(value);
1892		} else if( format == "email") {
1893			return /[0-9a-zA-Z][_0-9a-zA-Z-]*@[_0-9a-zA-Z-]+(\.[_0-9a-zA-Z-]+){1,2}$/.test(value);
1894		} else if( format == "number") {	// 숫자
1895			return /^[0-9]*$/.test(value);
1896		} else if( format == "float") {
1897			return /^[-]{0,1}[0-9]+[.]{0,1}[0-9]+/.test(value);
1898		}
1899		return true;
1900	} catch( e ) {
1901		return false;
1902	}
1903};
1904
1905/**
1906 * 조회된 값을 실제 표시값으로 포맷팅한다.<p>
1907 * 
1908 * @param format 형식 
1909 * @param val 값 
1910 * @param dispLen 표시 자리수 
1911 */
1912fsp_common.prototype.getFormatText = function(format, val, dispLen) {
1913	if( !format ) {
1914		format = "";
1915	}
1916	
1917	try {
1918		if(  format == "html" ) {	
1919			if( fsp.common.isEmpty(val)) {
1920				return "";
1921			} 
1922			return val.replace(/(?:\r\n|\r|\n)/g, '\n');
1923		}
1924		
1925		if( format == "bizno") {
1926			return this.getFormatBizNo(val, dispLen);	
1927		}
1928		
1929		if( format == "time" ) {	// 시간 
1930			return this.getFormatTime(val, dispLen);
1931		} else if( format == "ymd" || format == "date" || format == "datetime") {
1932			return this.getFormatDate(val, dispLen);
1933		} else if( format.endsWith("_YMD") || format.endsWith("_DTTM") || format.endsWith("_DT") ) {
1934			return this.getFormatDate(val, dispLen);
1935		} else if( format == "number" || format == "amt" || format.endsWith("_AMT") ) {
1936			return this.getFormatNumber(val);
1937		}
1938		
1939		if( !val || val == null ) {
1940			val = "";
1941		}
1942		return val;
1943	} catch( e ) {
1944		return val;
1945	}
1946};
1947/**
1948 * 서버에 값을 전송할 때 실제 전송해야 하는 값을 가져온다. <p>
1949 * 
1950 * @param key 컬럼명 
1951 * @param val 값ㅂ 
1952 */
1953fsp_common.prototype.getOriginText = function(format, val) {
1954	if( !format ) {
1955		return val;
1956	}
1957	try {
1958		if( format == "html") {
1959			if ( !val || val != "" || val != null) {
1960				return val.replace(/(?:\<br>)/g, '\n');
1961			} else {
1962				return "";
1963			}			
1964		} else if( format == "bizno") {	// 주민번호, 사업자번호 
1965			if ( !val || val != "" || val != null) {
1966				return val.replace(/[- :]/g, "");
1967			} else {
1968				return "";
1969			}
1970		} else if( format == "ymd" || format == "date" || format == "datetime" || format=="time") {
1971			if ( !val || val != "" || val != null) {
1972				return val.replace(/[- :]/g, "");
1973			} else {
1974				return "";
1975			}
1976		} else if( format == "number" || format == "amt") {	// 숫자
1977			if ( !val || val != "" || val != null) {
1978				return val.replace(/[,]/g, "");
1979			} else {
1980				return "";
1981			}
1982		} else if(format == "float") {	//소수점 표시
1983			if ( !val || val != "" || val != null) {
1984				return val.replace(/[,]/g, "");
1985			} else {
1986				return "";
1987			}
1988		}
1989		
1990		if( !val ) {
1991			return "";
1992		}
1993		return val;
1994	} catch( e ) {
1995		return val;
1996	}
1997};
1998/**
1999 * <br> 로 저장했던 글자를 엔터로 변경한다.<p>
2000 * 
2001 * @param val
2002 * @returns
2003 */
2004fsp_common.prototype.getBrToEnter = function(val) {
2005	if( fsp.common.isEmpty(val)) {
2006		return "";
2007	} 
2008	return val.replace(/(?:\<br>)/g, '\n');
2009}
2010/**
2011 * 숫자를 가져온다.<p>
2012 */
2013fsp_common.prototype.getNumber = function(val) {
2014	if( fsp.common.isEmpty(val)) {
2015		return 0;
2016	} else {
2017		return parseInt(val);
2018	}
2019}
2020/**
2021 * 검색 조건용 년(2자리),월,일 : 6자리 포맷 데이터를 가져온다.<p>
2022 * 
2023 * @param val 값 
2024 * @returns 포맷팅된 값 
2025 */
2026fsp_common.prototype.getShortYMD = function(val) {
2027	if( !val || val == "" ) {
2028		return "";
2029	}
2030	// 8자리보다 크거야 같아야 함. 
2031	var originText = val.replace(/[- :]/g, "");
2032	if( originText.length >= 8 ) {
2033		return originText.substr(2,6);
2034	} 
2035	
2036	return "";
2037}
2038/**
2039 * 일자를 포맷팅한다.<p>
2040 * 
2041 * @param val 값 
2042 * @returns 포맷팅된 값 
2043 */
2044fsp_common.prototype.getFormatDate = function(val, dispLen) {
2045	if( !val || val == "" ) {
2046		return "";
2047	}
2048	var maxLen = val.length;
2049	if( dispLen && dispLen !== undefined ) {
2050		maxLen = dispLen;
2051	}
2052	if( maxLen >= val.length ) {
2053		maxLen = val.length;
2054	}
2055	if( maxLen < 6 && maxLen > 14 ) {
2056		return val;
2057	}
2058	var rtnStr = "";
2059	if( maxLen <= 6 ) {	// 년월로만
2060		rtnStr = val.substr(0,4) + "-" + val.substr(4,2);
2061	} 
2062	
2063	if( maxLen >= 8 ) {
2064		rtnStr = val.substr(0,4) + "-" + val.substr(4,2) + "-" + val.substr(6,2);
2065	} 
2066	if( maxLen >= 10 ) {
2067		rtnStr += ( " " + val.substr(8,2) );
2068		if( maxLen == 10 ) {
2069			rtnStr += "시";
2070		}
2071	}
2072	if( maxLen >= 12) {
2073		rtnStr += ( ":" + val.substr(10,2) );
2074	}
2075	if( maxLen >= 14 ) {
2076		rtnStr += ( ":" + val.substr(12,2) );
2077	}
2078	return rtnStr;
2079}
2080/**
2081 * 시간을 포맷팅한다.<p>
2082 * 
2083 * @param val 값 
2084 * @returns 포맷팅된 값 
2085 */
2086fsp_common.prototype.getFormatTime = function(val, dispLen) {
2087	if( !val || val == "" ) {
2088		return "";
2089	}
2090	var maxLen = val.length;
2091	if( dispLen && dispLen !== undefined ) {
2092		maxLen = dispLen;
2093	}
2094	if( maxLen >= val.length ) {
2095		maxLen = val.length;
2096	}
2097	if( maxLen < 2 && maxLen > 6 ) {
2098		return val;
2099	}
2100	
2101	var rtnStr = "";
2102	if( maxLen >= 2 ) {
2103		rtnStr = val.substr(0,2);
2104	} 
2105	if( maxLen >= 4) {
2106		rtnStr += ( ":" + val.substr(2,2) );
2107	}
2108	
2109	if( maxLen >= 6 ) {
2110		rtnStr += ( ":" + val.substr(4,2) );
2111	}
2112	
2113	return rtnStr;
2114}
2115/**
2116 * 숫자를 포맷팅한다.<p>
2117 * 
2118 * @param val 값 
2119 * @returns 포맷팅된 값 
2120 */
2121fsp_common.prototype.getFormatNumber = function(val) {
2122	if( !val ) {
2123		return "";
2124	}
2125	return val.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
2126}
2127/**
2128*	+/- 일자를 계산한다. 
2129**/
2130fsp_common.prototype.getPlusDate = function(baseDt, days) {
2131	if( !baseDt || baseDt.length < 8) {
2132		return baseDt;
2133	}
2134	// 오늘일자 
2135	var dat1 = new Date(baseDt.substr(0,4), parseInt(baseDt.substr(4,2))-1
2136				, baseDt.substr(6,2));
2137	var dat2 = new Date(dat1.getTime() + (days+1) * 24*60*60*1000);
2138	return dat2.toISOString().slice(0,10).replace(/-/g,"");
2139}
2140
2141// 조회 처리 
2142fsp_common.prototype.fsp_search = function(callObj, sql, data, callback, rtnId, bizNm) {
2143	fsp.clear();
2144	if( bizNm ) {
2145		fsp.setBiz(bizNm);
2146	}
2147	
2148	fsp.addSearch(sql, false, false, rtnId ? rtnId : null );
2149	fsp.callService(
2150		""
2151		, ""
2152		, data
2153		, function(errorCode, rData){
2154			callback.call(callObj, errorCode, rData);
2155		}
2156		, true
2157	);
2158}
2159	
2160// kendo grid 설정
2161fsp_common.prototype.fn_gridHC = function(attrs) {
2162	var json = attrs ? attrs : {};			
2163	json.style = "text-align: center; vertical-align:middle;";
2164	return json;
2165}
2166fsp_common.prototype.fn_gridHR = function(attrs) {
2167	var json = attrs || {};		
2168	json.style = "text-align: right; vertical-align:middle;";
2169	return json;
2170}
2171fsp_common.prototype.fn_gridHL = function(attrs) {
2172	var json = attrs || {};		
2173	json.style = "text-align: left; vertical-align:middle;";
2174	return json;
2175}
2176fsp_common.prototype.fn_gridDL = function(attrs) {
2177	var json = attrs || {};	
2178	json.style = 'text-align: left; white-space: nowrap;';
2179	return json;
2180}
2181fsp_common.prototype.fn_gridDC =function(attrs) {
2182	var json = attrs || {};	
2183	json.style = 'text-align: center; white-space: nowrap;';
2184	return json;
2185}
2186fsp_common.prototype.fn_gridDR = function(attrs) {
2187	var json = attrs || {};	
2188	json.style = 'text-align: right; white-space: nowrap;';
2189	return json;
2190}
2191
2192/**
2193*	켄도 그리드 생성
2194*	@param grdId 그리드 아이디 
2195* 	@param columns 그리드 컬럼 정보 
2196* 	@param options 추가 옵션 
2197**/
2198fsp_common.prototype.fn_makeGrid = function(grdId, columns, options, bTree) {	// 그리드 생성 
2199	var opt = {
2200		height: (options.height || 550)
2201		, scrollable : true 
2202		, resizable: true
2203		, reorderable: true
2204		, navigatable: true 
2205		, noRecords: {
2206			template: "<span class='sz-input-12 text-center mt-5 mb-5'>
2206#= fsp.message.getMessage('CW00022') #</span>"
2207		}	
2208	    , columns : columns
2209	    , dataBinding: function() {
2210        	record = (this.dataSource.page() -1) * this.dataSource.pageSize();	// rowindex 
2211        	if( options.dataBinding ) {
2212        		options.dataBinding();
2213        	}
2214        }
2215	};
2216	
2217	if( options && options.dataSource ) {
2218		opt.dataSource = options.dataSource;
2219	} else {
2220		opt.dataSource = {
2221			  data: ""
2222			, pageSize : ( options.pageSize || 100 ) 
2223		};
2224	}
2225	
2226	let colMenuUse = !(options.columnMenu === false);
2227	if ( colMenuUse ) {
2228		opt.columnMenu = {};
2229	}
2230	
2231	if( options.sortable !== false ) {
2232		opt.sortable = (options.sortable || false);
2233		if( colMenuUse) {
2234			opt.columnMenu.sortable = opt.sortable;
2235		}	
2236	}
2237	if( options.filterable !== false ) {
2238		opt.filterable = (options.filterable || false);
2239		if( colMenuUse ) {
2240			opt.columnMenu.filterable = opt.filterable;
2241		}	
2242	}
2243	
2244	if( options.groupable === true ) {
2245		opt.groupable = options.groupable;
2246	}
2247	
2248	if(options.selectable !== false) {
2249		opt.selectable = (options.selectable || "row" ) 
2250	}
2251	
2252	if( options.dataBound ) {
2253		opt.dataBound = options.dataBound
2254	}
2255	
2256	if( options.change ) {
2257		opt.change = options.change;
2258	}
2259	if( options.page ) {
2260		opt.page = function(e) {	// 이전, 다음 페이지 조회
2261    		var newPageNo = parseInt(e.page);
2262    		var newPageSize = parseInt(e.sender.pager.pageSize());
2263    		options.page(e, newPageNo, newPageSize);
2264	    }
2265	}
2266	
2267	if( options.pagerChange ) {
2268		opt.pageable = {
2269			  refresh 		: false
2270			, pageSizes		: [50, 100, 200, 500]
2271	    	, buttonCount 	: (options.pageable && options.pageable.buttonCount > 0 ? options.pageable.buttonCount : 5 )
2272	    	, input : (options.pageable && options.pageable.input === false ? false : true )
2273	    	, info : true		// 전체 몇건 
2274	    	, messages: {
2275		        display: "{0}-{1} / 전체 {2} 건"
2276		        , empty: "0 ê±´"
2277		        , itemsPerPage: "건씩 표시"
2278		        , of: "from {0}"
2279		     }
2280	    }
2281	} else if ( options.pageable === false ) {
2282		opt.pageable = false;
2283	} else {
2284		opt.pageable = true;
2285	}
2286	
2287	opt.messages = {
2288		commands : {}
2289	};
2290	// 옵션 처리 
2291	if( options.excel === true  ) {
2292		opt.toolbar  = ["excel"];
2293		opt.excel =  {
2294	          allPages: true
2295	        , filterable: true
2296	        , fileName: (options.excelName || 'list.xlsx')
2297	    };
2298	} 
2299	
2300	if( options.search === true  ) {
2301		if ( opt.toolbar ) {
2302			opt.toolbar.push('search');
2303		} else {
2304			opt.toolbar  = ["search"];
2305		}
2306	}
2307	
2308	// 입력 그리드 
2309	if ( options.add === true ) {
2310		opt.editable = true;
2311		opt.toolbar = (opt.toolbar || []);
2312		opt.toolbar.push('create');
2313	}
2314	
2315	if( options.editable ) {	// incell( 바로 ), inline 
2316		opt.editable = options.editable;
2317		
2318		if ( !options.editable.mode ) {
2319			opt.editable = {
2320				createAt: "bottom"
2321				, mode : 'inline'
2322			}
2323		}
2324	} 
2325	
2326	var grid = null;
2327	if( bTree ) {
2328		grid = $(grdId).kendoTreeList(opt);	// end treeList 
2329	}  else {
2330		grid = $(grdId).kendoGrid(opt);	// end grid 
2331		if( options.pagerChange ) {
2332			var pager = $(grdId).data('kendoGrid').pager;
2333			pager.element.bind('change', function(e) {	// 건수 변경시 처리 
2334				var pageSize = $(grdId).data('kendoGrid').pager.pageSize();
2335				options.pagerChange(pageSize, e);
2336			});
2337		}	
2338	}
2339			
2340	return grid;
2341	
2342}		
2343// chart 관련 컬러 
2344fsp_common.prototype.CHART_COLORS = 
2345	["#1fb3ce", "#FC0","#F60","#E60039","#EB6877","#F29C9F","#920783"
2346      ,"#AE5DA1","#C490BF","#601986","#0068B7","#3AB0E1","#7ECEF4","#84CCC9","#13B5B1","#ACD598","#80C269","#094","#638C0B","#8A8000","#D1C0A5","#A6937C","#7E6B5A","#59493F","#362E2B","#CFA972","#B28850","#996C33","#81511C","#6A3906","#B7AA00","#FFF100","#FC0","#F39800","#FF5A00","#9178FF","#BBACFF","#20B","#00B4FF","#00D7DA","#BFEF00","#F00","#0060FF"
2347       ,"#9000FF","#000","#D200FF","#7D0022","#E5004F","#E4007F","#5F52A0"];
2348
2349// grid editor type 설정
2350fsp_common.prototype.gridEditorFieldTypeComboBox = function(container, options, myOptions, changeCallBack) {
2351   try {
2352       var input = $("<input/>");
2353       input.attr("name", options.field);
2354       input.appendTo(container);
2355       input.kendoDropDownList({
2356            dataTextField: ( myOptions.name ||  "CD_NM" )
2357         , dataValueField: ( myOptions.cd ||  "CD" )
2358         , dataSource: ( myOptions.ds || fsp.code.getCode(myOptions.cdGrp) )
2359         , index: -1
2360         , change: function(e) {
2361            var selectedItem = this.dataItem(this.selectedIndex);
2362           
2363            if( changeCallBack ) {
2364               changeCallBack(container, options, selectedItem);
2365            } else {
2366               if(selectedItem) {
2367                  options.model.typeNm = selectedItem[ ( myOptions.name ||  "CD_NM") ];
2368                  options.model.type = selectedItem[( myOptions.cd ||  "CD" )];
2369               }
2370            }
2371         }
2372      });
2373
2374   } catch( e ) {
2375      fsp.common.errorLog(e, {"screenName" : 'fsp.common', "fnName" : "gridEditorFieldTypeComboBox"});
2376   }
2377}//end gridEditorFieldTypeComboBox()
2378
2379fsp_common.prototype.gridEditorFieldTypeCheckbox = function(container, options, myOptions) {
2380	var guid = kendo.guid();
2381	var name = options.field;
2382	$('<input class="k-checkbox" id="' + guid + '" type="checkbox" name="' + name + '" data-type="boolean" data-bind="checked:' + name + '">').appendTo(container);
2383	$('<label class="k-checkbox-label" for="' + guid + '">​</label>').appendTo(container);
2384}	// gridEditorFieldTypeCheckbox
2385      
2386/**
2387 * fsp 객체에 추가한다.<p>
2388 */
2389fsp.addConstructor(function() {
2390    if (typeof fsp.common === "undefined") {
2391        fsp.common = new fsp_common();
2392    }
2393});
2394/*
2395 * 공통 코드 처리 모듈.
2396 */
2397var fsp_code = function() {
2398
2399};
2400/**
2401 * 공통 코드 정보.<p>
2402 */
2403fsp_code.codes = {
2404	"S901" : [	
2405		{"CD":"M", "CD_NM":"메시지"}
2406		, {"CD":"A", "CD_NM":"알림통"}
2407	]
2408	, "S902" : [	
2409		{"CD":"H", "CD_NM":"HTML"}
2410		, {"CD":"T", "CD_NM":"TEXT"}
2411		, {"CD":"U", "CD_NM":"URL"}
2412		, {"CD":"J", "CD_NM":"JSON"}
2413	]
2414	, "S903" : [	
2415		{"CD":"Y", "CD_NM":"확인"}
2416		, {"CD":"N", "CD_NM":"미확인"}
2417	]
2418	, "S904" : [	
2419		{"CD":"G", "CD_NM":"조직"}
2420		, {"CD":"U", "CD_NM":"사용자"}
2421	]
2422	, "S905" : [	
2423		{"CD":"Y", "CD_NM":"예"}
2424		, {"CD":"N", "CD_NM":"아니오"}
2425	]
2426	, "S906" : [	
2427		{"CD":"1", "CD_NM":"학생"}
2428		, {"CD":"2", "CD_NM":"교수"}
2429		, {"CD":"3", "CD_NM":"교직원"}
2430	]	
2431	
2432	/* FSPmobile 관리자 코드 */
2433	, F06 : [
2434	         {CD : "I", CD_NM : "iOS"}
2435	         , {CD : "A", CD_NM : "Android"}
2436	]
2437	, F11 : [
2438	         {CD : "1", CD_NM : "정상"}
2439	         , {CD : "5", CD_NM : "임시 사용 불가 처리"}
2440	         , {CD : "6", CD_NM : "푸시 서버에서 삭제되어 사용불가"}
2441	         , {CD : "7", CD_NM : "관리 정책에 의해 사용불가"}
2442	         , {CD : "8", CD_NM : "동일 디바이스 신규 등록으로 사용불가"}
2443	         , {CD : "9", CD_NM : "도난등으로 사용불가 기기로 등록"}
2444	]
2445	, F07 : [
2446	         {CD : "M", CD_NM : "전화번호"}
2447	         , {CD : "U", CD_NM : "디바이스 식별번호"}
2448	         , {CD : "D", CD_NM : "FSPmobile 디바이스 번호"}
2449	],
2450	F08 : [
2451	         {CD : "Q", CD_NM : "요청"}
2452	         , {CD : "A1", CD_NM : "관리자 승인"}
2453	         , {CD : "A2", CD_NM : "자동 승인"}
2454	         , {CD : "R", CD_NM : "거부"}
2455	]
2456};
2457
2458/**
2459 * 그룹 코드에 해당하는 코드를 가져온다.<p>
2460 * @param cdGrpId  그룹코드 
2461 */
2462fsp_code.prototype.getCode = function(cdGrpId) {
2463	var codes = fsp_code.codes[cdGrpId];
2464	if( !codes ) {
2465		return null;
2466	}
2467	return codes;
2468};
2469
2470/**
2471 * 코드에 해당하는 코드명을 가져온다.<p>
2472 * 
2473 * @param cdGrpdId 그룹코드 
2474 * @param cd 코드 
2475 */
2476fsp_code.prototype.getCodeName = function(cdGrpdId, cd) {
2477	if( !cdGrpdId ) {
2478		return "";
2479	}
2480	var grps = this.getCode(cdGrpdId);
2481	if( !grps ) {
2482		return "";
2483	}
2484	
2485	for(var i= 0; i < grps.length; i++) {
2486		if( grps[i].CD == cd ) {
2487			return grps[i].CD_NM;
2488		}
2489	}
2490	return "";
2491};
2492/**
2493 * 공통 코드를 설정한다.<p>
2494 * 
2495 * @param options 공통코드 옵션 
2496 */
2497fsp_code.prototype.makeCommCodes = function(options, callbackFunc) {
2498	if( !options || !options.length || options.length < 1 ) {
2499		return;
2500	}
2501	
2502	/*
2503	// 콤보를 생성한다. 
2504	for(var i = 0; i < options.length; i++) {
2505		$(options[i].compName).makeCombo(options[i].cdGrp, options[i].option);
2506	}
2507	*/
2508	// 코드 조회 
2509	this.fn_searchCommCode(options, callbackFunc);
2510}
2511
2512/**
2513 * 공통 코드를 조회한다.<p>
2514 * 
2515 * @param options 코드 옵션 
2516 */
2517fsp_code.prototype.fn_searchCommCode = function(options, callbackFunc) {
2518	try {
2519		fsp.clear();
2520		var cdGrp = "";
2521		for(var i = 0; i < options.length; i++) {
2522			var codes = this.getCode(options[i].cdGrp);
2523			// 이미 존재한다. 
2524			if( codes ) {
2525				continue;
2526			}
2527			if( cdGrp != "") {
2528				cdGrp += ",";
2529			}
2530			cdGrp += ( "'" + options[i].cdGrp + "'");
2531		}
2532		
2533		// 이미 모두 처리되어 있다. 
2534		if( cdGrp == "") {
2535			// 콤보를 생성한다. 
2536			for(var i = 0; i < options.length; i++) {
2537				// 객체가 없어도 생성할 수 있게 변경 
2538				if( options[i].compName ) {
2539					$(options[i].compName).makeCombo(options[i].cdGrp, options[i].option);
2540				}
2541			}	
2542			
2543			if( callbackFunc ) {
2544				callbackFunc(true);
2545			}
2546			return;
2547		}
2548		
2549		var data = {
2550			CD_GRP_ID : cdGrp
2551		};
2552		
2553		// 서버 호출하여 처리 
2554		fsp.addSearch("pnsadm/comm:CODE_S01", false);
2555		fsp.callService(
2556				""
2557				, ""
2558				, data
2559				, function(errorCode, rData) {
2560					try {
2561						if( errorCode != 0 ) {
2562							if( callbackFunc ) {
2563								callbackFunc(false);
2564							}
2565							return;
2566						}
2567						
2568						var ds = rData.ds_codes;
2569						var prevCode = "";
2570						var arr = null;
2571						for(var i = 0; i < ds.length; i++) {
2572							if( prevCode != ds[i].CD_GRP_ID ) {
2573								if( prevCode != "" ) {
2574									fsp_code.codes[prevCode] = arr;
2575								}
2576								arr = new Array();
2577								prevCode = ds[i].CD_GRP_ID;
2578							}
2579							var items = {
2580								CD : ds[i].CD
2581								, CD_NM : ds[i].CD_NM
2582								, ATTR1 : ds[i].ATTR1
2583								, ATTR2 : ds[i].ATTR2
2584								, ATTR3 : ds[i].ATTR3
2585							};
2586							arr.push(items);
2587						}
2588						
2589						if( arr != null ) {
2590							fsp_code.codes[prevCode] = arr;
2591						}
2592						
2593						// 콤보를 생성한다. 
2594						for(var i = 0; i < options.length; i++) {
2595							// 객체가 없어도 생성할 수 있게 변경 
2596							if( options[i].compName ) {
2597								$(options[i].compName).makeCombo(options[i].cdGrp, options[i].option);
2598							}
2599						}
2600						if( callbackFunc ) {
2601							callbackFunc(true);
2602						}
2603					} catch( e ) {
2604						fsp.common.jsError("fsp.code.fn_searchCode#success", e);
2605					}
2606				}
2607				, true
2608			);	// end call 
2609	} catch( e ) {
2610		fsp.common.jsError("fsp.code.fn_searchCode", e);
2611		if( callbackFunc ) {
2612			callbackFunc(false);
2613		}
2614	}
2615};
2616
2617/**
2618 * 주어진 sqlmap을 조회한다. 
2619 * 
2620 * @param sqlInfo [
2621 * 		{
2622 * 			sql : 
2623 * 			, rtnId :
2624 * 			, comboId : 
2625 * 			, cond : [
2626 * 				{name : 
2627 * 				, value : }
2628 * 			]
2629 * 			, all : "N" // N 이면 전체 없음. 그외는 표시된 글자를 넣어줌. 
2630 * 		}
2631 * ]
2632 */
2633fsp_code.prototype.fn_searchCode = function( sqlInfo ) {
2634	try {
2635		var data = {	
2636		};
2637		
2638		fsp.clear();
2639		for( var i = 0; i < sqlInfo.length; i++) {
2640			var sql = sqlInfo[i];
2641			
2642			if( !sql.rtnId ) {
2643				continue;
2644			} 
2645			
2646			fsp.addSearch( sql.sql, false, false, sql.rtnId);
2647			
2648			// 조회 조건을 설정한다. 
2649			if( sql.cond ) {
2650				for( var j = 0; j < cond.length; j++) {
2651					data[sql.cond[j].name] = sql.cond[j].value;
2652				}
2653			}
2654		}
2655		
2656		fsp.callService(
2657			""
2658			, ""
2659			, data
2660			, function(errorCode, rData) {
2661				try {
2662					if( errorCode != 0 ) {
2663						return;
2664					}
2665					for( var i = 0; i < sqlInfo.length; i++) {
2666						var sql = sqlInfo[i];
2667						var dsId = sql.rtnId;
2668						var ds = rData[dsId];
2669						if( typeof sql.comboId == "string" ) {
2670							var combo = sql.comboId;
2671							if( !combo ) {
2672								continue;
2673							}
2674							
2675							combo = $("#" + combo);
2676							if( !combo ) {
2677								continue;
2678							}
2679							
2680							if( sql.all && sql.all != "N") {
2681								ds.insert(0, {CD : '', CD_NM : sql.all });
2682							}
2683							combo.kendoComboBox({
2684								dataTextField : "CD_NM"
2685				        		, dataValueField: "CD"
2686				        		, dataSource : ds 
2687				        		, valuePrimitive: true
2688							});	// combo 생성 
2689							
2690						} else if( sql.comboId.length ){
2691							for( var i = 0; i < sql.comboId.length; i++) {
2692								var combo = sql.comboId[i];
2693								if( !combo ) {
2694									continue;
2695								}
2696								
2697								combo = $("#" + combo);
2698								if( !combo ) {
2699									continue;
2700								}
2701								
2702								if( sql.all && sql.all != "N") {
2703									ds.insert(0, {CD : '', CD_NM : sql.all });
2704								}
2705								
2706								combo.kendoComboBox({
2707									dataTextField : "CD_NM"
2708					        		, dataValueField: "CD"
2709					        		, dataSource : ds 
2710					        		, valuePrimitive: true
2711								});	// combo 생성 
2712							}
2713						}
2714					}
2715				} catch( e ) {
2716					fsp.common.jsError("fsp.code.fn_searchCode#success", e);
2717				}
2718			}
2719			, true
2720		);
2721	} catch( e ) {
2722		fsp.common.jsError("fsp.code.fn_searchCode", e);
2723	}
2724};
2725
2726/**
2727 * fsp 객체에 추가한다.<p>
2728 */
2729fsp.addConstructor(function() {
2730    if (typeof fsp.code === "undefined") {
2731        fsp.code = new fsp_code();
2732    }
2733});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.