/*
 * ag-Grid(https://www.ag-grid.com) Common Java Script written by gagamel.
 *
 * Copyright (c) 2019 gagamel
 * Dual licensed under GPL (GPL-LICENSE.txt) licenses.
 *
 * $Date: 2019-03-31 $
 */

var isNavigationKey = function(event) {
	var KEY_LEFT = 37;
	var KEY_UP = 38;
	var KEY_RIGHT = 39;
	var KEY_DOWN = 40;
	var KEY_PAGE_UP = 33;
	var KEY_PAGE_DOWN = 34;
	var KEY_PAGE_HOME = 36;
	var KEY_PAGE_END = 35;

	var keyCode = event.keyCode;

	return keyCode === KEY_LEFT || keyCode === KEY_RIGHT || keyCode === KEY_UP || keyCode === KEY_DOWN
		|| keyCode === KEY_PAGE_DOWN || keyCode === KEY_PAGE_UP || keyCode === KEY_PAGE_HOME || keyCode === KEY_PAGE_END;
}

/**
 * Customer loading
 * @author gagamel
 * @since 2019. 4. 10
 */
var getCustomLoadingOverlay = function() {
	function CustomLoadingOverlay() {};

	CustomLoadingOverlay.prototype.init = function(params) {
		this.eGui = document.createElement('div');
		this.eGui.innerHTML = ''
			+ '<div style="'
			+ 'background: url(/ux/plugins/gaga/loader.gif); border-style: none; background-repeat: no-repeat; '
			+ 'position: absolute; top: 45%; left: 50%; width: auto; '
			+ 'z-index: 101; padding: 16px; margin: 5px;'
			+ '"></div>';
	};

	CustomLoadingOverlay.prototype.getGui = function() {
		// Button disabled & progressBar creation
		//$('.btn').each(function(idx) { $(this).attr('disabled', true); });
		return this.eGui;
	};

	return CustomLoadingOverlay;
}

/**
 * Datepicker component
 * <pre>
 *     cellEditor : 'datePicker'
 * </pre>
 * @author gagamel
 * @since 2019. 4. 8
 */
var getDatePicker = function(pattern) {
	if (typeof(pattern) == 'undefined') {
		pattern = "yyyy/mm/dd";
	}

	// function to act as a class
	function Datepicker() {};

	// gets called once before the renderer is used
	Datepicker.prototype.init = function(params) {
		this.eGui = document.createElement('div');
		this.eGui.innerHTML = '<input'
			+ ' name="' + params.colDef.field + '"'
			+ (!gagajf.isNull(params.value) ? ' value="' + params.value + '"' : '')
			+ (!gagajf.isNull(params.maxlength) ? ' maxlength="' + params.maxlength + '"' : '')
			+ (!gagajf.isNull(params.validType) ? ' data-valid-type="' + params.validType + '"' : '')
			+ ' style="width: 100%; z-index: 999999"/>';

		this.eInput = this.eGui.querySelector('input');

		if (params.required) {
			this.eInput.required = 'required';
//			this.eInput.classList.add('required');
		}

		$(this.eInput).datepicker({
			startView: 0,
			format: pattern,
			todayBtn: "linked",
			keyboardNavigation: false,
			forceParse: false,
			autoclose: true,
			todayHighlight: true,
			onSelect: function(dateText, inst) {
				params.stopEditing();
			}
		});
	};

	// gets called once when grid ready to insert the element
	Datepicker.prototype.getGui = function() {
		return this.eGui;
	};

	// focus and select can be done after the gui is attached
	Datepicker.prototype.afterGuiAttached = function() {
		this.eInput.focus();
		this.eInput.select();
	};

	// returns the new value after editing
	Datepicker.prototype.getValue = function() {
		return this.eInput.value;
	};

	// any cleanup we need to be done here
	Datepicker.prototype.destroy = function() {
		// but this example is simple, no cleanup, we could
		// even leave this method out as it's optional
	};

	// if true, then this editor will appear in a popup
	Datepicker.prototype.isPopup = function() {
		// and we could leave this method out also, false is the default
		return true;
	};

	return Datepicker;
}

/**
 * Datepicker component
 * <pre>
 *     cellEditor : 'dateTimer'
 * </pre>
 * @author gagamel
 * @since 2020. 1. 09
 */
var getDateTimer = function(pattern) {
	// function to act as a class
	function DateTimer() {};

	// gets called once before the renderer is used
	DateTimer.prototype.init = function(params) {
		this.eGui = document.createElement('div');

		// 시간 초기화
		var TimeH = '';
		var TimeM = '';
		var SecGb =  params.colDef.field;
		if( SecGb.indexOf('ed') > -1 || SecGb.indexOf('end') > -1) {
			TimeH = '23';
			TimeM = '59';
			this.sec = '59';
		}else{
			TimeH = '00';
			TimeM = '00';
			this.sec = '00';
		}

		if(typeof(params.value) != 'undefined') {
			var time = params.value.split(":");
			TimeH = time[0];
			TimeM = time[1];
		}

		// 시간 셀렉트 박스
		var time = 0;
		var Hhtml = '';
		Hhtml += '<select name="' + params.colDef.field + 'H">\n';
		for (var i = 0; i < 24; i++) {
			time = i;
			if ( time < 10)
				time = '0' + time;
			if (TimeH != i) {
				Hhtml += '<option value="' + time + '">' + time + '시</option>\n';
			} else {
				Hhtml += '<option selected="selected" value="' + time + '">' + time + '시</option>\n';
			}
		}
		Hhtml += '</select>\n';

		this.eGui.innerHTML = Hhtml;
		this.eSelectH = this.eGui.querySelector('select');
		this.eSelectH.innerHTML = Hhtml;

		// 분 셀렉트 박스
		time = 0;
		var Mhtml ='';
		Mhtml = '<select name="' + params.colDef.field + 'M">\n';
		for (var i = 0; i < 60; i++) {
			time = i;
			if ( time < 10)
				time = '0' + time;
			if (TimeM != i) {
				Mhtml += '<option value="' + time + '">' + time + '분</option>\n';
			} else {
				Mhtml += '<option selected="selected" value="' + time + '">' + time + '분</option>\n';
			}
		}
		Mhtml += '</select>';

		this.eGui.innerHTML = Mhtml;
		this.eSelectM = this.eGui.querySelector('select');
		this.eSelectM.innerHTML = Mhtml;
		this.eGui.innerHTML = Hhtml + Mhtml; // html 전체가 있어야 하기 떄문에 마지막에 전체 입력

		if (params.required) {
			this.eSelectH.required = 'required';
			this.eSelectM.required = 'required';
//			this.eInput.classList.add('required');
		}
	};

	// gets called once when grid ready to insert the element
	DateTimer.prototype.getGui = function() {
		return this.eGui;
	};

	// focus and select can be done after the gui is attached
	DateTimer.prototype.afterGuiAttached = function() {
		this.eSelectH.focus();
	};

	// returns the new value after editing
	DateTimer.prototype.getValue = function() {
		return $('[name=' + this.eSelectH.name + '] option:selected').val() + ':' + $('[name=' + this.eSelectM.name + '] option:selected').val() + ':' + this.sec ;
	};

	// any cleanup we need to be done here
	DateTimer.prototype.destroy = function() {
		// but this example is simple, no cleanup, we could
		// even leave this method out as it's optional
	};

	// if true, then this editor will appear in a popup
	DateTimer.prototype.isPopup = function() {
		// and we could leave this method out also, false is the default
		return false;
	};

	return DateTimer;
}

