/**
* Copyright (c) 2014, Oracle and/or its affiliates.
* All rights reserved.
*/
/**
* @preserve Copyright 2013 jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
/*jslint browser: true,devel:true*/
/**
* @export
* @class oj.CollectionTableDataSource
* @extends oj.TableDataSource
* @classdesc Object representing data used by table component
* @param {oj.Collection} data data supported by the components
* @param {Object|null} options Array of options for the TableDataSource
* @constructor
*/
oj.CollectionTableDataSource = function(data, options)
{
// Initialize
this.data = {}; // This was put in to keep closure happy...
if (!(data instanceof oj.Collection))
{
// we only support oj.Collection
var errSummary = oj.TableDataSource._LOGGER_MSG['_ERR_DATA_INVALID_TYPE_SUMMARY'];
var errDetail = oj.TableDataSource._LOGGER_MSG['_ERR_DATA_INVALID_TYPE_DETAIL'];
throw new Error(errSummary + '\n' + errDetail);
}
oj.CollectionTableDataSource.superclass.constructor.call(this, data, options);
this._collection = data;
this._addCollectionEventListeners();
this.Init();
if ((options != null && (options['startFetch'] == 'enabled' || options['startFetch'] == null))
|| options == null)
{
this._startFetchEnabled = true;
}
};
// Subclass from oj.DataSource
oj.Object.createSubclass(oj.CollectionTableDataSource, oj.TableDataSource, "oj.CollectionTableDataSource");
/**
* Initializes the instance.
* @export
* @expose
* @memberof! oj.CollectionTableDataSource
* @instance
*/
oj.CollectionTableDataSource.prototype.Init = function()
{
oj.CollectionTableDataSource.superclass.Init.call(this);
};
/**
* Return the row data found at the given index.
*
* @param {number} index Index for which to return the row data.
* @param {Object=} options Options to control the at.
* @param {number} options.fetchSize fetch size to use if the call needs to fetch more records from the server, if virtualized. Overrides the overall fetchSize setting <p>
* @return {Promise} Promise resolves to a compound object which has the structure below. If the index is out of range, Promise resolves to null.<p>
* <table>
* <tbody>
* <tr><td><b>data</b></td><td>The raw row data</td></tr>
* <tr><td><b>index</b></td><td>The index for the row</td></tr>
* <tr><td><b>key</b></td><td>The key value for the row</td></tr>
* </tbody>
* </table>
* @export
* @expose
* @memberof! oj.CollectionTableDataSource
* @instance
*/
oj.CollectionTableDataSource.prototype.at = function(index, options)
{
options = options || {};
options['deferred'] = true;
var model = this._collection.at(index, options);
var self = this;
// the at() call can result in fetches
self._isFetchingForAt = true;
var row;
return new Promise(function(resolve, reject)
{
if (model != null)
{
model.then(function(resolvedModel)
{
self._isFetchingForAt = false;
row = {'data': resolvedModel['attributes'], 'index': index, 'key': resolvedModel['id']};
resolve(row);
},
function(e)
{
self._isFetchingForAt = false;
oj.TableDataSource.superclass.handleEvent.call(self, oj.TableDataSource.EventType['ERROR'], e);
reject(e);
});
}
else
{
resolve(null);
}
});
};
/**
* Fetch the row data.
* @param {Object=} options Options to control fetch
* @param {number} options.startIndex The index at which to start fetching records.
* @param {boolean} options.silent If set, do not fire a sync event.
* @return {Promise} Promise object resolves to a compound object which contains an array of row data objects, an array of ids, and the startIndex triggering done when complete.<p>
* The structure of the resolved compound object is:<p>
* <table>
* <tbody>
* <tr><td><b>data</b></td><td>An array of raw row data</td></tr>
* <tr><td><b>keys</b></td><td>An array of key values for the rows</td></tr>
* <tr><td><b>startIndex</b></td><td>The startIndex for the returned set of rows</td></tr>
* </tbody>
* </table>
* @export
* @expose
* @memberof! oj.CollectionTableDataSource
* @instance
*/
oj.CollectionTableDataSource.prototype.fetch = function(options)
{
options = options || {};
var fetchType = options['fetchType'];
if (fetchType == 'init' && !this._startFetchEnabled)
{
return Promise.resolve();
}
return this._fetchInternal(options);
};
/**
* Return the first row data whose id value is the given id
* @param {string} id ID for which to return the row data, if found.
* @param {Object=} options Options to control the get.
* @return {Promise} Promise which resolves to a compound object which has the structure below where the id matches the given id. If none are found, resolves to null.<p>
* <table>
* <tbody>
* <tr><td><b>data</b></td><td>The raw row data</td></tr>
* <tr><td><b>index</b></td><td>The index for the row</td></tr>
* <tr><td><b>key</b></td><td>The key value for the row</td></tr>
* </tbody>
* </table>
* @export
* @expose
* @memberof! oj.CollectionTableDataSource
* @instance
*/
oj.CollectionTableDataSource.prototype.get = function(id, options)
{
options = options || {};
options['deferred'] = true;
var model = this._collection.get(id, options);
var self = this;
var row;
return new Promise(function(resolve, reject)
{
if (model != null)
{
model.then(function(resolvedModel)
{
row = {'data': resolvedModel['attributes'], 'index': resolvedModel['index'], 'key': resolvedModel['id']};
resolve(row);
},
function(e)
{
oj.TableDataSource.superclass.handleEvent.call(self, oj.TableDataSource.EventType['ERROR'], e);
reject(e);
});
}
else
{
resolve(null);
}
});
};
/**
* Performs a sort on the data source.
* @override
* @param {Object} criteria the sort criteria.
* @param {Object} criteria.key The key that identifies which field to sort
* @param {string} criteria.direction the sort direction, valid values are "ascending", "descending", "none" (default)
* @return {Promise} promise object triggering done when complete.
* @export
* @expose
* @memberof! oj.CollectionTableDataSource
* @instance
*/
oj.CollectionTableDataSource.prototype.sort = function(criteria)
{
if (criteria == null)
{
return Promise.resolve();
}
var self = this;
return new Promise(function(resolve, reject) {
self._setComparator(criteria);
self._collection.sort(null);
var result = {'header': criteria['key'], 'direction': criteria['direction']};
resolve(result);
});
};
/**
* Return the total size of data available, including server side if not local.
* @override
* @returns {number} total size of data
* @export
* @expose
* @memberof! oj.CollectionTableDataSource
* @instance
*/
oj.CollectionTableDataSource.prototype.totalSize = function()
{
var totalSize = this._collection['totalResults'] >= 0 ? this._collection['totalResults'] : -1;
if (totalSize > -1)
{
var size = this._collection.size();
return size > totalSize ? size : totalSize;
}
else
{
if (this._fetchResultSize > 0)
{
totalSize = this._fetchResultSize;
}
}
return totalSize;
};
/**
* Add event listeners to the collection
* @private
*/
oj.CollectionTableDataSource.prototype._addCollectionEventListeners = function()
{
var self = this;
this._collection.on(oj.Events.EventType['SYNC'], function(event)
{
if (!(event instanceof oj.Collection))
{
return;
}
if (!self._isFetchingForAt && !self._isFetching)
{
var startIndex = event['offset'];
var pageSize = event['lastFetchCount'];
if (pageSize > 0)
{
self._startIndex = startIndex;
self._pageSize = pageSize;
// paged fetch
self._isFetchingForAt = true;
event.IterativeAt(startIndex, startIndex + pageSize).then(function(modelArray)
{
self._isFetchingForAt = false;
var rowArray = [];
var keyArray = [];
var i;
for (i = 0; i < modelArray.length; i++)
{
if (modelArray[i] != null)
{
rowArray.push(modelArray[i]['attributes']);
keyArray.push(modelArray[i]['id']);
}
}
var result = {'data': rowArray, 'keys': keyArray, 'startIndex': startIndex};
self._endFetch.call(self, {'silent': false}, result, null);
});
}
else
{
var result = self._getRowArray();
self._endFetch.call(self, {'silent': false}, result, null);
}
}
});
this._collection.on(oj.Events.EventType['ALLADDED'], function(event, modelArray) {
var rowArray = [];
var keyArray = [];
var indexArray = [];
var i;
for (i = 0; i < modelArray.length; i++)
{
rowArray.push(modelArray[i]['attributes']);
keyArray.push(modelArray[i]['id']);
indexArray.push(modelArray[i]['index']);
}
oj.TableDataSource.superclass.handleEvent.call(self, oj.TableDataSource.EventType['ADD'], {'data': rowArray, 'keys': keyArray, 'indexes': indexArray});
});
this._collection.on(oj.Events.EventType['ALLREMOVED'], function(event, modelArray) {
var rowArray = [];
var keyArray = [];
var indexArray = [];
var i;
for (i = 0; i < modelArray.length; i++)
{
rowArray.push(modelArray[i]['attributes']);
keyArray.push(modelArray[i]['id']);
indexArray.push(modelArray[i]['index']);
}
oj.TableDataSource.superclass.handleEvent.call(self, oj.TableDataSource.EventType['REMOVE'], {'data': rowArray, 'keys': keyArray, 'indexes': indexArray});
});
this._collection.on(oj.Events.EventType['RESET'], function(event) {
oj.TableDataSource.superclass.handleEvent.call(self, oj.TableDataSource.EventType['RESET'], event);
});
this._collection.on(oj.Events.EventType['SORT'], function(event, eventOpts) {
if (eventOpts == null || !eventOpts['add'])
{
oj.TableDataSource.superclass.handleEvent.call(self, oj.TableDataSource.EventType['SORT'], event);
}
});
this._collection.on(oj.Events.EventType['CHANGE'], function(event) {
oj.TableDataSource.superclass.handleEvent.call(self, oj.TableDataSource.EventType['CHANGE'], {'data': [event['attributes']], 'keys': [event['id']], 'indexes': [event['index']]});
});
this._collection.on(oj.Events.EventType['DESTROY'], function(event) {
oj.TableDataSource.superclass.handleEvent.call(self, oj.TableDataSource.EventType['DESTROY'], event);
});
this._collection.on(oj.Events.EventType['REFRESH'], function(event) {
oj.TableDataSource.superclass.handleEvent.call(self, oj.TableDataSource.EventType['REFRESH'], event);
});
this._collection.on(oj.Events.EventType['ERROR'], function(collection, xhr, options) {
oj.TableDataSource.superclass.handleEvent.call(self, oj.TableDataSource.EventType['ERROR'], collection, xhr, options);
});
};
oj.CollectionTableDataSource.prototype._fetchInternal = function(options)
{
this._startFetch(options);
options = options || {};
var self = this;
this._isPaged = options['pageSize'] > 0 ? true : false;
this._startIndex = options['startIndex'] == null ? this._startIndex : options['startIndex'];
this._initFetch = true;
this._pageSize = options['pageSize'] > 0 ? options['pageSize'] : -1;
options['pageSize'] = this._pageSize;
options['startIndex'] = this._startIndex;
options['refresh'] = true;
return new Promise(function (resolve, reject)
{
if (self._isPaged)
{
self._collection.setRangeLocal(self._startIndex, self._pageSize).then(function(actual)
{
var rowArray = [];
var keyArray = [];
var i;
for (i = 0; i < actual['models'].length; i++)
{
rowArray[i] = actual['models'][i]['attributes'];
keyArray[i] = actual['models'][i]['id'];
}
var result = {'data': rowArray, 'keys': keyArray, 'startIndex': self._startIndex};
if (actual['models'].length < self._pageSize)
{
// it returned less than a page of data so we're at the end
// set the totalSize if unknown
if (self.totalSize() < 0)
{
self._fetchResultSize = self._startIndex + actual['models'].length;
}
}
else
{
self._fetchResultSize = null;
}
self._endFetch.call(self, options, result, null);
resolve(result);
},
function(error)
{
self._endFetch.call(self, options, null, error);
reject(error);
});
}
else
{
if (self._collection.size() > 0)
{
var result = self._getRowArray();
self._endFetch.call(self, options, result, null);
resolve(result);
}
else
{
self._collection.fetch({
'success': function(collection, response, opts)
{
self._collection = collection;
var result = self._getRowArray();
self._endFetch.call(self, options, result, null);
resolve(result);
},
'error': function(collection, e, opts)
{
self._collection = collection;
self._endFetch.call(self, options, null, e);
reject(e);
}
});
}
}
});
};
/**
* Indicate starting fetch
* @param {Object} options
* @private
*/
oj.CollectionTableDataSource.prototype._startFetch = function(options)
{
this._isFetching = true;
if (!options['silent'])
{
oj.TableDataSource.superclass.handleEvent.call(this, oj.TableDataSource.EventType['REQUEST'], {'startIndex' : options['startIndex']});
}
};
/**
* Indicate ending fetch
* @param {Object} options
* @param {Object} result Result object
* @param {*} error Error
* @private
*/
oj.CollectionTableDataSource.prototype._endFetch = function(options, result, error)
{
this._isFetching = false;
if (error != null)
{
oj.TableDataSource.superclass.handleEvent.call(this, oj.TableDataSource.EventType['ERROR'], error);
}
else
{
if (!options['silent'])
{
oj.TableDataSource.superclass.handleEvent.call(this, oj.TableDataSource.EventType['SYNC'], result);
}
}
};
oj.CollectionTableDataSource.prototype._setComparator = function(criteria) {
if (criteria == null)
{
this._collection['comparator'] = null;
return;
}
var key = criteria['key'];
var direction = criteria['direction'];
var comparator = null;
if (this._collection.IsVirtual()) {
// Only strings are supported for virtual sorts
this._collection['comparator'] = key;
this._collection['sortDirection'] = direction === 'ascending' ? 1 : -1;
return;
}
if (direction == 'ascending')
{
comparator = function(row) {
if ($.isFunction(row.get))
{
return row.get(key);
}
else
{
return row[key]();
}
};
}
else if (direction == 'descending')
{
comparator = function(rowA, rowB) {
var a, b;
if ($.isFunction(rowA.get))
{
a = rowA.get(key);
b = rowB.get(key);
}
else
{
a = rowA[key]();
b = rowB[key]();
}
if (a === b)
{
return 0;
}
return a > b ? -1 : 1;
};
}
this._collection['comparator'] = comparator;
};
oj.CollectionTableDataSource.prototype._getRowArray = function()
{
var endIndex = this._collection.size() - 1;
var rowArray = [];
var keyArray = [];
var i;
for (i = 0; i <= endIndex; i++)
{
rowArray[i] = this._collection.at(i)['attributes'];
keyArray[i] = this._collection.at(i)['id'];
}
return {'data': rowArray, 'keys': keyArray, 'startIndex': this._startIndex};
};
Source: src/main/javascript/oracle/oj/ojdatacollection-common/CollectionTableDataSource.js
Oracle® JavaScript Extension Toolkit (JET)
1.1.2
E65298-01