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;
});