/**
 * Numeric editor component
 * <pre>
 *     cellEditor: 'numericCellEditor'
 * </pre>
 * @author gagamel
 * @since 2019. 4. 25
 */
var getNumericCellEditor = function() {
	function isCharNumeric(charStr) {
		return !!/\d/.test(charStr);
	}

	function getCharCodeFromEvent(event) {
		event = event || window.event;
		return (typeof event.which == "undefined") ? event.keyCode : event.which;
	}

	function isKeyPressedNumeric(event) {
		var charCode = getCharCodeFromEvent(event);
		var charStr = String.fromCharCode(charCode);
		return isCharNumeric(charStr);
	}

	// function to act as a class
	function NumericCellEditor() {};

	// gets called once before the renderer is used
	NumericCellEditor.prototype.init = function(params) {
		// create the cell
		this.eInput = document.createElement('input');

		if (isCharNumeric(params.charPress)) {
			this.eInput.value = params.charPress;
		} else {
			if (!gagajf.isNull(params.value)) {
				this.eInput.value = params.value;
			}
		}

		var that = this;
		this.eInput.addEventListener('keypress', function(event) {
			if (!isKeyPressedNumeric(event)) {
				that.eInput.focus();
				if (event.preventDefault) event.preventDefault();
			//} else if (that.isKeyPressedNavigation(event)) {
			} else if (isNavigationKey(event)) {
				event.stopPropagation();
			}
		});

		// only start edit if key pressed is a number, not a letter
		var charPressIsNotANumber = params.charPress && ('1234567890'.indexOf(params.charPress) < 0);
		this.cancelBeforeStart = charPressIsNotANumber;
	};

//	NumericCellEditor.prototype.isKeyPressedNavigation = function(event) {
//		return event.keyCode === 39 || event.keyCode === 37;
//	};

	// gets called once when grid ready to insert the element
	NumericCellEditor.prototype.getGui = function() {
		return this.eInput;
	};

	// focus and select can be done after the gui is attached
	NumericCellEditor.prototype.afterGuiAttached = function() {
		this.eInput.focus();
	};

	// returns the new value after editing
	NumericCellEditor.prototype.isCancelBeforeStart = function() {
		return this.cancelBeforeStart;
	};

	// example - will reject the number if it contains the value 007
	// - not very practical, but demonstrates the method.
	NumericCellEditor.prototype.isCancelAfterEnd = function() {
		var value = this.getValue();
		return value.indexOf('007') > -1;
	};

	// returns the new value after editing
	NumericCellEditor.prototype.getValue = function() {
		return this.eInput.value;
	};

	// any cleanup we need to be done here
	NumericCellEditor.prototype.destroy = function() {
		// but this example is simple, no cleanup, we could  even leave this method out as it's optional
	};

	// if true, then this editor will appear in a popup
	NumericCellEditor.prototype.isPopup = function() {
		// and we could leave this method out also, false is the default
		return false;
	};

	return NumericCellEditor;
}

/**
 * Cell renderer component for checkbox with value (Y/N)
 * <pre>
 *     cellRenderer: 'booleanCellRenderer'
 * </pre>
 * @author gagamel
 * @since 2019. 4. 8
 */
var getBooleanCellRenderer = function() {

	// function to act as a class
	function BooleanCellRenderer() {};

	// gets called once before the renderer is used
	BooleanCellRenderer.prototype.init = function(params) {
		this.eGui = document.createElement('span');

		if (!gagajf.isNull(params.value)) {
			var checked = (params.value == "Y" || params.value == "1") ? "checked" : "";
			var input = document.createElement('input');
			input.type = "checkbox";
			input.checked = checked;
			input.value = params.value;
			input.addEventListener('click', function (event) {
				if (params.value == "Y") params.value = "N";
				else if (params.value == "N") params.value = "Y";
				else if (params.value == "1") params.value = "0";
				else if (params.value == "0") params.value = "1";

				// checked input value has changed, perform your update here
				params.data[params.colDef.field] = params.value;
				params.data.crud = "U";
				params.api.updateRowData({update: [params.data]});
			});
			this.eGui.innerHTML = '';
			this.eGui.appendChild(input);
		}
	};

	// gets called once when grid ready to insert the element
	BooleanCellRenderer.prototype.getGui = function() {
		return this.eGui;
	};

	// focus and select can be done after the gui is attached
	BooleanCellRenderer.prototype.afterGuiAttached = function() {

	};

	// returns the new value after editing
	BooleanCellRenderer.prototype.getValue = function() {
		return this.eGui.value;
	};

	// any cleanup we need to be done here
	BooleanCellRenderer.prototype.destroy = function() {
		// but this example is simple, no cleanup, we could
		// even leave this method out as it's optional
	};

	// if true, then this editor will appear in a popup
	BooleanCellRenderer.prototype.isPopup = function() {
		// and we could leave this method out also, false is the default
		return false;
	};

	return BooleanCellRenderer;
}

/**
 * Checkbox editor component with value (Y/N)
 * <pre>
 *     cellEditor: 'booleanCellEditor'
 * </pre>
 * @author gagamel
 * @since 2019. 4. 8
 */
var getBooleanCellEditor = function() {

	// function to act as a class
	function BooleanCellEditor() {};

	// gets called once before the renderer is used
	BooleanCellEditor.prototype.init = function(params) {
		this.container = document.createElement('div');
		this.value = params.value;
		params.stopEditing();
	};

	// gets called once when grid ready to insert the element
	BooleanCellEditor.prototype.getGui = function() {
		return this.container;
	};

	// focus and select can be done after the gui is attached
	BooleanCellEditor.prototype.afterGuiAttached = function() {

	};

	BooleanCellEditor.prototype.getValue = function() {
		return this.value;
	};

	// any cleanup we need to be done here
	BooleanCellEditor.prototype.destroy = function() {

	};

	// if true, then this editor will appear in a popup
	BooleanCellEditor.prototype.isPopup = function() {
		return true;
	};

	return BooleanCellEditor;
}

