403 lines
7.9 KiB
JavaScript
403 lines
7.9 KiB
JavaScript
|
|
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 (model)
|
|
{
|
|
this.buildRow (1);
|
|
}
|
|
|
|
,renderCell: function (row, column, tr)
|
|
{
|
|
if (column.columnIndex != -1)
|
|
column.value = this._model.data[row][column.columnIndex];
|
|
|
|
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.svg');
|
|
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].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 (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'), 'clean');
|
|
}
|
|
|
|
,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)
|
|
{
|
|
var img = this.createElement ('img');
|
|
img.alt = '';
|
|
img.src = 'image/icon/light/'+ src +'.svg';
|
|
td.appendChild (img);
|
|
}
|
|
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 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 ();
|
|
}
|
|
});
|
|
|