jui.defineUI("grid.table", [ "jquery", "util.base", "ui.dropdown", "grid.base", "grid.row" ], function($, _, dropdown, Base, Row) { _.resize(function() { var call_list = jui.get("grid.table"); for(var i = 0; i < call_list.length; i++) { var ui_list = call_list[i]; for(var j = 0; j < ui_list.length; j++) { ui_list[j].resize(); } } }, 1000); /** * @class grid.table * @extends core * @alias Table * @requires jquery * @requires util.base * @requires ui.dropdown * @requires grid.table.base * */ var UI = function() { var $obj = null, ddUi = null; // table/thead/tbody 구성요소, 컬럼 설정 UI (Dropdown) var selectedIndex = null, editableIndex = null, dragIndex = null, expandedIndex = null, checkedIndexes = {}; // TODO: 로우 객체 기반으로 변경하기 (#8) var is_resize = false; function getExpandHtml(self) { return "<tr class='expand' style='display: none;'><td id='EXPAND_" + self.timestamp + "'></td></tr>"; } function getColumnIndexes(self, colkeys) { var indexList = []; for(var i = 0; i < colkeys.length; i++) { if(typeof(colkeys[i]) == "string") { var column = self.getColumn(colkeys[i]); indexList.push(column.index); } else { indexList.push(colkeys[i]); } } return indexList; } function setColumnStatus(self) { var colkeys = self.options.colshow, len = self.uit.getColumnCount(); if(colkeys === true) { self.options.colshow = colkeys = []; for(var i = 0; i < len; i++) { colkeys.push(i); } } else { colkeys = getColumnIndexes(self, colkeys); } for(var i = 0; i < len; i++) { if($.inArray(i, colkeys) == -1) self.uit.hideColumn(i); else self.uit.showColumn(i); } } function setColumnMenu(self) { var columns = self.listColumn(), columnNames = []; for(var i = 0; i < columns.length; i++) { columnNames.push($(columns[i].element).text()); } var $ddObj = $(self.tpl.menu({ columns: columnNames })); $("body").append($ddObj); ddUi = dropdown($ddObj, { close: false }); $(ddUi.root).find("input[type=checkbox]").each(function(i) { if(columns[i].type == "show") this.checked = true; else this.checked = false; self.addEvent(this, "click", function(e) { var ckCount = $(ddUi.root).find("input[type=checkbox]:checked").size(); if(this.checked) { self.showColumn(i, e); } else { if(ckCount > 0) { self.hideColumn(i, e); } else { this.checked = true; } } self.hideExpand(); self.scroll(); }); }); } function setScrollResize(self) { var tableWidth = $obj.table.outerWidth(), thCount = self.uit.getColumnCount(), isLastCheck = false; for(var i = thCount - 1; i >= 0; i--) { var colInfo = self.getColumn(i), thWidth = $(colInfo.element).outerWidth(); // 마지막 TD는 스크롤 사이즈를 차감 if($(colInfo.element).css("display") == "none") {} else { if(!isLastCheck) { thWidth = thWidth - getScrollWidth(); isLastCheck = true; } } $(colInfo.list[0]).outerWidth(thWidth); } $obj.tbody.outerWidth(tableWidth); } function setScrollEvent(self) { if(!$(self.root).hasClass("table-scroll")) { // 스크롤일 경우, 별도 처리 self.scroll(); } $obj.tbody.off("scroll").scroll(function(e) { if(($obj.tbody.scrollTop() + self.options.scrollHeight) == $obj.tbody.get(0).scrollHeight){ self.emit("scroll", e); return false; } }); } function setUpdateInit(self, isInit) { if(self.uit.getRowCount() < 1) return; if(isInit) { if(self.options.expand) { $obj.tbody.prepend(getExpandHtml(self)); } self.scroll(); } if(self.options.scroll) { // 스크롤 이벤트 처리 setScrollEvent(self); } self.setVo(); } function setEventRows(self, rows) { var rows = (!rows) ? self.uit.getRow() : rows; for(var i = 0; i < rows.length; i++) { (function(row) { if(row.element == null) return; if(row.children.length > 0) { setEventRow(self, row); setEventRows(self, row.children); } else { setEventRow(self, row); } })(rows[i]) } } function setEventRow(self, row) { self.addEvent(row.element, "click", function(e) { // 1. 공통 이벤트 발생 self.emit("select", [ row, e ]); // deprecated self.emit("click", [ row, e ]); // 2. 확장영역 자동 이벤트 처리 if(self.options.expand) { if(self.options.expandEvent === false) return; var expandedRow = (expandedIndex instanceof Row) ? expandedIndex : self.get(expandedIndex); // TODO: #8 가상스크롤 지원 이슈로 인한 사이드이펙트 if(expandedRow === row) { self.hideExpand(e); } else { if(expandedIndex != null) { self.hideExpand(e); } self.showExpand(row, undefined, e); } } }); self.addEvent(row.element, "dblclick", function(e) { self.emit("dblclick", [ row, e ]); }); self.addEvent(row.element, "contextmenu", function(e) { self.emit("rowmenu", [ row, e ]); return false; }); // 로우 수정 이벤트 설정 if(self.options.editRow && self.options.editEvent) { self.addEvent(row.element, "dblclick", function(e) { if(e.target.tagName == "TD" || e.target.tagName == "TR") { self.showEditRow(row.index, e); } }); } // 로우 이동 이벤트 설정 if(self.options.moveRow) { // 드래그 시작 이벤트 self.addEvent(row.element, "mousedown", function(e) { if(dragIndex != null) return; dragIndex = row.index; // 테이블 상태 초기화 $obj.tbody.find("tr").removeClass("dragtarget"); $(row.element).addClass("dragtarget"); $("body").append(createRow(row.element)); }); // 마우스 오버시 라인 위치 변경 이벤트 self.addEvent(row.element, "mouseover", function(e) { if(dragIndex == null) return; $obj.tbody.find(".dragline").remove(); createLine().insertBefore(row.element); }); self.addEvent(document, "mouseover", function(e) { if(dragIndex == null || e.target.tagName == "TD" || e.target.tagName == "TR") return; $obj.tbody.find(".dragline").remove(); $obj.tbody.append(createLine()); }); // 마우스 이동시 클론 로우 위치 변경 이벤트 self.addEvent(row.element, "mousemove", function(e) { if(dragIndex == null) return; $("#TABLE_LAYER_" + self.timestamp).css({ left: e.pageX + 2, top: e.pageY + 2, display: "table" }); }); // 마우스 드래그 완료시 처리 이벤트 self.addEvent(row.element, "mouseup", function(e) { moveDragEnd(row.index, e); }); self.addEvent($obj.thead, "mouseover", function(e) { moveDragEnd(0, e); }); self.addEvent(document, "mouseup", function(e) { moveDragEnd(self.count(), e); }); function createLine() { return $("<tr class='dragline'><td colspan='" + row.list.length + "'></td></tr>"); } function createRow(element) { var $clone = $("<table id='TABLE_LAYER_" + self.timestamp + "' class='" + $(self.root).attr("class") + " layer'></table>"), $cloneRow = $(element).clone(); $clone.css({ position: "absolute", width: $(self.root).width(), display: "none" }); $cloneRow.attr({ "class": "dragclone" }); $clone.append($cloneRow); return $clone; } function moveDragEnd(end, e) { $obj.tbody.find(".dragline").remove(); if(dragIndex != null) { $("#TABLE_LAYER_" + self.timestamp).remove(); if (dragIndex != end) { if (self.emit("move", [ self.get(dragIndex), e ]) !== false) { self.move(dragIndex, end); var row = self.get((dragIndex < end) ? end - 1 : end); $(row.element).addClass("dragtarget"); self.hideExpand(e); self.emit("moveend", [ row, e ]); } } dragIndex = null; } } } } function setEventEditCell(self, elem, row, colIndex, event, callback) { var column = self.getColumn(colIndex), colkeys = self.options.editRow, $input = null; if(!column.name || (colkeys !== true && $.inArray(colIndex, getColumnIndexes(self, colkeys)) == -1)) { $input = $("<div class='edit'></div>").html($(elem).html() || " "); $input.attr("disabled", true); } else { $input = $("<input type='text' class='edit' />").val((column.name) ? column.data[row.index] : ""); } $(elem).html($input.css("width", "100%")); // 클릭 엘리먼트에 포커스 맞추기 if(event && event.target == elem) $input.focus(); // 엔터 키 이벤트 발생시 업데이트 self.addEvent($input, "keypress", function(e) { if(e.which == 13) { update(); } }); // 포커스가 바뀌었을 경우 업데이트 self.addEvent($obj.tbody, "mousedown", function(e) { if(e.target.tagName == "TD" || e.target.tagName == "TR") { update(); } }); function update() { if(editableIndex != null) { callback(); } } } function setEventColumn(self) { var opts = self.options, len = self.uit.getColumnCount(); // 컬럼 컨텍스트 이벤트 for(var i = 0; i < len; i++) { var col = self.getColumn(i); (function(index, column) { if(!opts.fields || !opts.sort || opts.sortEvent !== true) { self.addEvent(column.element, "click", function (e) { self.emit("colclick", [ column, e ]); }); } self.addEvent(column.element, "dblclick", function(e) { self.emit("coldblclick", [ column, e ]); }); self.addEvent(column.element, "contextmenu", function(e) { self.emit("colmenu", [ column, e ]); return false; }); })(i, col); } } function setEventSort(self) { var sortIndexes = self.options.sort, len = (sortIndexes === true) ? self.uit.getColumnCount() : sortIndexes.length; for(var i = 0; i < len; i++) { var colKey = (sortIndexes === true) ? i : sortIndexes[i], col = self.getColumn(colKey); if(col.element != null) { (function(index, column) { self.addEvent(column.element, "click", function(e) { if($(e.target).hasClass("resize")) return; self.sort(index, undefined, e); self.emit("colclick", [ column, e ]); }); })(colKey, col); $(col.element).css("cursor", "pointer"); } } } function setColumnResize(self) { var resizeX = 0, tablePos = $obj.table.offset(); var col = null, colNext = null, colWidth = 0, colNextWidth = 0, colResize = null; // 리사이즈 엘리먼트 삭제 $obj.thead.find(".resize").remove(); for(var i = 0; i < self.uit.getColumnCount() - 1; i++) { var $colElem = $(self.getColumn(i).element), $resizeBar = $("<div class='resize'></div>"); var pos = $colElem.offset(); // ie8 버그로 인해 position에서 offset으로 변경함 $resizeBar.css({ position: "absolute", width: "8px", height: $colElem.outerHeight(), left: ($colElem.outerWidth() + (pos.left - tablePos.left) - 1) + "px", top: (pos.top - tablePos.top) + "px", cursor: "w-resize", "z-index": "1" }); $colElem.append($resizeBar); // Event Start (function(index) { self.addEvent($resizeBar, "mousedown", function(e) { if(resizeX == 0) resizeX = e.pageX; // 컬럼 객체 가져오기 col = self.getColumn(index); colNext = getNextColumn(index); colWidth = $(col.element).outerWidth(); colNextWidth = $(colNext.element).outerWidth(); colResize = this; is_resize = true; return false; }); })(i); } self.addEvent(document, "mousemove", function(e) { if(resizeX > 0) { colResizeWidth(self, e.pageX - resizeX); } }); self.addEvent(document, "mouseup", function(e) { if(resizeX > 0) { resizeX = 0; // 리사이징 바, 위치 이동 var left = $(col.element).offset().left - tablePos.left; $(colResize).css("left", $(col.element).outerWidth() + left - 1); self.emit("colresize", [ col, e ]); // 리사이징 상태 변경 (delay) setTimeout(function() { is_resize = false; }, 100); return false; } }); function getNextColumn(index) { for(var i = index + 1; i < self.uit.getColumnCount(); i++) { var elem = self.getColumn(i).element; if(!$(elem).is(':hidden')) { return self.getColumn(i); } } } function colResizeWidth(self, disWidth) { var colMinWidth = 30; // 최소 크기 체크 if(colWidth + disWidth < colMinWidth || colNextWidth - disWidth < colMinWidth) return; $(col.element).outerWidth(colWidth + disWidth); $(colNext.element).outerWidth(colNextWidth - disWidth); // 스크롤 옵션일 경우, 별도 처리 if(self.options.scroll) { var colLastWidth = $(colNext.element).outerWidth() - ((col.index == self.uit.getColumnCount() - 2) ? getScrollWidth() : 0); $(col.list[0]).outerWidth($(col.element).outerWidth()); $(colNext.list[0]).outerWidth(colLastWidth); } } } function resetRowStatus(self, isChecked) { self.hideExpand(); self.hideEditRow(); self.unselect(); if(!isChecked) { self.uncheckAll(); } } function getScrollWidth() { if($(".jui").size() > 0 && _.browser.webkit) { return 10; } return _.scrollWidth(); } this.init = function() { var opts = this.options; // @Deprecated, 'rows'는 의미상 맞지 않아 차후 삭제 opts.data = (opts.rows != null) ? opts.rows : opts.data; // UIHandler, 추후 코어에서 처리 $obj = { table: $(this.root).css({ "position": "relative" }), thead: $(this.root).find("thead"), tbody: $(this.root).find("tbody") }; // UITable 객체 생성 this.uit = new Base({ $obj: $obj, $tpl: this.tpl }, opts.fields); // 신규 테이블 클래스 사용 if(opts.moveRow) { $obj.tbody.css({ "-webkit-user-select": "none", "-moz-user-select": "none", "-ms-user-select": "none", "-o-user-select": "none", "user-select": "none" }); } if(opts.fields && opts.colshow) { // 컬럼 보이기 초기값 설정 setColumnStatus(this); } if(opts.fields && this.tpl.menu && dropdown != null) { // 컬럼 보이기/숨기기 메뉴 설정 setColumnMenu(this); } if(opts.resize) { setColumnResize(this); } if(opts.fields && opts.sort && opts.sortEvent === true) { setEventSort(this); } if(opts.data.length > 0) { this.update(opts.data); } else { this.setVo(); // 데이터가 있을 경우에는 VO 세팅을 별도로 함 } if(opts.width > 0) { $obj.table.outerWidth(opts.width); } if(!opts.fields) { if(opts.sort || opts.colshow || opts.editRow) { throw new Error("JUI_CRITICAL_ERR: 'fields' option is required"); } } setEventColumn(this); } /** * @method update * Updates the list of rows or modifies the row at a specified index. * * @param {Array} rows */ this.update = function() { var dataList = (arguments.length == 1) ? arguments[0] : arguments[1], index = (arguments.length == 2) ? arguments[0] : null; if(index != null) { // 1. 단일 로우 업데이트 var tmpRow = this.uit.updateRow(index, dataList); setEventRow(this, tmpRow); // 첫번째 로우일 경우, 스크롤 다시 처리 if(parseInt(index) == 0) { this.scroll(); } } else { // 2. 로우 목록 업데이트 this.uit.removeRows(); this.scroll(); this.append(dataList); // 정렬 인덱스가 옵션에 있을 경우, 해당 인덱스의 컬럼 정렬 if(this.options.sortIndex) { this.sort(this.options.sortIndex, this.options.sortOrder, null); } } } /** * @method updateTree * It is possible to configure a tree table using an object array with the index and data properties. * * @param {Array} rows */ this.updateTree = function(rows) { // index & data 조합의 객체 배열 var iParser = _.index(); // 전체 로우 제거 this.uit.removeRows(); // 트리 로우 추가 for(var i = 0; i < rows.length; i++) { var pIndex = iParser.getParentIndex(rows[i].index); if(pIndex == null) { this.uit.appendRow(rows[i].data); } else { this.uit.appendRow(pIndex, rows[i].data); } } setUpdateInit(this, true); setEventRows(this); } /** * @method append * Add a row or a child row to at a specified index. * * @param {RowObject} row */ this.append = function() { var isInit = (this.count() > 0) ? false : true; var dataList = (arguments.length == 1) ? arguments[0] : arguments[1], index = (arguments.length == 2) ? arguments[0] : null; dataList = (dataList.length == undefined) ? [ dataList ] : dataList; for(var i = 0; i < dataList.length; i++) { var tmpRow = null; if(index != null) tmpRow = this.uit.appendRow(index, dataList[i]); else tmpRow = this.uit.appendRow(dataList[i]); // 추가 로우 추가시 이벤트 걸기 if(!isInit) { setEventRow(this, tmpRow); } } setUpdateInit(this, isInit); if(isInit) setEventRows(this); // 최초에 데이터가 없을 경우에만 전체 이벤트 걸기 } /** * @method insert * Adds a row at a specified index. * * @param {Integer} index * @param {RowObject} row */ this.insert = function(index, dataList) { var isInit = (this.count() > 0) ? false : true; var dataList = (dataList.length == undefined) ? [ dataList ] : dataList; for(var i = 0; i < dataList.length; i++) { this.uit.insertRow(index, dataList[i]); } setUpdateInit(this, isInit); setEventRows(this); } /** * @method select * Adds a selected class to a row at a specified index and gets an instance of the applicable row. * * @param {Integer} index * @return {RowObject} row */ this.select = function(index) { // 모든 로우 상태 초기화 resetRowStatus(this); var row = this.get(index); $(row.element).parent().find(".selected").removeClass("selected"); $(row.element).addClass("selected"); selectedIndex = index; return row; } /** * @method unselect * Removes a selected class from a selected row and gets an instance of the row in question. * * @return {RowObject} row */ this.unselect = function() { if(selectedIndex == null) return; var row = this.get(selectedIndex); $(row.element).removeClass("selected"); selectedIndex = null; return row; } /** * @method check * Add a checked class to a row at a specified index. * * @param {Integer} index */ this.check = function(index) { // 모든 로우 상태 초기화 (체크만 제외 ) resetRowStatus(this, true); var row = this.get(index); // 초기화 this.hideExpand(); this.hideEditRow(); this.unselect(); checkedIndexes[index] = row; $(row.element).addClass("checked"); } /** * @method uncheck * Removes a checked class from a row at a specified index. * * @param {Integer} index */ this.uncheck = function(index) { if(checkedIndexes[index] == null) return; var row = this.get(index); checkedIndexes[index] = null; $(row.element).removeClass("checked"); } /** * @method uncheckAll * Removes checked classes from all rows. */ this.uncheckAll = function() { checkedIndexes = {}; $obj.tbody.find(".checked").removeClass("checked"); } /** * @method remove * Remove a row at a specified index. * * @param {Integer} index */ this.remove = function(index) { if(index == null) return null; this.uit.removeRow(index); setEventRows(this); this.scroll(); } /** * @method reset * Removes all rows. */ this.reset = function() { selectedIndex = null; expandedIndex = null; editableIndex = null; dragIndex = null; checkedIndexes = {}; this.uit.removeRows(); this.scroll(); } /** * @method move * Moves a row iat a specified index to the target index. * * @param {Integer} index * @param {Integer} targetIndex */ this.move = function(index, targetIndex) { this.uit.moveRow(index, targetIndex); setEventRows(this); // 첫번째 로우일 경우, 스크롤 다시 처리 if(parseInt(index) == 0 || parseInt(targetIndex) == 0) { this.scroll(); } } /** * @method sort * Moves a row iat a specified index to the target index. * * @param {Integer} index * @param {String} order "asc" or "desc" */ this.sort = function(index, order, e) { // index는 컬럼 key 또는 컬럼 name if(!this.options.fields || !this.options.sort || is_resize) return; var column = this.getColumn(index); if(typeof(column.name) == "string") { column.order = (order) ? order : (column.order == "asc" || column.order == null) ? "desc" : "asc"; this.uit.setColumn(index, column); this.uit.sortRows(column.name, (column.order == "desc") ? true : false); this.emit("sort", [ column, e ]); setUpdateInit(this, true); setEventRows(this); } } /** * @method scroll * Sets the scroll based on the height of a table. * * @param {Integer} height */ this.scroll = function(height) { if(!this.options.scroll) return; var self = this, h = (height && height > 0) ? height : this.options.scrollHeight, h = (h > 0) ? h : 200; this.options.scrollHeight = h; $obj.tbody.css("maxHeight", h + "px"); setTimeout(function() { if($obj.tbody.outerHeight() < h) { $obj.table.css({ "table-layout": "" }); $obj.tbody.css({ "display": "", "overflow": "" }); } else { $obj.table.css({ "table-layout": "fixed" }); $obj.tbody.css({ "display": "block", "overflow": "auto" }); } setScrollResize(self); }, 10); } /** * @method open * Shows a child row of a specified index. * * @param {Integer} index */ this.open = function(index) { // 로트 제외, 하위 모든 노드 대상 if(index == null) return; this.uit.openRow(index); this.emit("open", [ this.get(index) ]); } /** * @method fold * Hides a child row of a specified index. * * @param {Integer} index */ this.fold = function(index) { if(index == null) return; this.uit.foldRow(index); this.emit("fold", [ this.get(index) ]); } /** * @method openAll * Shows all child rows of a specified index. */ this.openAll = function() { // 로트 포함, 하위 모든 노드 대상 this.uit.openRowAll(); this.emit("openall"); } /** * @method foldAll * Hides all child rows of a specified index. */ this.foldAll = function() { this.uit.foldRowAll(); this.emit("foldall"); } /** * @method resize * Resets the inner scroll and columns of a table. */ this.resize = function() { this.scroll(); if(this.options.resize) { setColumnResize(this); } } /** * @method resizeColumns * Resets the sizes of all columns of a table. */ this.resizeColumns = function() { var columns = this.listColumn(); for(var i = 0; i < columns.length; i++) { if(columns[i].width == null) { $(columns[i].element).outerWidth("auto"); } } } /** * @method size * Gets the size of all the rows of a table. * * @return {Integer} size */ this.size = function() { // 차후 수정 (컬럼 * 로우 개수 * 바이트) return this.uit.getRowCount(); } /** * @method count * Gets the number of trows of a table. * * @return {Integer} count */ this.count = function() { return this.uit.getRowCount(); } /** * @method list * Gets all the rows of a table. * * @return {Array} rows */ this.list = function() { return this.uit.getRow(); } /** * @method listData * Gets the data of all the rows of a table. * * @return {Array} datas */ this.listData = function() { var rows = this.list(), data = []; for(var i = 0; i < rows.length; i++) { data.push(rows[i].data); } return data; } /** * @method listAll * Gets all the rows of a table including child rows. * * @return {Array} rows */ this.listAll = function() { return this.uit.getRowAll(); } /** * @method listChecked * Gets all rows in a check state. * * @return {Array} rows */ this.listChecked = function() { var list = []; for(var row in checkedIndexes) { if(checkedIndexes[row] != null) { list.push(checkedIndexes[row]); } } return list; } /** * @method listColumn * Gets all columns. * * @return {Array} columns */ this.listColumn = function() { return this.uit.getColumn(); } /** * @method get * Gets the row at the specified index. * * @param {Integer} index * @return {RowObject} row */ this.get = function(index) { if(index == null) return null; return this.uit.getRow(index); } /** * @method getAll * Gets all rows of at the specified index including child rows. * * @param {Integer} index * @return {Array} rows */ this.getAll = function(index) { if(index == null) return null; return this.uit.getRowAll(index); } /** * @method getColumn * Gets the column at the specified index. * * @param {"Integer"/"String"} key index or column key * @return {ColumnObject} column */ this.getColumn = function(index) { // index or columnName if(index == null) return null; else { if(typeof(index) == "string") return this.uit.getColumn($.inArray(index, this.options.fields)); else return this.uit.getColumn(index); } } /** * @method showColumn * Shows the column index (or column name). * * @param {"Integer"/"String"} key index or column name */ this.showColumn = function(index, e) { // index or columnName if(!this.options.fields) return; var column = this.getColumn(index); this.uit.showColumn(column.index); this.scroll(); this.resizeColumns(); if(this.options.resize) { setColumnResize(this); } // 커스텀 이벤트 발생 this.emit("colshow", [ column, e ]); } /** * @method hideColumn * Hides the column index (or column name). * * @param {"Integer"/"String"} key index or column name */ this.hideColumn = function(index, e) { // index or columnName if(!this.options.fields) return; var column = this.getColumn(index); this.uit.hideColumn(column.index); this.scroll(); this.resizeColumns(); if(this.options.resize) { setColumnResize(this); } // 커스텀 이벤트 발생 this.emit("colhide", [ column, e ]); } /** * @method initColumns * It is possible to determine the index or name of the column to be shown in an array. * * @param {"Integer"/"String"} key index or column name */ this.initColumns = function(keys) { if(typeof(keys) != "object") return; this.options.colshow = keys; setColumnStatus(this); this.scroll(); this.resizeColumns(); if(this.options.resize) { setColumnResize(this); } } /** * @method showColumnMenu * Shows the Show/Hide Column menu at specified coordinates. * * @param {Integer} x * @param {Integer} y */ this.showColumnMenu = function(x, y) { if(!this.options.fields || !ddUi) return; var columns = this.listColumn(), offset = $obj.thead.offset(), cx = _.typeCheck("integer", x) ? x : 0, cy = _.typeCheck("integer", y) ? y : offset.top + $obj.thead.outerHeight(); // 현재 체크박스 상태 설정 $(ddUi.root).find("input[type=checkbox]").each(function(i) { if(columns[i].type == "show") this.checked = true; else this.checked = false; }); ddUi.move(cx, cy); ddUi.show(); } /** * @method hideColumnMenu * Hides the Show/Hide Column menu. */ this.hideColumnMenu = function() { if(!this.options.fields || !ddUi) return; ddUi.hide(); } /** * @method toggleColumnMenu * Shows or hides the Show/Hide Column menu. * * @param {Integer} x * @param {Integer} y */ this.toggleColumnMenu = function(x, y) { if(!this.options.fields || !ddUi) return; if(ddUi.type == "show") this.hideColumnMenu(); else this.showColumnMenu(x, y); } /** * @method showExpand * Shows the extended row area of a specified index. * * @param {Integer} index */ this.showExpand = function(index, obj, e) { if(!this.options.expand) return; // 모든 로우 상태 초기화 resetRowStatus(this); var expandSel = "#EXPAND_" + this.timestamp, row = (index instanceof Row) ? index : this.get(index), obj = (typeof(obj) != "object") ? $.extend({ row: row }, row.data) : obj, $expand = $(expandSel).parent().show(); $obj.tbody.find("tr").removeClass("open"); $expand.insertAfter($(row.element).addClass("open")); $(expandSel) .attr("colspan", $obj.thead.find("tr:last > th:visible").size()) .html(this.tpl["expand"](obj)); // 스크롤 및 VO 적용 this.scroll(); this.setVo(); // 커스텀 이벤트 호출 expandedIndex = index; this.emit("expand", [ row, e ]); } /** * @method hideExpand * Hides the extended row area of a specified index. */ this.hideExpand = function(e) { if(expandedIndex == null) return; var row = (expandedIndex instanceof Row) ? expandedIndex : this.get(expandedIndex); $('#EXPAND_' + this.timestamp).parent().hide(); $obj.tbody.find("tr").removeClass("open"); // 스크롤 적용 this.scroll(); expandedIndex = null; this.emit("expandend", [ row, e ]); } /** * @method getExpand * Get a row in which the extended area is currently activated. * * @return {RowObject} row */ this.getExpand = function() { if(expandedIndex == null) return null; return (expandedIndex instanceof Row) ? expandedIndex : this.get(expandedIndex); } /** * @method showEditRow * Shows the modified row area of a specified index. * * @param {Integer} index */ this.showEditRow = function(index, e) { if(!this.options.editRow) return; // 모든 로우 상태 초기화 resetRowStatus(this); var self = this, row = this.get(index); var $cells = $(row.element).find("td"); $cells.each(function(i) { setEventEditCell(self, this, row, i, e, function() { var data = {}, originData = row.data; $cells.each(function(colIndex) { var column = self.getColumn(colIndex); if(column.name != null) { var $edit = $(this).find("input.edit"); if($edit.size() == 1) { var value = $edit.val(); data[column.name] = (!isNaN(value) && value != null && value != "") ? parseFloat(value) : value; } } }); // 변경된 값으로 데이터 갱신하기 row.data = $.extend(row.data, data); // 콜백 결과 가져오기 var res = self.emit("editend", [ row, e ]); // 이벤트 리턴 값이 false가 아닐 경우에만 업데이트 if(res !== false) { self.hideEditRow(data); } else { row.data = originData; } }); }); editableIndex = index; self.emit("editstart", [ row, e ]); } /** * @method hideEditRow * Hides the modified row area of a specified index. */ this.hideEditRow = function(data) { if(editableIndex == null) return; var row = this.get(editableIndex); editableIndex = null; this.update(row.index, !data ? row.data : data); } /** * @method getEditRow * Get a row in which the modified area is currently activated. * * @return {RowObject} row */ this.getEditRow = function() { if(editableIndex == null) return null; return this.get(editableIndex); } /** * @method setCsv * Updates a table using a CVS string. */ this.setCsv = function() { var opts = this.options; if(!opts.fields && !opts.csv) return; var csv = (arguments.length == 1) ? arguments[0] : arguments[1], key = (arguments.length == 2) ? arguments[0] : null; var fields = _.getCsvFields(opts.fields, opts.csv), csvNumber = (opts.csvNumber) ? _.getCsvFields(opts.fields, opts.csvNumber) : null, dataList = _.csvToData(fields, csv, csvNumber); if(key == null) { this.update(dataList); } else { this.reset(); for(var i = 0; i < dataList.length; i++) { var index = dataList[i][key]; if(index) { this.insert(index, dataList[i]); } } } } /** * @method setCsvFile * Updates a table using a CVS file. */ this.setCsvFile = function() { if(!this.options.fields && !this.options.csv) return; var self = this, file = (arguments.length == 1) ? arguments[0] : arguments[1], key = (arguments.length == 2) ? arguments[0] : null; _.fileToCsv(file, function(csv) { if(key == null) self.setCsv(csv); else self.setCsv(key, csv); }); } /** * @method getCsv * Gets the data of a table as a CSV string. * * @param {Boolean} isTree * @return {String} csv */ this.getCsv = function(isTree) { if(!this.options.fields && !this.options.csv) return; var fields = _.getCsvFields(this.options.fields, this.options.csv); var dataList = [], rows = (isTree) ? this.listAll() : this.list(); for(var i = 0; i < rows.length; i++) { dataList.push(rows[i].data); } return _.dataToCsv2({ fields: fields, rows: dataList, names: this.options.csvNames }); } /** * @method getCsvBase64 * Gets the data of a table as a CSV string encoded as base64. * * @param {Boolean} isTree * @return {String} base64 */ this.getCsvBase64 = function(isTree) { if(!this.options.fields && !this.options.csv) return; return _.csvToBase64(this.getCsv(isTree)); } /** * @method downloadCsv * Downloads the data of a table as a CSV file. * * @param {String} name * @param {Boolean} isTree */ this.downloadCsv = function(name, isTree) { if(_.typeCheck("string", name)) { name = name.split(".")[0]; } var a = document.createElement('a'); a.download = (name) ? name + ".csv" : "table.csv"; a.href = this.getCsvBase64(isTree); document.body.appendChild(a); a.click(); a.parentNode.removeChild(a); } /** * @method activeIndex * Gets the index of a row that is activated in an extended/modified/selected state. * * @return {Integer} index */ this.activeIndex = function() { // 활성화된 확장/수정/선택 상태의 로우 인덱스를 리턴 if(expandedIndex != null) { return (expandedIndex instanceof Row) ? expandedIndex.index : expandedIndex; } return selectedIndex || editableIndex; } } UI.setup = function() { return { /** * @cfg {Array} [fields=null] * Sets the name of columns in the order of being displayed on the table screen. */ fields: null, /** * @cfg {Array} [csv=null] * Sets the column key shown when converted to a CSV string. */ csv: null, /** * @cfg {Array} [csvNames=null] * Sets the name of a column shown when converting to a CSV string, which must be defined in the same order as the CSV option. */ csvNames: null, /** * @cfg {Array} [csvNumber=null] * Sets the column key to be changed to a number form when converted to a CSV string. */ csvNumber: null, /** * @cfg {Array} data * Sets the initial row list of a table. */ data: [], /** * @cfg {Array} rows * Sets the initial row list of a table (@Deprecated). */ rows: null, // @Deprecated /** * @cfg {Boolean/Array} [colshow=false] * Sets a column index shown when the Show/Hide Column menu is enabled. */ colshow: false, /** * @cfg {Boolean} [scroll=false] * Determines whether to use a table scroll. */ scroll: false, /** * @cfg {Integer} [scrollHeight=200] * Sets the reference height of a body area when using a table scroll. */ scrollHeight: 200, /** * @cfg {Integer} [width=0] * Sets the area of a table. */ width: 0, /** * @cfg {Boolean} [expand=false] * Determines whether to use an extended row area. */ expand: false, /** * @cfg {Boolean} [expandEvent=true] * Sets the Show/Hide state of an extended row area when clicking on a row. */ expandEvent: true, /** * @cfg {Boolean|Array} [editRow=false] * Determines whether to use a modified row area. */ editRow: false, /** * @cfg {Boolean} [editEvent=true] * Sets the Show/Hide state of an extended row area when doubleclicking on a row/cell. */ editEvent: true, /** * @cfg {Boolean} [resize=false] * Determines whether to use the column resizing function. */ resize: false, /** * @cfg {Boolean/Array} [sort=false] * Determines whether to use the table sort function. */ sort: false, /** * @cfg {Integer} [sortIndex=null] * Determines whether to use the table sort function. */ sortIndex: null, /** * @cfg {String} [sortOrder="asc"] * Determines whether to use the table sort function. */ sortOrder: "asc", /** * @cfg {Boolean} [sortEvent=true] * Determines whether to use the sort function when you click on a column. */ sortEvent: true, /** * @cfg {Boolean} [moveRow=false] * Determines whether to use the move function when you fire row draggable event. */ moveRow: false, /** * @cfg {Boolean} [animate=false] * @deprecated */ animate: false } } /** * @event select * Event that occurs when a row is selected (@Deprecated) * * @param {RowObject) row * @param {EventObject} e The event object */ /** * @event click * Event that occurs when a row is clicked * * @param {RowObject) row * @param {EventObject} e The event object */ /** * @event dblclick * Event that occurs when a row is double clicked * * @param {RowObject) row * @param {EventObject} e The event object */ /** * @event sort * Event that occurs when the table is sorted. * * @param {ColumnObject) column * @param {EventObject} e The event object */ /** * @event scroll * Event that occurs when the scroll of a table is located at the lowermost position. * * @param {EventObject} e The event object */ * @event rowmenu * Event that occurs when a row is right clicked. * * @param {RowObject) row * @param {EventObject} e The event object */ /** * @event colclick * Event that occurs when a column is clicked. * * @param {ColumnObject) column * @param {EventObject} e The event object */ /** * @event colshow * Event that occurs when shown column is selected. * * @param {ColumnObject) column * @param {EventObject} e The event object */ /** * @event colhide * Event that occurs when hidden column is selected. * * @param {ColumnObject) column * @param {EventObject} e The event object */ /** * @event colresize * Event that occurs when the column resizing is activated. * * @param {ColumnObject) column * @param {EventObject} e The event object */ /** * @event editstart * Event that occurs when a row is in a modification state. * * @param {RowObject) row * @param {EventObject} e The event object */ /** * @event editend * Event that occurs when the modification of a row is completed. * * @param {RowObject) row * @param {EventObject} e The event object */ /** * @event expand * Event that occurs when the extended row area is enabled. * * @param {RowObject) row * @param {EventObject} e The event object */ /** * @event expandend * Event that occurs when the extended row area is disabled. * * @param {RowObject) row * @param {EventObject} e The event object */ /** * @event open * Event that occurs when a child row is shown. * * @param {RowObject) row * @param {EventObject} e The event object */ /** * @event fold * Event that occurs when a child row is hidden. * * @param {RowObject) row * @param {EventObject} e The event object */ /** * @event openall * Event that occurs when all child rows are shown. */ /** * @event foldall * Event that occurs when all child rows are hidden. */ return UI; });