Htk.Grid = new Class ({ Extends: Htk.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 form = new Db.Form ({model: x}); this.link ({form: form}); this.onUpdatableChange (); this.onModelChange (); } ,get: function () { return this._model; } }, /** * Message that should be displayed when source model is not ready. **/ emptyMessage: { type: String ,value: _('NoData') }, /** * 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 ,form: null ,columns: new Array () ,internalColumn: null ,internalColumns: 0 ,sortColumn: -1 ,sortWay: null ,_showHeader: true ,initialize: function (props) { this.table = this.createElement ('table'); this.table.className = 'htk-grid'; var thead = document.createElement ('thead'); this.table.appendChild (thead); this.thead = document.createElement ('tr') thead.appendChild (this.thead); this.tbody = document.createElement ('tbody'); this.table.appendChild (this.tbody); this.parent (props); } ,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 (model, row) { this.buildRow (1); } ,renderCell: function (row, column, tr) { if (column.columnIndex != -1) column.value = this._model.data[row][column.columnIndex]; if (column.renderer) { this.form.row = row; column.renderer (column, this.form); } 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 = document.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 ({ image: 'image/delete.svg' ,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 () { if (!this._model) { this.showMessage (this.emptyMessage, 'refresh.svg'); return; } this.table.removeChild (this.tbody); this.tbody = document.createElement ('tbody'); switch (this._model.status) { case Db.Model.Status.READY: { for (var i = 0; i < this.columns.length; i++) this.columns[i].updateColumnIndex (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 (this.emptyMessage, 'refresh.svg'); break; case Db.Model.Status.ERROR: this.showMessage (_('ErrorLoadingData'), 'error.svg'); break; } this.table.appendChild (this.tbody); } ,showNoRecordsFound: function (count) { if (this._model.numRows == 0) this.showMessage (_('EmptyList'), 'clean.svg'); } ,showMessage: function (message, src) { if (this.columns.length == 0) return; var tr = document.createElement ('tr'); this.tbody.appendChild (tr); var td = document.createElement ('td'); td.className = 'message'; td.colSpan = this.columns.length; tr.appendChild (td); if (src) { var img = document.createElement ('img'); img.alt = ''; img.src = 'image/'+ src; td.appendChild (img); } else { var spinner = new Htk.Spinner (); spinner.start (); td.appendChild (spinner.getNode ()); } var message = document.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 columnIndex = column.columnIndex; if (this._model && columnIndex != -1) { if (this.sortColumn === columnIndex && this.sortWay === Db.Model.SortWay.ASC) this.sortWay = Db.Model.SortWay.DESC; else this.sortWay = Db.Model.SortWay.ASC; this.sortColumn = columnIndex; this._model.sort (columnIndex, this.sortWay); } } ,columnChanged: function (column, row, newValue) { var columnIndex = column.columnIndex; if (columnIndex != -1) this._model.setByIndex (row, columnIndex, 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 (); } });