///**
// * Cell renderer component for selectbox
// * <pre>
// *     cellRenderer: 'selectCellRenderer'
// * </pre>
// * @param comboList - Combo List
// * @author gagamel
// * @since 2019. 4. 29
// */
//var getSelectCellRenderer = function(comboList) {
//
//	// function to act as a class
//	function SelectCellRenderer() {};
//
//	// gets called once before the renderer is used
//	SelectCellRenderer.prototype.init = function(params) {
//		this.eGui = document.createElement('span');
//		var select = document.createElement('select');
//
//		console.log('========getSelectCellRenderer========');
//		console.log(comboList);
//
//		var options;
//		var comboLen = comboList.length;
//		for (var i = 0; i < comboLen; i++) {
//			options += '<option value="' + comboList[i].cd + '">' + comboList[i].cdNm + '</option>';
//		}
//
//		console.log(options);
//		select.appendChild(options);
//
//		//input.value = params.value;
//		this.eGui.innerHTML = '';
//		this.eGui.appendChild(select);
//	};
//
//	// gets called once when grid ready to insert the element
//	SelectCellRenderer.prototype.getGui = function() {
//		return this.eGui;
//	};
//
//	// focus and select can be done after the gui is attached
//	SelectCellRenderer.prototype.afterGuiAttached = function() {
//
//	};
//
//	// returns the new value after editing
//	SelectCellRenderer.prototype.getValue = function() {
//		return this.eGui.value;
//	};
//
//	// any cleanup we need to be done here
//	SelectCellRenderer.prototype.destroy = function() {
//		// but this example is simple, no cleanup, we could
//		// even leave this method out as it's optional
//	};
//
//	// if true, then this editor will appear in a popup
//	SelectCellRenderer.prototype.isPopup = function() {
//		// and we could leave this method out also, false is the default
//		return false;
//	};
//
//	return SelectCellRenderer;
//}
//
///**
// * Selectbox editor component
// * <pre>
// *     cellEditor: 'selectCellEditor'
// * </pre>
// * @author gagamel
// * @since 2019. 4. 29
// */
//var getSelectCellEditor = function() {
//	// function to act as a class
//	function SelectCellEditor() {};
//
//	// gets called once before the renderer is used
//	SelectCellEditor.prototype.init = function(params) {
//		// create the cell
//		this.eInput = document.createElement('select');
//		this.eInput.value = params.value;
//
//		var that = this;
//		this.eInput.addEventListener('change', function(event) {
//			console.log('SelectCellEditor change');
////			that.eInput.focus();
////			if (event.preventDefault) event.preventDefault();
//			event.stopPropagation();
//		});
//	};
//
//	// gets called once when grid ready to insert the element
//	SelectCellEditor.prototype.getGui = function() {
//		console.log('SelectCellEditor getGui');
//		return this.eInput;
//	};
//
//	// focus and select can be done after the gui is attached
//	SelectCellEditor.prototype.afterGuiAttached = function() {
//		console.log('SelectCellEditor afterGuiAttached');
//		this.eInput.focus();
//	};
//
//	// returns the new value after editing
//	SelectCellEditor.prototype.isCancelBeforeStart = function() {
//		console.log('SelectCellEditor isCancelBeforeStart');
//		//return this.cancelBeforeStart;
//	};
//
//	// not very practical, but demonstrates the method.
//	SelectCellEditor.prototype.isCancelAfterEnd = function() {
//		console.log('SelectCellEditor isCancelAfterEnd');
//	};
//
//	// returns the new value after editing
//	SelectCellEditor.prototype.getValue = function() {
//		console.log('SelectCellEditor getValue');
//		return this.eInput.value;
//	};
//
//	// any cleanup we need to be done here
//	SelectCellEditor.prototype.destroy = function() {
//		console.log('SelectCellEditor destroy');
//		// but this example is simple, no cleanup, we could  even leave this method out as it's optional
//	};
//
//	// if true, then this editor will appear in a popup
//	SelectCellEditor.prototype.isPopup = function() {
//		console.log('SelectCellEditor isPopup');
//		// and we could leave this method out also, false is the default
//		return false;
//	};
//
//	return SelectCellEditor;
//}

/**
 * Text Tooltip component
 * <pre>
 *     textTooltip: TextTooltip
 * </pre>
 * @author gagamel
 * @since 2019. 4. 29
 */
var getTextTooltip = function() {

	// function to act as a class
	function TextTooltip() {};

	TextTooltip.prototype.init = function(params) {
		var eGui = this.eGui = document.createElement('div');
		var color = params.color || 'white';

		eGui.classList.add('custom-tooltip');
		eGui.style['background-color'] = color;
		/*var data = params.api.getRowNode(params.rowIndex).data;
		eGui.innerHTML =
			'<p><span class"name">' + data.athlete + '</span></p>' +
			'<p><span>Country: </span>' + data.country + '</p>' +
			'<p><span>Total: </span>' + data.total + '</p>';*/

		eGui.innerHTML = '<p><span>' + params.value + '</span>';
	};

	TextTooltip.prototype.getGui = function() {
		return this.eGui;
	};

	return TextTooltip;
}


/**
 * Text cell editor component
 * <pre>
 *     cellEditor: 'textCellEditor'
 * </pre>
 * @author gagamel
 * @since 2019. 4. 30
 */
var getTextCellEditor = function() {

	// function to act as a class
	function TextCellEditor() {};

	TextCellEditor.prototype.init = function(params) {
		this.eGui = document.createElement('div');
		this.eGui.innerHTML = '<input'
			+ ' name="' + params.colDef.field + '"'
			+ (!gagajf.isNull(params.value) ? ' value="' + params.value + '"' : '')
			+ (!gagajf.isNull(params.maxlength) ? ' maxlength="' + params.maxlength + '"' : '')
			+ (!gagajf.isNull(params.validType) ? ' data-valid-type="' + params.validType + '"' : '')
			+ (!gagajf.isNull(params.onblur) ? ' onblur="' + params.onblur + '"' : '')
			+ ' style="width: 100%"/>';

		this.eInput = this.eGui.querySelector('input');

		if (params.required) {
			this.eInput.required = 'required';
//			this.eInput.classList.add('required');
		}

//		this.eInput.addEventListener('input', this.inputChanged.bind(this));

//		var that = this;
//		this.eInput.addEventListener('keypress', function(event) {
//			if (!isKeyPressedNumeric(event)) {
//				that.eInput.focus();
//				if (event.preventDefault) event.preventDefault();
//			//} else if (that.isKeyPressedNavigation(event)) {
//			} else if (isNavigationKey(event)) {
//				event.stopPropagation();
//			}
//		});

//		this.eInput.addEventListener('blur', function(event) {
//			if (params.required) {
//				if (gagajf.isNull(event.target.value)) {
//					alert(params.colDef.headerName + ' 을(를) 입력해 주세요.');
//					that.eInput.focus();
//					if (event.preventDefault) event.preventDefault();
//					event.stopPropagation();
//					return false;
//				}
//			}
//		});
	}

//	TextCellEditor.prototype.inputChanged = function(event) {
//		console.log('TextCellEditor.prototype.inputChanged');
//		console.log(event);
//
//		const val = event.target.value;
//
////		if (!this.isValid(val)) {
////			this.eInput.classList.add('invalid-cell');
////		} else {
////			this.eInput.classList.remove('invalid-cell');
////		}
//
//		if (this.eInput.required === 'required') {
//			if (gagajf.isNull(val)) {
//				alert('을(를) 입력해 주세요.');
//				return false;
//			}
//		}
//
//		return true;
//	}

//	TextCellEditor.prototype.isValid = function(value) {
//		return value.length <= this.maxLength;
//	}

	TextCellEditor.prototype.getValue = function() {
		return this.eInput.value;
	}

	TextCellEditor.prototype.isCancelAfterEnd = function() {
//		return !this.isValid(this.eInput.value);
	}

	TextCellEditor.prototype.getGui = function() {
		return this.eGui;
	}

	TextCellEditor.prototype.destroy = function() {
		this.eInput.removeEventListener('input', this.inputChanged);
	}

	return TextCellEditor;
}


