var Widget = require('./widget'); module.exports = new Class({ Extends: Widget ,Tag: 'htk-grid' ,Child: 'model' ,Properties: { /** * The source data model. */ model: { type: Db.Model ,set: function(x) { this.link({_model: x}, { 'status-changed': this.onModelChange ,'row-deleted': this.onRowDelete ,'row-updated': this.onRowUpdate ,'row-inserted': this.onRowInsert ,'updatable-changed': this.onUpdatableChange }); var set = new Db.SimpleIterator({model: x}); this.link({_set: set}); this.onUpdatableChange(); this.onModelChange(); } ,get: function() { return this._model; } }, /** * Message that should be displayed when source model is not ready. */ emptyMessage: { type: String ,value: null }, /** * Wether to display the header with column titles. */ showHeader: { type: Boolean ,set: function(x) { this._showHeader = x; this.thead.style.display = x ? 'table-header-group' : 'none'; } ,get: function() { return this._showHeader; } } } ,_model: null ,_set: null ,columns: new Array() ,internalColumn: null ,internalColumns: 0 ,sortColumn: -1 ,sortWay: null ,_showHeader: true ,render: function() { var table = this.createRoot('table'); table.className = 'htk-grid'; var thead = this.createElement('thead'); table.appendChild(thead); this.thead = this.createElement('tr') thead.appendChild(this.thead); this.tbody = this.createElement('tbody'); table.appendChild(this.tbody); } ,appendChild: function(child) { this.appendColumn(child); } ,removeClicked: function(column, value, row) { if (confirm(_('ReallyDelete'))) this._model.deleteRow(row); } ,onRowDelete: function(model, row) { var tableRows = this.tbody.childNodes; this.tbody.removeChild(tableRows[row]); for (var i = row; i < tableRows.length; i++) tableRows[i].className = (i % 2) ? 'pair-row' : ''; this.showNoRecordsFound(); } ,onRowInsert: function() { this.buildRow(1); } ,renderCell: function(row, column, tr) { if (column.column) column.value = this._model.data[row][column.column]; if (column.renderer) { this._set.row = row; column.renderer(column, this._set); } return column.render(tr); } ,refreshRow: function(row, columns) { var x = this.columns; var tr = this.tbody.childNodes[row]; for (var i = 0; i < x.length; i++) if (x[i].renderer || columns.indexOf(x[i].columnIndex) != -1) { var cell = this.renderCell(row, x[i], tr); tr.replaceChild(cell, tr.childNodes[i]); } } ,onRowUpdate: function(model, row, columns) { this.refreshRow(row, columns); } ,buildRow: function(count) { for (var i = 0; i < count; i++) { var tr = this.createElement('tr'); if (i % 2) tr.className = 'pair-row'; for (var j = 0; j < this.columns.length; j++) { var cell = this.renderCell(i, this.columns[j], tr); tr.appendChild(cell); } this.tbody.appendChild(tr); } } ,onUpdatableChange: function() { if (this._model && this._model.updatable) { if (!this.internalColumn) { this.internalColumn = new Htk.ColumnButton({ icon: 'delete' ,tip: _('Remove') }); this.internalColumn.on('clicked', this.removeClicked, this); this.insertInternalColumn(0, this.internalColumn); } } else if (this.internalColumn) { this.internalColumn = null; this.removeInternalColumn(0); } } ,onModelChange: function() { var emptyMessage = this.emptyMessage ? this.emptyMessage : _('NoData'); if (!this._model) { this.showMessage(emptyMessage, 'refresh'); return; } this._node.removeChild(this.tbody); this.tbody = this.createElement('tbody'); switch (this._model.status) { case Db.Model.Status.READY: { for (var i = 0; i < this.columns.length; i++) this.columns[i].updateColumnName(this._model); this.buildRow(this._model.numRows); this.showNoRecordsFound(); break; } case Db.Model.Status.LOADING: this.showMessage(_('Loading'), null); break; case Db.Model.Status.CLEAN: this.showMessage(emptyMessage, 'refresh'); break; case Db.Model.Status.ERROR: this.showMessage(_('ErrorLoadingData'), 'error'); break; } this._node.appendChild(this.tbody); } ,showNoRecordsFound: function() { if (this._model.numRows == 0) this.showMessage(_('EmptyList'), 'block'); } ,showMessage: function(message, src) { if (this.columns.length == 0) return; var tr = this.createElement('tr'); this.tbody.appendChild(tr); var td = this.createElement('td'); td.className = 'message'; td.colSpan = this.columns.length; tr.appendChild(td); if (src) { const icon = new Htk.Icon({ name: src }); td.appendChild(icon.node); } else { var spinner = new Htk.Spinner(); spinner.start(); td.appendChild(spinner.node); } var message = this.createTextNode(message); td.appendChild(message); } ,scrollToRow: function(row) { if (row >= 0) { var height = parseInt(this.tr.style.height); this.node.scrollTop = (row - 2) * height; if (this.selectedRow) this.selectedRow.style.backgroundColor = null; this.selectedRow = this.tbody.childNodes[row] this.selectedRow.style.backgroundColor = '#AAD'; } } ,sortModel: function(column) { var columnName = column.column; if (this._model && columnName != -1) { if (this.sortColumn === columnName && this.sortWay === Db.Model.SortWay.ASC) this.sortWay = Db.Model.SortWay.DESC; else this.sortWay = Db.Model.SortWay.ASC; this.sortColumn = columnName; this._model.sortByName(columnName, this.sortWay); } } ,columnChanged: function(column, row, newValue) { var columnName = column.column; if (columnName != -1) this._model.set(row, columnName, newValue); } ,addColumn: function(pos, column) { var header = column.renderHeader(); if (pos == -1 || pos >= this.columns.length) { pos = this.columns.push(column) - 1; this.thead.appendChild(header); } else { this.columns.splice(pos, 0, column); this.thead.insertBefore(header, this.thead.childNodes[pos]); } header.addEventListener('click', this.sortModel.bind(this, column)); header.title = _('Sort'); column.on('changed', this.columnChanged, this); var rows = this.tbody.childNodes; if (this._model && this._model.numRows > 0) for (var i = 0; i < rows.length; i++) { var cell = this.renderCell(i, column, rows[i]); rows[i].insertBefore(cell, rows[i].childNodes[pos+1]); } return pos; } ,insertInternalColumn: function(pos, column) { if (pos < 0 || pos > this.internalColumns) pos = this.internalColumns; this.internalColumns++; return this.addColumn(pos, column); } ,insertColumn: function(pos, column) { if (pos > 0) pos += this.internalColumns; return this.addColumn(pos, column) - this.internalColumns; } ,appendColumn: function(column) { return this.insertColumn(-1, column); } ,deleteColumn: function(pos) { if (pos < 0 || pos >= this.columns.length) return; this.columns.splice(pos, 1); this.thead.removeChild(this.thead.childNodes[pos]); var rows = this.tbody.childNodes; if (this._model && this._model.numRows > 0) for (var i = 0; i < rows.length; i++) rows[i].removeChild(rows[i].childNodes[pos]); } ,removeInternalColumn: function(pos) { if (this.internalColumns == 0) return; if (pos < 0 || pos > this.internalColumns) pos = this.internalColumns; this.deleteColumn(pos); this.internalColumns--; } ,removeColumn: function(pos) { if (pos > 0) pos += this.internalColumns; this.deleteColumn(pos); } ,reloadModel: function() { if (this._model != null) this.onModelChange(); } });