hedera-web/js/vn/model.js

351 lines
6.5 KiB
JavaScript

var VnObject = require('./object');
var simpleEquals = require('./value').simpleEquals;
var Klass = new Class();
module.exports = Klass;
var Status = {
CLEAN : 1
,LOADING : 2
,READY : 3
,ERROR : 4
};
var SortWay = {
ASC : 1
,DESC : 2
};
Klass.extend({
Status
,SortWay
});
Klass.implement({
Extends: VnObject
,Tag: 'vn-model'
,Properties: {
/**
* The model internal data.
*/
data: {
type: Array
,get() {
return this._data;
}
,set(x) {
this._setData(x);
}
},
/**
* The number of rows in the model.
*/
numRows: {
type: Number
,get() {
if (this._data)
return this._data.length;
return 0;
}
},
/**
* The current status of the model.
*/
status: {
type: Number
,get() {
return this._status;
}
},
/**
* Checks if the model data is ready.
*/
ready: {
type: Boolean
,get() {
return this._status === Status.READY;
}
}
}
,_data: null
,_status: Status.CLEAN
,_requestedSortName: null
,_sortColumn: null
,_sortWay: null
,_requestedIndexes: {}
,_indexes: []
//+++++++++++++++++++++++++++++ Data hadling
,_setData(data) {
this._data = data;
this._refreshRowIndexes(0);
if (this._requestedSortName != null)
this._realSort(this._requestedSortName, this._sortWay);
for (column in this._requestedIndexes)
this._buildIndex(column);
this._setStatus(Status.READY);
}
/**
* Checks if the row exists.
*
* @param {Integer} rowIndex The row index
* @return {Boolean} %true if row exists, %false otherwise
*/
,checkRowExists(rowIndex) {
return this._data != null
&& rowIndex >= 0
&& rowIndex < this._data.length;
}
/**
* Gets a value from the model.
*
* @param {Number} rowIndex The row index
* @param {Number} columnName The column name
* @return {*} The value
*/
,get(rowIndex, columnName) {
if (!this.checkRowExists(rowIndex))
return undefined;
return this._data[rowIndex][columnName];
}
/**
* Updates a value on the model.
*
* @param {Number} rowIndex The row index
* @param {Number} columnName The column name
* @param {*} value The new value
*/
,set(rowIndex, columnName, value) {
if (!this.checkRowExists(rowIndex))
return;
this.emit('row-updated-before', rowIndex);
this._data[rowIndex][columnName] = value;
this.emit('row-updated', rowIndex, [columnName]);
}
/**
* Gets a row as an object using the column index.
*
* @param {Number} rowIndex The row index
* @return {Object} The row as an object
*/
,getObject(rowIndex) {
return this.checkRowExists(rowIndex) ?
this._data[rowIndex] : null;
}
,_setStatus(status) {
this._status = status;
this.emit('status-changed', status);
this.emit('status-changed-after', status);
}
/**
* Inserts a new row on the model.
*
* @return The index of the inserted row
*/
,insertRow(newRow) {
var rowIndex = this._data.push(newRow) - 1;
newRow.index = rowIndex;
this.emit('row-inserted', rowIndex);
return rowIndex;
}
/**
* Deletes a row from the model.
*
* @param {Number} rowIndex The row index
*/
,deleteRow(rowIndex) {
if (!this.checkRowExists(rowIndex))
return;
this.emit('row-deleted-before', rowIndex);
this._data.splice(rowIndex, 1);
this.emit('row-deleted', rowIndex);
this._refreshRowIndexes(rowIndex);
}
//+++++++++++++++++++++++++++++ Sorting
/**
* Orders the model by the specified column name.
*
* @param {String} columnName The column name
* @param {SortWay} way The sort way
*/
,sort(columnName, way) {
this._requestedSortName = columnName;
this._sort(columnName, way);
}
,_sort(columnName, way) {
var status = this._status;
this._setStatus(Status.LOADING);
this._realSort(columnName, way);
this._setStatus(status);
}
,_realSort(columnName, way) {
if (!this._data)
return;
if (columnName !== this._sortColumn) {
if (way === SortWay.DESC)
var sortFunction = this.sortFunctionDesc;
else
var sortFunction = this.sortFunctionAsc;
this._data.sort(sortFunction.bind(this, columnName));
} else if (way !== this._sortWay)
this._data.reverse();
this._sortColumn = columnName;
this._sortWay = way;
this._refreshRowIndexes(0);
}
,_refreshRowIndexes(start) {
var data = this._data;
for (var i = start; i < data.length; i++)
data[i].index = i;
}
/*
* Function used to sort the model ascending.
*/
,sortFunctionAsc(column, a, b) {
if (a[column] < b[column])
return -1;
else if (a[column] > b[column])
return 1;
return 0;
}
/*
* Function used to sort the model descending.
*/
,sortFunctionDesc(column, a, b) {
if (a[column] > b[column])
return -1;
else if (a[column] < b[column])
return 1;
return 0;
}
//+++++++++++++++++++++++++++++ Searching
/**
* Builds an internal hash index for the specified column, this speeds
* significantly searches on that column, specially when model has a lot of
* rows.
*
* FIXME: Not fully implemented.
*
* @param {String} column The column name
*/
,indexColumn(column) {
this._requestedIndexes[column] = true;
if (this._status === Status.READY)
this._buildIndex(column);
}
,getHashValue(value) {
if (value instanceof Date)
return value.getTime();
else
return value;
}
,_buildIndex(columnName) {
if (this.columnMap[columnName] === undefined)
return;
var data = this._data;
var values = {};
var nulls = [];
for (var i = 0; i < data.length; i++) {
var value = data[i][columnName];
if (value == null) {
nulls.push(data[i]);
continue;
}
index[this.getHashValue(value)] = data[i];
}
this._indexes[columnName] = {
values: values,
index: index
};
}
/**
* Searchs a value on the model and returns the row index of the first
* ocurrence.
*
* @param {Number} columnName The column name
* @param {Object} value The value to search
* @return {Number} The column index
*/
,search(columnName, value) {
var data = this._data;
if (data == null)
return -1;
// Searchs the value using an internal index.
var index = this._indexes[columnName];
if (index) {
if (value == null) {
if (index.nulls[0] !== undefined)
return index.nulls[0].index;
} else {
var row = index.values[this.getHashValue(value)];
if (rowIndex !== undefined)
return row.index;
}
return -1;
}
// Searchs the value using a loop.
for (var i = 0; i < data.length; i++)
if (simpleEquals(data[i][columnName], value))
return i;
return -1;
}
//+++++++++++++++++++++++++++++ Virtual
,refresh() {
this._setStatus(Status.READY);
}
});