/**
 * 공통 그리드 util
 */
var gagaAgGrid = {
	defaultBlank : " - ",
	eGridDiv : "",

	/**
	 * 기본 Grid Options을 얻는다.
	 * @param colDefs - Column Definition
	 */
	getGridOptions : function(colDefs) {
		return {
			// a default column definition with properties that get applied to every column
			defaultColDef: {
				sortable: true, // make every column sortable
				resizable: true, // make every column resizable
				filter: 'agTextColumnFilter', // make every column use 'text' filter by default
				tooltipComponent: 'textTooltip',
				suppressSizeToFit: true // this columns width to be fixed during 'size to fit' operation.
			},

//			floatingFilter: true, // display filter region

			// if we had column groups, we could provide default group items here
			defaultColGroupDef: {
				//marryChildren: true
			},

			// define a column type (you can define as many as you like)
			columnTypes: {
				boolean: {
					cellClass: 'text-center',
					/*cellRenderer: function(params) {
						return '<input type="checkbox" ' + (params.value ? "checked" : "") + '/>';
					},*/
					cellRenderer: 'booleanCellRenderer',
					cellEditor: 'booleanCellEditor'
				},
				numeric: {
					filter: 'agNumberColumnFilter'
				},
				date: {
					filter: 'agDateColumnFilter',
					filterParams: {
						comparator: function(filterLocalDateAtMidnight, cellValue) {
							// Dates are stored as yyyy/MM/dd
							// We create a Date object for comparison against the filter date
							var dateParts = cellValue.split('/');
							var year = Number(dateParts[0]);
							var month = Number(dateParts[1]) - 1;
							var day = Number(dateParts[2]);
							var cellDate = new Date(day, month, year);

							// Now that both parameters are Date objects, we can compare
							if (cellDate < filterLocalDateAtMidnight) {
								return -1;
							} else if (cellDate > filterLocalDateAtMidnight) {
								return 1;
							} else {
								return 0;
							}
						}
					},
					suppressMenu: true
				},
				measure: {
					// test 김유중
//					chartDataType: 'series',
//					cellClass: 'number',
					valueFormatter: 'numberCellFormatter',
					cellRenderer: 'agAnimateShowChangeCellRenderer'

				},
				numberValue: {
					enableValue: true,
					aggFunc: 'sum',
					editable: true,
//					valueParser: numberParser
				},
				nonEditable: {
					editable: false
				}
			},

			columnDefs: colDefs,

			// Columns
			suppressAutoSize: false, // suppresses auto-sizing columns

			// Selection
			//rowSelection: 'single', // 'single' or 'multiple'
			//suppressRowClickSelection: false,
			enableRangeSelection: true, // enable Range Selection

			// Rendering & Styling
			animateRows: true, // enable Row Animation

			// Localization
			localeText: {
				noRowsToShow: '조회 결과가 없습니다.'
			},

			// Overlays
			suppressLoadingOverlay: true, // disables the 'loading' overlay
			loadingOverlayComponent: 'customLoadingOverlay',
			loadingOverlayComponentParams: {
				loadingMessage: 'Loading... one moment please...'
			},

			// Scrolling
			//suppressHorizontalScroll: false,

			statusBar: {
				statusPanels: [
					{ statusPanel: 'agTotalRowCountComponent', align: 'left' },
					{ statusPanel: 'agFilteredRowCountComponent' },
					{ statusPanel: 'agSelectedRowCountComponent' },
					{ statusPanel: 'agAggregationComponent' }
				]
			},

			// 헤더의 열에 aggFunc명(예, sum, avg 등) 표시 제거
			suppressAggFuncInHeader: true,

			// Grouping Custom Function
			aggFuncs: {
				// this overrides the grids built in sum function
				'sum': this.sum,
				'avg': this.avg,
				'ratio': this.ratio
			},

			onGridReady: function(params) {
				params.api.sizeColumnsToFit(); // 자동 맞춤
			},

			// 창 크기 변경 되었을 때 이벤트
			onGridSizeChanged: function(params) {
				params.api.sizeColumnsToFit(); // 자동 맞춤
			},

//			debug: true,

			onCellValueChanged: function(event) {
				// if cell is editable and changed value
				if (event.colDef.editable && event.oldValue != event.newValue) {
					/*var cellClass = event.colDef.cellClass;
					event.colDef.cellClass = cellClass ? cellClass + ' modified' : 'modified';
					var row = this.api.getDisplayedRowAtIndex(event.rowIndex);
					this.api.redrawRows({ rowNodes: [row] });*/
				}
			},

			onCellEditingStopped: function(event) {
				if (event.colDef.editable && event.data.crud != "C") {
					event.data.crud = "U";
					//gridOptions.api.updateRowData({update: [event.data]});
				}
			},

			// 총합계, 소계 등을 표시하기 위한 row의 스타일
			getRowStyle: function(params) {
				if (params.node.rowPinned) {
					return {'font-weight': 'bold', 'color': '#721c24', 'background': '#f8d7da'};
				}
			},

			components:{
				customLoadingOverlay: getCustomLoadingOverlay(),
				booleanCellRenderer: getBooleanCellRenderer(),
				booleanCellEditor: getBooleanCellEditor(),
				numericCellEditor: getNumericCellEditor(),
				datePicker: getDatePicker(),
				dateTimer: getDateTimer(),
				textTooltip: getTextTooltip(),
				textCellEditor: getTextCellEditor(),
				ComboboxCellRenderer : getComboboxCellRenderer(),
				ComboboxHeaderComponent : getComboboxHeaderComponent()
			}
		};
	},

	/**
	 * Create a ag-Grid
	 * @param gridId - ag-Grid ID
	 * @param gridOptions - ag-Grid options
	 * @author gagamel
	 * @since 2019. 4. 8
	 */
	createGrid : function (gridId, gridOptions) {
		eGridDiv = document.querySelector('#' + gridId);
		if (typeof gridOptions.rowHeight == "undefined") gridOptions.rowHeight = 32;
		new agGrid.Grid(eGridDiv, gridOptions);
	},

	/**
	 * Progress bar
	 */
	showProgressbar : function(isLoading) {
		if (isLoading) {
			// Button disabled & progressBar creation
			$('.btn').each(function(idx) { $(this).attr('disabled', true); });
			var load_AjaxSubmit = '<div id="load_AjaxSubmit" style="'
				+ 'background: url(/ux/plugins/gaga/loader.gif); border-style: none; background-repeat: no-repeat; '
				+ 'position: absolute; top: 45%; left: 50%; width: auto; '
				+ 'z-index: 101; padding: 16px; margin: 5px;'
				+ '"></div>';
			$(eGridDiv).append(load_AjaxSubmit);
		} else {
			// Button activated & progressBar remove
			$('.btn').each(function(idx) { $(this).attr('disabled', false); });
			$('#load_AjaxSubmit').remove();
		}
	},

	/**
	 * Fetch data using json format.
	 * <pre>
	 *     var actionUrl = $('#searchForm').prop('action') + '?' + $('#searchForm').serialize();
	 *     gagaAgGrid.fetch(actionUrl, gridOptions);
	 * </pre>
	 * @param actionUrl - request URL. 필수
	 * @param gridOptions - ag-Grid options. 필수
	 * @param formId - form ID. option. 옵션
	 * @param callbackFn - Callback function. 옵션
	 * @author gagamel
	 * @since 2019. 4. 8
	 */
	fetch : function(actionUrl, gridOptions, formId, callbackFn) {
//		gridOptions.api.showLoadingOverlay();

		var _this = this;

		if (typeof(formId) == 'undefined' || gagajf.isNull(formId)) { // formId 값이 없으면
//			fetch(actionUrl).then(function(response) {
//				return response.json(); // promise 반환
//			}).then(function(data) {
//				gridOptions.api.setRowData(data);
//				_this.showProgressbar(false);
//			});

			$.ajax({
				type : 'GET',
				url : actionUrl,
				data : null,
				dataType : 'json',
				beforeSend : function(xhr, settings) {
					// Button disabled & progressBar creation
					_this.showProgressbar(true);
				},
				complete : function() {
					_this.showProgressbar(false);
				},
				success : function(data) {
					try {
						gridOptions.api.setRowData(data);
					} catch (e) {
						console.log(e);
						mcxDialog.alert('오류로 인해 처리되지 않았습니다.');
					}

					if (typeof(callbackFn) == "function") {
						callbackFn.call(this, data);
					}
				},
				error : function(data) {
					console.log(data);
					_this.showProgressbar(false);
					mcxDialog.alert('오류로 인해 처리되지 않았습니다.');
				}
			});
		} else { // formId 값이 있으면
			// comma(,) 제거
			gagajf.removeCommaAtNumberFormattedInput(formId);
			var jsonData = JSON.stringify($(formId).serializeObject());

			$.ajax({
				type : 'POST',
				url : actionUrl,
				data : jsonData,
				dataType : 'json',
				beforeSend : function(xhr, settings) {
					// dataType: "json"일 때
					xhr.setRequestHeader('Accept', 'application/json');
					xhr.setRequestHeader('Content-Type', 'application/json');

					// Button disabled & progressBar creation
					_this.showProgressbar(true);
				},
				complete : function() {
					_this.showProgressbar(false);
				},
				success : function(data) {
					try {
						gridOptions.api.setRowData(data);
					} catch (e) {
						console.log(e);
						mcxDialog.alert('오류로 인해 처리되지 않았습니다.');
					}

					if (typeof(callbackFn) == "function") {
						callbackFn.call(this, data);
					}
				},
				error : function(data) {
					console.log(data);
					_this.showProgressbar(false);
					mcxDialog.alert('오류로 인해 처리되지 않았습니다.');
				}
			});

//			var options = {
//				method: "POST",
//				headers: { "Content-Type": "application/json; charset=utf-8" },
//				body: jsonData
//			};
//
//			fetch(actionUrl, options).then(function(response) {
//				return response.json(); // promise 반환
//			}).then(function(data) {
//				gridOptions.api.setRowData(data);
//				_this.showProgressbar(false);
//			});
		}

		// Button activated & progressBar remove

//		gridOptions.api.hideOverlay();
	},

	/**
	 * 그리드의 총건수 가져오기
	 * <pre>
	 *     gagaAgGrid.getTotalCount(gridOptions);
	 * </pre>
	 * @param gridOptions - ag-Grid options
	 * @author gagamel
	 * @since 2019. 5. 8
	 */
	getTotalCount : function(gridOptions) {
		return gridOptions.api.getDisplayedRowCount();
	},

	/**
	 * 그리드 컬럼을 보이기/감추기
	 * <pre>
	 *     gagaAgGrid.showOrHideColumn(gridOptions, "useYn", false);
	 * </pre>
	 * @param gridOptions - ag-Grid options
	 * @param colKey - column ID
	 * @param isShow - show or hide (true/false)
	 * @author gagamel
	 * @since 2019. 4. 8
	 */
	showOrHideColumn : function(gridOptions, colKey, isShow) {
		gridOptions.columnApi.setColumnVisible(colKey, isShow);
	},

	/**
	 * 그리드 컬럼의 Header명을 설정
	 * <pre>
	 *     gagaAgGrid.setColumnHeaderName(gridOptions, 'M1', '1월');
	 * </pre>
	 * @param gridOptions - ag-Grid options
	 * @param colKey - column ID or column group ID
	 * @param headerName - header name
	 * @author gagamel
	 * @since 2019. 5. 7
	 */
	setColumnHeaderName : function(gridOptions, colKey, headerName) {
		gridOptions.columnApi.getColumn(colKey).colDef.headerName = headerName;
//		gridOptions.columnApi.resetColumnState();
	},

	/**
	 * 그리드 컬럼 그룹의 Header명을 설정
	 * <pre>
	 *     gagaAgGrid.setColumnGroupHeaderName(gridOptions, 'M1', '1월');
	 * </pre>
	 * @param gridOptions - ag-Grid options
	 * @param colKey - column ID
	 * @param headerName - header name
	 * @author gagamel
	 * @since 2019. 5. 7
	 */
	setColumnGroupHeaderName : function(gridOptions, colKey, headerName) {
		gridOptions.columnApi.getColumn(colKey).parent.originalColumnGroup.colGroupDef.headerName = headerName;
//		gridOptions.columnApi.resetColumnState();
	},

	/**
	 * 그리드 내에 변경된 데이터를 배열로 반환한다.
	 * <pre>
	 *     var changedData = gagaAgGrid.getChangedData(gridOptions);
	 * </pre>
	 * @param gridOptions - ag-Grid options
	 * @author gagamel
	 * @since 2019. 4. 8
	 */
	getChangedData : function(gridOptions) {
		// Stop editing
		gridOptions.api.stopEditing();

		var changedData = [];

		gridOptions.api.forEachNode(function(rowNode, index) {
			if (rowNode.data.crud == "C" || rowNode.data.crud == "U" || rowNode.data.crud == "D") {
				rowNode.data.index = index;
				changedData.push(rowNode.data);
			}
		});

		return changedData;
	},

	/**
	 * 그리드 첫번째 row에 data를 갖는 행을 추가한다.
	 * <pre>
	 *     var data = { cdGb: "", cd: "", cdNm: "", cdChar: "40", cdNum: 1, cdDt: today, dispRk: 1, useYn: "Y" };
	 *     gagaAgGrid.addRowData(gridOptions, data, "cdGb");
	 * </pre>
	 * @param gridOptions - ag-Grid options
	 * @param data - 추가할 데이터
	 * @param focusedColKey - Column ID to focus
	 * @author gagamel
	 * @since 2019. 4. 9
	 */
	addRowData : function(gridOptions, data, focusedColKey) {
		data.crud = "C";

		gridOptions.api.updateRowData({add: [data], addIndex: 0});

		// Focused cell
		gridOptions.api.setFocusedCell(0, focusedColKey, null);

		// start editing cell
		//gridOptions.api.startEditing({rowIndex: 0, colKey: focusedColKey});
	},

	/**
	 * 그리드 내에 선택된 데이터를 가져온다
	 * <pre>
	 *     gagaAgGrid.selectedRowData(gridOptions);
	 * </pre>
	 * @param gridOptions - ag-Grid options
	 * @author gagamel
	 * @since 2019. 7. 9
	 */
	selectedRowData : function(gridOptions) {
		return gridOptions.api.getSelectedRows();
	},

	/**
	 * 그리드 내에 전체 데이터를 가져온다
	 * <pre>
	 *     gagaAgGrid.getAllRowData(gridOptions);
	 * </pre>
	 * @param gridOptions - ag-Grid options
	 * @author qkwlstktma
	 * @since 2019. 7. 25
	 */
	getAllRowData : function(gridOptions){
		var data = [];

		gridOptions.api.forEachNode(function(rowNode, index) {
			data.push(rowNode.data);
		});

		return data;
	},

	/**
	 * 그리드 내에 선택된 데이터를 삭제하고 배열에 담아 반환한다.
	 * 실제 삭제되지 않고 해당 행이 보이지 않게 된다.
	 * <pre>
	 *     gagaAgGrid.removeRowData(gridOptions);
	 * </pre>
	 * @param gridOptions - ag-Grid options
	 * @param isShow - Row 보이기(true/false. 디폴트는 보이는 것으로). 옵션
	 * @author gagamel
	 * @since 2019. 4. 9
	 */
	removeRowData : function(gridOptions, isShow) {
		var selectedData = gridOptions.api.getSelectedRows();

		var removedData = [];

		selectedData.forEach(function(item, index) {
			if (item.crud == 'C') {
				gridOptions.api.updateRowData({remove: [item]});
			} else {
				removedData.push(item);
				if (typeof(isShow) != 'undefined' && !isShow) {
					gridOptions.api.updateRowData({remove: [item]});
				}
			}
		});

		return removedData;
	},

	/**
	 * 그리드 내에 고정된(pinned) top 또는 bottom에 데이터 표시
	 * <pre>
	 *     var data = {
	 *         sellDt: '총합계',
	 *         orderQty: 100, orderAmt: 10000,
	 *     };
	 *     gagaAgGrid.setPinnedRowData(gridOptions, data, 'top');
	 * </pre>
	 * @param gridOptions - ag-Grid options. 필수
	 * @param data - 고정된 row에 보일 데이터. 필수
	 * @param topBottom - top/bottom. 옵션
	 * @author gagamel
	 * @since 2019. 9. 7
	 */
	setPinnedRowData : function(gridOptions, data, topBottom) {
		if (typeof(topBottom) == 'undefined' || topBottom == 'top') {
			gridOptions.api.setPinnedTopRowData([data]);
		} else {
			gridOptions.api.setPinnedBottomRowData([data]);
		}
	},

	checkRequired : function(gridOptions, validItem) {
		var isInvalid = true;

		for (var i = 0; i < gridOptions.columnDefs.length; i++) {
			var column = gridOptions.columnDefs[i];

			if (typeof(column.cellEditorParams) == 'undefined')
				continue;

			if (typeof(column.cellEditorParams.required) == 'undefined')
				continue;

			if (!column.cellEditorParams.required)
				continue;

			if (!gagajf.isNull(validItem[column.field]))
				continue;

			mcxDialog.alert(column.headerName + '은[는] 필수 입력 항목입니다.');

			// Focused cell
			gridOptions.api.setFocusedCell(validItem.index, column.field, null);

			isInvalid = false;
			break;
		}

		return isInvalid;
	},

	validation : function(gridOptions, validData) {
		var isInvalid = true;

		validData.every(function(item, index) {
			if (gagaAgGrid.checkRequired(gridOptions, item))
				return true;

			isInvalid = false;
			return false;
		});

		return isInvalid;
	},

	/*// 그리드 값 넣기
	setRowData : function (gridOptions, rowData) {
		//$('#'+gridDiv).children().remove();
		gridOptions.api.setRowData(rowData);
	},*/

	/**
	 * mappings(코드목록배열) 중에 명칭만을 배열로 구성해 반환한다.
	 * <pre>
	 *     cellEditorParams: { values: gagaAgGrid.extractValues(mappings) }
	 * </pre>
	 * @param mappings - 코드목록배열. 필수
	 *     예) { "10":"항공(AIR)", "20":"해운(OCEAN)", ... }
	 * @author gagamel
	 * @since 2019. 4. 7
	 */
	extractValues : function(mappings) {
		return Object.keys(mappings);
	},

	/**
	 * mappings(코드목록배열) 중에 키(key)에 메핑되는 값을 반환한다.
	 * <pre>
	 *     valueFormatter: function (params) { return gagaAgGrid.lookupValue(mappings, params.value); }
	 * </pre>
	 * @param mappings - 코드목록배열. 필수
	 *     예) { "10":"항공(AIR)", "20":"해운(OCEAN)", ... }
	 * @param key - 키. 필수
	 * @author gagamel
	 * @since 2019. 4. 7
	 */
	lookupValue : function(mappings, key) {
		return mappings[key];
	},

	/**
	 * mappings(코드목록배열) 중에 name(콤보박스 option text)에 메핑되는 Key를 반환한다.
	 * <pre>
	 *     valueParser: function (params) { return gagaAgGrid.lookupKey(mappings, params.newValue); }
	 * </pre>
	 * @param mappings - 코드목록배열. 필수
	 *     예) { "10":"항공(AIR)", "20":"해운(OCEAN)", ... }
	 * @param name - 콤보박스 option text. 필수
	 * @author gagamel
	 * @since 2019. 4. 7
	 */
	lookupKey : function(mappings, name) {
		for (var key in mappings) {
			if (mappings.hasOwnProperty(key)) {
				if (name === mappings[key]) {
					return key;
				}
			}
		}
	},

	/**
	 * value를 yyyy-MM-dd 형식으로 반환
	 * <pre>
	 *     cellRenderer: function (params) { return gagaAgGrid.toDateFormat(params.value); }
	 * </pre>
	 * @param value - 일자 (yyyyMMdd 형식)
	 * @author gagamel
	 * @since 2019. 4. 8
	 */
	toDateFormat : function(value) {
		if (gagajf.isNull(value))
			return "";

		return value.replaceAll("/", "").replaceAll("-", "").toDate("YYYYMMDD").format("YYYY-MM-DD");
	},

	/**
	 * value를 yyyy-MM-dd HH:mm:ss 형식으로 반환
	 * <pre>
	 *     cellRenderer: function (params) { return gagaAgGrid.toDateTimeFormat(params.value); }
	 * </pre>
	 * @param value - 일자 (yyyyMMddHHmmss 형식)
	 * @param format - 일자표현식
	 * @author gagamel
	 * @since 2019. 6. 7
	 */
	toDateTimeFormat : function(value, format) {
		if (gagajf.isNull(value))
			return "";

		var rst = "";

		if (format == "YYYYMMDD") {
			rst = value.replace(/[^0-9]/g, "").toDate("YYYYMMDDHHmmss").format("YYYYMMDD");
		} else if (format == "hh:mm:ss") {
			rst = value.replace(/[^0-9]/g, "").toDate("YYYYMMDDHHmmss").format("hh:mm:ss");
		} else if (format == "yyyy-MM-dd") {
			rst = value.replace(/[^0-9]/g, "").toDate("YYYYMMDDHHmmss").format("YYYY-MM-DD");
		} else {
			rst = value.replace(/[^0-9]/g, "").toDate("YYYYMMDDHHmmss").format("YYYY-MM-DD HH:mm:ss");
		}

		return rst;
	},

	/**
	 * value가 숫자 타입일 때 comma(,)를 붙여 반환
	 * <pre>
	 *     cellRenderer: function (params) { return gagaAgGrid.toAddComma(params.value); }
	 * </pre>
	 * @param value - 숫자
	 * @author gagamel
	 * @since 2019. 9. 8
	 */
	toAddComma : function(value) {
		if (gagajf.isNull(value) || typeof value != 'number')
			return '';

		return value.addComma();
	},

	/**
	 * value가 실수 타입일 때 comma(,)를 붙여 반환 소수점 n까지 반환
	 * <pre>
	 *     cellRenderer: function (params) { return gagaAgGrid.toFixed(params.value, n); }
	 * </pre>
	 * @param value - 숫자
	 * @author ldh
	 * @since 2019. 12. 10
	 */
	toFixed : function(value, n) {
		if (gagajf.isNull(value) || typeof value != 'number')
			return '';

		return value.toFixed(n);
	},

	/*
	convertTime : function (param){
		if (!param.value) {
			return this.defaultBlank;
		} else {
			return moment(param.value, "YYYYMMDDHHmmss").format("HH:mm");
		}
	},

	convertTimestamp : function(param) { // 8자리 문자열을 날짜로 변환
		if (!param.value) {
			return this.defaultBlank;
		} else {
			return moment(param.value, "YYYYMMDDHHmmss").format("YYYY-MM-DD HH:mm:ss");
		}
	},

	formatDate : function(param) { // long 값을 날짜로 변환
		if (!param.value) {
			return this.defaultBlank;
		} else {
			return moment(parseInt(param.value)).format("YYYY-MM-DD");
		}
	},

	formatTimestamp : function(param) { // long 값을 날짜로 변환
		if (!param.value) {
			return this.defaultBlank;
		} else {
			return moment(parseInt(param.value)).format("YYYY-MM-DD HH:mm:ss");
		}
	},

	numberOnly : function(param){
		if (!param.value || param.value =='')
			return '';

		if (typeof param.value === 'string') {
			return param.value.replace(/[^0-9\.]+/g, '');
		}

		return CommonGrid.formatCurrency(param);
	},

	formatCurrency : function (param){
		if (!param.value && param.value != "0") {
			return this.defaultBlank;
		}

		return parseInt(param.value).format();
	},

	formatNumber : function(param){
		if (!param.value && param.value != "0") {
			return this.defaultBlank;
		}

		return numberFormatPoint2Digits(param.value);
	},

	convertCommon : function(codes, param){
		if (!codes) return " - ";
		for (var i = 0; i < codes.length;i++) {
			if (param.value == codes[i].name) {
				return codes[i].value;
			}
		}
		return this.defaultBlank;
	},

	getCode : function(codes, value) {
		for (var i = 0; i < codes.length; i++) {
			if (value == codes[i].value) {
				return codes[i].name;
			}
		}

		return "";
	},

	checkMobile : function(gridOpts){
		// 모바일 브라우저 가로크기 체크
		if (document.body.clientWidth < 800) {
			if (gridOpts){
				gridOpts.columnDefs.forEach(function(el) {
					el.pinned = null;
					if(el.children){
						el.children.forEach(function(subEl) {
							subEl.pinned = null;
						});
					}
				});
			}
		}
	},

	makeTopGrid : function(gridMainOpts, rowData, params) {
		var colsSum = [];

		gridMainOpts.columnDefs.forEach(function(coldefs, index) {
			if (coldefs.children) {
				coldefs.children.forEach(function(child, index) {
					if (child.isSum && child.isSum == true){
						var temp = {};
						temp[child.field] = 0;
						temp["field"] = child.field;
						colsSum.push(temp);
					}
				});
			} else {
				if (coldefs.isSum && coldefs.isSum == true) {
					var temp = {};
					temp[coldefs.field] = 0;
					temp["field"] = coldefs.field;
					colsSum.push(temp);
				}
			}
		});

		rowData.forEach(function(rowvalue, index) {
			colsSum.forEach(function(value, index) {
				if (rowvalue[value.field] && !rowvalue[value.field].isNaN)
					value[value.field] += rowvalue[value.field];
			});
		});

		var resultSum = {}
		colsSum.forEach(function(cValue, index) {
			resultSum[cValue.field] = cValue[cValue.field];
		});

		if (params && params.length > 0) {
			params.forEach(function(value, index) {
				resultSum[value.field] = value.value;
			});
		}

		resultSum['top'] = true;

		var resultArray = [];
		resultArray[0] = resultSum;
		gridMainOpts.api.setPinnedTopRowData(resultArray);
	}*/

	// sum function has no advantage over the built in sum function.

	// it's shown here as it's the simplest form of aggregation and
	// showing it can be good as a starting point for understanding
	// how the aggregation functions work.
	sum : function(values) {
		var result = 0;
		values.forEach(function(value) {
			if (typeof value === 'number') {
				result += value;
			}
		});
		return result;
	},

	// Average function
	avg : function(values) {
		var result = 0;
		var cnt = 0;
		values.forEach(function(value) {
			if (typeof value === 'number') {
				result += value;
				cnt++;
			}
		});
		return gagaAgGrid.toAddComma(Number(gagaAgGrid.toFixed(result / cnt)));
	},

	// Ratio function
	ratio : function(values) {
		var value1 = 0;
		var value2 = 0;

		values.forEach(function(value) {
			if (value && value.value1) {
				value1 += value.value1;
			}
			if (value && value.value2) {
				value2 += value.value2;
			}
		});

		return gagaAgGrid.setRatio(value1, value2);
	},

	/**
	 * 2개의 값으로 비율값을 계산해 설정한다.
	 * <pre>
	 *     valueGetter: function (params) { if (!params.node.group) return gagaAgGrid.setRatio(params.data.payAmt, params.data.sellTagAmt); }
	 * </pre>
	 * @param value1 - 분자
	 * @param value2 - 분모
	 * @author gagamel
	 * @since 2020. 5. 11
	 */
	setRatio : function(value1, value2) {
		return {
			value1: value1,
			value2: value2,
			toString: function() {
				return value1 && value2 ? gagaAgGrid.toFixed(value1 / value2 * 100, 2) : 0;
			}
		};
	},

	// csv 파일 export
	exportToCsv : function(title, gridOptions) {
		var params = {
			skipHeader: false,
			skipFooters: false,
			columnGroups: true,
			skipGroups: false,
			skipPinnedTop: false,
			skipPinnedBottom: false,
			allColumns: true,
			onlySelected: false,
			fileName: title + moment().format("YYYYMMDDHHmmss") + '.csv'
		};

		gridOptions.api.exportDataAsCsv(params);
	},

	/**
	 * 그리드 데이터를 엑셀로 내보내기
	 * <pre>
	 * 		gagaAgGrid.exportToExcel('주문목록', gridOptionsOrderList, columnDefs);
	 * 		or
	 * 		gagaAgGrid.exportToExcel('주문목록', gridOptionsOrderList, columnDefs, true);
	 * </pre>
	 * @param title - 엑셀의 타이틀
	 * @param gridOptions - Grid options
	 * @param columnDefs - Column definitions
	 * @param isSelected - true/false
	 * @author gagamel
	 * @since 2019. 6. 7
	 */
	exportToExcel  : function(title, gridOptions, isSelected) {
		var bOnlySelected = (typeof(isSelected) == 'undefined') ? false : isSelected;

		var params = {
			onlySelected: bOnlySelected, // true: Only export selected rows
			fileName : title + "_" + new Date().format("YYYYMMDDHHmmss"),
			sheetName: "DATA"/* ,
			customHeader: title,
			customFooter: "Copyright(c) 2019 TSIT, All rights reserved." */
		};

		gridOptions.excelStyles = [
			{
				id: 'dateFormat',
				dataType: 'dateTime',
				numberFormat: {
					format: 'YYYY-MM-DD;@'
				}
			},
			{
				id: 'textFormat',
				dataType: 'string'
			}
		]

		gridOptions.api.exportDataAsExcel(params);
	},

}

/*var gDefaultTxt = '';
var gIsDisplayCode = true;
var ComboboxCellRenderer = function(defaultTxt, isDisplayCode) {
	if (defaultTxt) gDefaultTxt = defaultTxt;
	if (isDisplayCode) gIsDisplayCode = isDisplayCode;
}

ComboboxCellRenderer.prototype.init = function(params) {
	this.eGui = document.createElement('span');
	if (!gagajf.isNull(params.value)) {
		var checked = (params.value == "Y" || params.value == "1") ? "checked" : "";
		var input = document.createElement('input');
		input.type = "checkbox";
		input.checked = checked;
		input.value = params.value;
		input.addEventListener('click', function (event) {
			if (params.value == "Y") params.value = "N";
			else if (params.value == "N") params.value = "Y";
			else if (params.value == "1") params.value = "0";
			else if (params.value == "0") params.value = "1";
			//checked input value has changed, perform your update here
			console.log("addEventListener params.value: "+ params.value);
		});
		this.eGui.innerHTML = '';
		this.eGui.appendChild(input);
	}
};

ComboboxCellRenderer.prototype.getGui = function() {
	return this.eGui;
};*/

// cellRenderer: 'ComboboxCellRenderer'
var getComboboxCellRenderer = function() {
	function ComboboxCellRenderer() {};

	ComboboxCellRenderer.prototype.init = function(params) {
		this.eGui = document.createElement('span');
		var span1 = document.createElement('span');
		var span2 = document.createElement('span');
		var span3 = document.createElement('span');
		this.eGui.classList = 'cellChecked'
		span1.classList = 'ag-selection-checkbox';
		var check = 'params.data.' + params.colDef.field;
		$(".allChecked").closest('div').each(function(){
			if($(this).attr('col-id')==params.colDef.field){
				if($(this).children('.allChecked').children('.ag-selection-checkbox').children('.ag-icon-checkbox-unchecked').hasClass('ag-hidden')){
					eval(check+' = "Y"');
				}else if($(this).children('.allChecked').hasClass('uncheckedAll')){
					eval(check+' = "N"');
				}
			}
		});
		if(eval(check)=='Y'){
			span2.classList = 'ag-icon ag-icon-checkbox-checked';
			span3.classList = 'ag-icon ag-icon-checkbox-unchecked ag-hidden';
		}else{
			eval(check+' = "N"');
			span2.classList = 'ag-icon ag-icon-checkbox-checked ag-hidden';
			span3.classList = 'ag-icon ag-icon-checkbox-unchecked';
		}
		this.eGui.addEventListener('click', function (event) {
			if(span2.classList.contains('ag-hidden')){
				eval(check+' = "Y"');
				span2.classList.remove('ag-hidden');
				span3.classList.add('ag-hidden');
				$(".allChecked").closest('div').each(function(){
					if($(this).attr('col-id')==params.colDef.field){
						$(this).children('.allChecked').removeClass('uncheckedAll');
					}
				});
			}else{
				eval(check+' = "N"');
				span2.classList.add('ag-hidden');
				span3.classList.remove('ag-hidden');
				$(".allChecked").closest('div').each(function(){
					if($(this).attr('col-id')==params.colDef.field){
						$(this).children('.allChecked').children('.ag-selection-checkbox').children('.ag-icon-checkbox-checked').addClass('ag-hidden');
						$(this).children('.allChecked').children('.ag-selection-checkbox').children('.ag-icon-checkbox-unchecked').removeClass('ag-hidden');
					}
				});
			}
		});
		span1.appendChild(span2);
		span1.appendChild(span3);
		this.eGui.innerHTML = '';
		this.eGui.appendChild(span1);
	};

	ComboboxCellRenderer.prototype.getGui = function() {
		return this.eGui;
	};

	return ComboboxCellRenderer;
}

// headerComponent : 'ComboboxHeaderComponent'
var getComboboxHeaderComponent = function() {
	function ComboboxHeaderComponent() {};

	ComboboxHeaderComponent.prototype.init = function(params) {
		this.eGui = document.createElement('span');
		var span1 = document.createElement('span');
		var span2 = document.createElement('span');
		var span3 = document.createElement('span');
		this.eGui.classList = 'allChecked'
		span1.classList = 'ag-selection-checkbox';
		span2.classList = 'ag-icon ag-icon-checkbox-checked ag-hidden';
		span3.classList = 'ag-icon ag-icon-checkbox-unchecked';
		this.eGui.addEventListener('click', function (event) {
			if(span2.classList.contains('ag-hidden')){
				this.classList.remove('uncheckedAll');
				span2.classList.remove('ag-hidden');
				span3.classList.add('ag-hidden');
				$(".ag-icon-checkbox-checked").closest('div').each(function(){
					if($(this).attr('col-id')==params.column.colId){
						$(this).children('.cellChecked').children('.ag-selection-checkbox').children('.ag-icon-checkbox-checked').addClass('ag-hidden');
						$(this).children('.cellChecked').children('.ag-selection-checkbox').children('.ag-icon-checkbox-unchecked').removeClass('ag-hidden');
						$(this).children('.cellChecked').click();
					}
				});
			}else{
				this.classList.add('uncheckedAll');
				span2.classList.add('ag-hidden');
				span3.classList.remove('ag-hidden');
				$(".ag-icon-checkbox-checked").closest('div').each(function(){
					if($(this).attr('col-id')==params.column.colId){
						$(this).children('.cellChecked').children('.ag-selection-checkbox').children('.ag-icon-checkbox-checked').removeClass('ag-hidden');
						$(this).children('.cellChecked').children('.ag-selection-checkbox').children('.ag-icon-checkbox-unchecked').addClass('ag-hidden');
						$(this).children('.cellChecked').click();
					}
				});
			}
		});

		span1.appendChild(span2);
		span1.appendChild(span3);
		span1.append(" "+params.displayName);
		this.eGui.innerHTML = '';
		this.eGui.appendChild(span1);
	};

	ComboboxHeaderComponent.prototype.getGui = function() {
		return this.eGui;
	};

	return ComboboxHeaderComponent;
}
