|
|
@ -37,43 +37,39 @@ |
|
|
|
*/ |
|
|
|
// TODO: add a DataSet constructor DataSet(data, options)
|
|
|
|
function DataSet (data, options) { |
|
|
|
this.id = util.randomUUID(); |
|
|
|
|
|
|
|
// correctly read optional arguments
|
|
|
|
if (data && !Array.isArray(data) && !util.isDataTable(data)) { |
|
|
|
options = data; |
|
|
|
data = null; |
|
|
|
} |
|
|
|
|
|
|
|
this.options = options || {}; |
|
|
|
this.data = {}; // map with data indexed by id
|
|
|
|
this.fieldId = this.options.fieldId || 'id'; // name of the field containing id
|
|
|
|
this.type = {}; // internal field types (NOTE: this can differ from this.options.type)
|
|
|
|
this.showInternalIds = this.options.showInternalIds || false; // show internal ids with the get function
|
|
|
|
this._options = options || {}; |
|
|
|
this._data = {}; // map with data indexed by id
|
|
|
|
this._fieldId = this._options.fieldId || 'id'; // name of the field containing id
|
|
|
|
this._type = {}; // internal field types (NOTE: this can differ from this._options.type)
|
|
|
|
|
|
|
|
// all variants of a Date are internally stored as Date, so we can convert
|
|
|
|
// from everything to everything (also from ISODate to Number for example)
|
|
|
|
if (this.options.type) { |
|
|
|
for (var field in this.options.type) { |
|
|
|
if (this.options.type.hasOwnProperty(field)) { |
|
|
|
var value = this.options.type[field]; |
|
|
|
if (this._options.type) { |
|
|
|
for (var field in this._options.type) { |
|
|
|
if (this._options.type.hasOwnProperty(field)) { |
|
|
|
var value = this._options.type[field]; |
|
|
|
if (value == 'Date' || value == 'ISODate' || value == 'ASPDate') { |
|
|
|
this.type[field] = 'Date'; |
|
|
|
this._type[field] = 'Date'; |
|
|
|
} |
|
|
|
else { |
|
|
|
this.type[field] = value; |
|
|
|
this._type[field] = value; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// TODO: deprecated since version 1.1.1 (or 2.0.0?)
|
|
|
|
if (this.options.convert) { |
|
|
|
if (this._options.convert) { |
|
|
|
throw new Error('Option "convert" is deprecated. Use "type" instead.'); |
|
|
|
} |
|
|
|
|
|
|
|
this.subscribers = {}; // event subscribers
|
|
|
|
this.internalIds = {}; // internally generated id's
|
|
|
|
this._subscribers = {}; // event subscribers
|
|
|
|
|
|
|
|
// add initial data when provided
|
|
|
|
if (data) { |
|
|
@ -91,10 +87,10 @@ function DataSet (data, options) { |
|
|
|
* {String | Number} senderId |
|
|
|
*/ |
|
|
|
DataSet.prototype.on = function(event, callback) { |
|
|
|
var subscribers = this.subscribers[event]; |
|
|
|
var subscribers = this._subscribers[event]; |
|
|
|
if (!subscribers) { |
|
|
|
subscribers = []; |
|
|
|
this.subscribers[event] = subscribers; |
|
|
|
this._subscribers[event] = subscribers; |
|
|
|
} |
|
|
|
|
|
|
|
subscribers.push({ |
|
|
@ -111,9 +107,9 @@ DataSet.prototype.subscribe = DataSet.prototype.on; |
|
|
|
* @param {function} callback |
|
|
|
*/ |
|
|
|
DataSet.prototype.off = function(event, callback) { |
|
|
|
var subscribers = this.subscribers[event]; |
|
|
|
var subscribers = this._subscribers[event]; |
|
|
|
if (subscribers) { |
|
|
|
this.subscribers[event] = subscribers.filter(function (listener) { |
|
|
|
this._subscribers[event] = subscribers.filter(function (listener) { |
|
|
|
return (listener.callback != callback); |
|
|
|
}); |
|
|
|
} |
|
|
@ -135,11 +131,11 @@ DataSet.prototype._trigger = function (event, params, senderId) { |
|
|
|
} |
|
|
|
|
|
|
|
var subscribers = []; |
|
|
|
if (event in this.subscribers) { |
|
|
|
subscribers = subscribers.concat(this.subscribers[event]); |
|
|
|
if (event in this._subscribers) { |
|
|
|
subscribers = subscribers.concat(this._subscribers[event]); |
|
|
|
} |
|
|
|
if ('*' in this.subscribers) { |
|
|
|
subscribers = subscribers.concat(this.subscribers['*']); |
|
|
|
if ('*' in this._subscribers) { |
|
|
|
subscribers = subscribers.concat(this._subscribers['*']); |
|
|
|
} |
|
|
|
|
|
|
|
for (var i = 0; i < subscribers.length; i++) { |
|
|
@ -209,11 +205,11 @@ DataSet.prototype.update = function (data, senderId) { |
|
|
|
var addedIds = [], |
|
|
|
updatedIds = [], |
|
|
|
me = this, |
|
|
|
fieldId = me.fieldId; |
|
|
|
fieldId = me._fieldId; |
|
|
|
|
|
|
|
var addOrUpdate = function (item) { |
|
|
|
var id = item[fieldId]; |
|
|
|
if (me.data[id]) { |
|
|
|
if (me._data[id]) { |
|
|
|
// update item
|
|
|
|
id = me._updateItem(item); |
|
|
|
updatedIds.push(id); |
|
|
@ -299,7 +295,6 @@ DataSet.prototype.update = function (data, senderId) { |
|
|
|
*/ |
|
|
|
DataSet.prototype.get = function (args) { |
|
|
|
var me = this; |
|
|
|
var globalShowInternalIds = this.showInternalIds; |
|
|
|
|
|
|
|
// parse the arguments
|
|
|
|
var id, ids, options, data; |
|
|
@ -343,15 +338,8 @@ DataSet.prototype.get = function (args) { |
|
|
|
returnType = 'Array'; |
|
|
|
} |
|
|
|
|
|
|
|
// we allow the setting of this value for a single get request.
|
|
|
|
if (options != undefined) { |
|
|
|
if (options.showInternalIds != undefined) { |
|
|
|
this.showInternalIds = options.showInternalIds; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// build options
|
|
|
|
var type = options && options.type || this.options.type; |
|
|
|
var type = options && options.type || this._options.type; |
|
|
|
var filter = options && options.filter; |
|
|
|
var items = [], item, itemId, i, len; |
|
|
|
|
|
|
@ -374,8 +362,8 @@ DataSet.prototype.get = function (args) { |
|
|
|
} |
|
|
|
else { |
|
|
|
// return all items
|
|
|
|
for (itemId in this.data) { |
|
|
|
if (this.data.hasOwnProperty(itemId)) { |
|
|
|
for (itemId in this._data) { |
|
|
|
if (this._data.hasOwnProperty(itemId)) { |
|
|
|
item = me._getItem(itemId, type); |
|
|
|
if (!filter || filter(item)) { |
|
|
|
items.push(item); |
|
|
@ -384,9 +372,6 @@ DataSet.prototype.get = function (args) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// restore the global value of showInternalIds
|
|
|
|
this.showInternalIds = globalShowInternalIds; |
|
|
|
|
|
|
|
// order the results
|
|
|
|
if (options && options.order && id == undefined) { |
|
|
|
this._sort(items, options.order); |
|
|
@ -452,10 +437,10 @@ DataSet.prototype.get = function (args) { |
|
|
|
* @return {Array} ids |
|
|
|
*/ |
|
|
|
DataSet.prototype.getIds = function (options) { |
|
|
|
var data = this.data, |
|
|
|
var data = this._data, |
|
|
|
filter = options && options.filter, |
|
|
|
order = options && options.order, |
|
|
|
type = options && options.type || this.options.type, |
|
|
|
type = options && options.type || this._options.type, |
|
|
|
i, |
|
|
|
len, |
|
|
|
id, |
|
|
@ -480,7 +465,7 @@ DataSet.prototype.getIds = function (options) { |
|
|
|
this._sort(items, order); |
|
|
|
|
|
|
|
for (i = 0, len = items.length; i < len; i++) { |
|
|
|
ids[i] = items[i][this.fieldId]; |
|
|
|
ids[i] = items[i][this._fieldId]; |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
@ -489,7 +474,7 @@ DataSet.prototype.getIds = function (options) { |
|
|
|
if (data.hasOwnProperty(id)) { |
|
|
|
item = this._getItem(id, type); |
|
|
|
if (filter(item)) { |
|
|
|
ids.push(item[this.fieldId]); |
|
|
|
ids.push(item[this._fieldId]); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -509,7 +494,7 @@ DataSet.prototype.getIds = function (options) { |
|
|
|
this._sort(items, order); |
|
|
|
|
|
|
|
for (i = 0, len = items.length; i < len; i++) { |
|
|
|
ids[i] = items[i][this.fieldId]; |
|
|
|
ids[i] = items[i][this._fieldId]; |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
@ -517,7 +502,7 @@ DataSet.prototype.getIds = function (options) { |
|
|
|
for (id in data) { |
|
|
|
if (data.hasOwnProperty(id)) { |
|
|
|
item = data[id]; |
|
|
|
ids.push(item[this.fieldId]); |
|
|
|
ids.push(item[this._fieldId]); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -538,8 +523,8 @@ DataSet.prototype.getIds = function (options) { |
|
|
|
*/ |
|
|
|
DataSet.prototype.forEach = function (callback, options) { |
|
|
|
var filter = options && options.filter, |
|
|
|
type = options && options.type || this.options.type, |
|
|
|
data = this.data, |
|
|
|
type = options && options.type || this._options.type, |
|
|
|
data = this._data, |
|
|
|
item, |
|
|
|
id; |
|
|
|
|
|
|
@ -549,7 +534,7 @@ DataSet.prototype.forEach = function (callback, options) { |
|
|
|
|
|
|
|
for (var i = 0, len = items.length; i < len; i++) { |
|
|
|
item = items[i]; |
|
|
|
id = item[this.fieldId]; |
|
|
|
id = item[this._fieldId]; |
|
|
|
callback(item, id); |
|
|
|
} |
|
|
|
} |
|
|
@ -579,9 +564,9 @@ DataSet.prototype.forEach = function (callback, options) { |
|
|
|
*/ |
|
|
|
DataSet.prototype.map = function (callback, options) { |
|
|
|
var filter = options && options.filter, |
|
|
|
type = options && options.type || this.options.type, |
|
|
|
type = options && options.type || this._options.type, |
|
|
|
mappedItems = [], |
|
|
|
data = this.data, |
|
|
|
data = this._data, |
|
|
|
item; |
|
|
|
|
|
|
|
// convert and filter items
|
|
|
@ -689,17 +674,15 @@ DataSet.prototype.remove = function (id, senderId) { |
|
|
|
*/ |
|
|
|
DataSet.prototype._remove = function (id) { |
|
|
|
if (util.isNumber(id) || util.isString(id)) { |
|
|
|
if (this.data[id]) { |
|
|
|
delete this.data[id]; |
|
|
|
delete this.internalIds[id]; |
|
|
|
if (this._data[id]) { |
|
|
|
delete this._data[id]; |
|
|
|
return id; |
|
|
|
} |
|
|
|
} |
|
|
|
else if (id instanceof Object) { |
|
|
|
var itemId = id[this.fieldId]; |
|
|
|
if (itemId && this.data[itemId]) { |
|
|
|
delete this.data[itemId]; |
|
|
|
delete this.internalIds[itemId]; |
|
|
|
var itemId = id[this._fieldId]; |
|
|
|
if (itemId && this._data[itemId]) { |
|
|
|
delete this._data[itemId]; |
|
|
|
return itemId; |
|
|
|
} |
|
|
|
} |
|
|
@ -712,10 +695,9 @@ DataSet.prototype._remove = function (id) { |
|
|
|
* @return {Array} removedIds The ids of all removed items |
|
|
|
*/ |
|
|
|
DataSet.prototype.clear = function (senderId) { |
|
|
|
var ids = Object.keys(this.data); |
|
|
|
var ids = Object.keys(this._data); |
|
|
|
|
|
|
|
this.data = {}; |
|
|
|
this.internalIds = {}; |
|
|
|
this._data = {}; |
|
|
|
|
|
|
|
this._trigger('remove', {items: ids}, senderId); |
|
|
|
|
|
|
@ -728,7 +710,7 @@ DataSet.prototype.clear = function (senderId) { |
|
|
|
* @return {Object | null} item Item containing max value, or null if no items |
|
|
|
*/ |
|
|
|
DataSet.prototype.max = function (field) { |
|
|
|
var data = this.data, |
|
|
|
var data = this._data, |
|
|
|
max = null, |
|
|
|
maxField = null; |
|
|
|
|
|
|
@ -752,7 +734,7 @@ DataSet.prototype.max = function (field) { |
|
|
|
* @return {Object | null} item Item containing max value, or null if no items |
|
|
|
*/ |
|
|
|
DataSet.prototype.min = function (field) { |
|
|
|
var data = this.data, |
|
|
|
var data = this._data, |
|
|
|
min = null, |
|
|
|
minField = null; |
|
|
|
|
|
|
@ -778,9 +760,9 @@ DataSet.prototype.min = function (field) { |
|
|
|
* The returned array is unordered. |
|
|
|
*/ |
|
|
|
DataSet.prototype.distinct = function (field) { |
|
|
|
var data = this.data, |
|
|
|
var data = this._data, |
|
|
|
values = [], |
|
|
|
fieldType = this.options.type[field], |
|
|
|
fieldType = this._options.type[field], |
|
|
|
count = 0; |
|
|
|
|
|
|
|
for (var prop in data) { |
|
|
@ -811,11 +793,11 @@ DataSet.prototype.distinct = function (field) { |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
DataSet.prototype._addItem = function (item) { |
|
|
|
var id = item[this.fieldId]; |
|
|
|
var id = item[this._fieldId]; |
|
|
|
|
|
|
|
if (id != undefined) { |
|
|
|
// check whether this id is already taken
|
|
|
|
if (this.data[id]) { |
|
|
|
if (this._data[id]) { |
|
|
|
// item already exists
|
|
|
|
throw new Error('Cannot add item: item with id ' + id + ' already exists'); |
|
|
|
} |
|
|
@ -823,18 +805,17 @@ DataSet.prototype._addItem = function (item) { |
|
|
|
else { |
|
|
|
// generate an id
|
|
|
|
id = util.randomUUID(); |
|
|
|
item[this.fieldId] = id; |
|
|
|
this.internalIds[id] = item; |
|
|
|
item[this._fieldId] = id; |
|
|
|
} |
|
|
|
|
|
|
|
var d = {}; |
|
|
|
for (var field in item) { |
|
|
|
if (item.hasOwnProperty(field)) { |
|
|
|
var fieldType = this.type[field]; // type may be undefined
|
|
|
|
var fieldType = this._type[field]; // type may be undefined
|
|
|
|
d[field] = util.convert(item[field], fieldType); |
|
|
|
} |
|
|
|
} |
|
|
|
this.data[id] = d; |
|
|
|
this._data[id] = d; |
|
|
|
|
|
|
|
return id; |
|
|
|
}; |
|
|
@ -842,31 +823,26 @@ DataSet.prototype._addItem = function (item) { |
|
|
|
/** |
|
|
|
* Get an item. Fields can be converted to a specific type |
|
|
|
* @param {String} id |
|
|
|
* @param {Object.<String, String>} [type] field types to convert |
|
|
|
* @param {Object.<String, String>} [types] field types to convert |
|
|
|
* @return {Object | null} item |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
DataSet.prototype._getItem = function (id, type) { |
|
|
|
DataSet.prototype._getItem = function (id, types) { |
|
|
|
var field, value; |
|
|
|
|
|
|
|
// get the item from the dataset
|
|
|
|
var raw = this.data[id]; |
|
|
|
var raw = this._data[id]; |
|
|
|
if (!raw) { |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
// convert the items field types
|
|
|
|
var converted = {}, |
|
|
|
fieldId = this.fieldId, |
|
|
|
internalIds = this.internalIds; |
|
|
|
if (type) { |
|
|
|
var converted = {}; |
|
|
|
if (types) { |
|
|
|
for (field in raw) { |
|
|
|
if (raw.hasOwnProperty(field)) { |
|
|
|
value = raw[field]; |
|
|
|
// output all fields, except internal ids
|
|
|
|
if ((field != fieldId) || (!(value in internalIds) || this.showInternalIds)) { |
|
|
|
converted[field] = util.convert(value, type[field]); |
|
|
|
} |
|
|
|
converted[field] = util.convert(value, types[field]); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -875,10 +851,7 @@ DataSet.prototype._getItem = function (id, type) { |
|
|
|
for (field in raw) { |
|
|
|
if (raw.hasOwnProperty(field)) { |
|
|
|
value = raw[field]; |
|
|
|
// output all fields, except internal ids
|
|
|
|
if ((field != fieldId) || (!(value in internalIds) || this.showInternalIds)) { |
|
|
|
converted[field] = value; |
|
|
|
} |
|
|
|
converted[field] = value; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -894,11 +867,11 @@ DataSet.prototype._getItem = function (id, type) { |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
DataSet.prototype._updateItem = function (item) { |
|
|
|
var id = item[this.fieldId]; |
|
|
|
var id = item[this._fieldId]; |
|
|
|
if (id == undefined) { |
|
|
|
throw new Error('Cannot update item: item has no id (item: ' + JSON.stringify(item) + ')'); |
|
|
|
} |
|
|
|
var d = this.data[id]; |
|
|
|
var d = this._data[id]; |
|
|
|
if (!d) { |
|
|
|
// item doesn't exist
|
|
|
|
throw new Error('Cannot update item: no item with id ' + id + ' found'); |
|
|
@ -907,7 +880,7 @@ DataSet.prototype._updateItem = function (item) { |
|
|
|
// merge with current item
|
|
|
|
for (var field in item) { |
|
|
|
if (item.hasOwnProperty(field)) { |
|
|
|
var fieldType = this.type[field]; // type may be undefined
|
|
|
|
var fieldType = this._type[field]; // type may be undefined
|
|
|
|
d[field] = util.convert(item[field], fieldType); |
|
|
|
} |
|
|
|
} |
|
|
@ -915,17 +888,6 @@ DataSet.prototype._updateItem = function (item) { |
|
|
|
return id; |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
|
* check if an id is an internal or external id |
|
|
|
* @param id |
|
|
|
* @returns {boolean} |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
DataSet.prototype.isInternalId = function(id) { |
|
|
|
return (id in this.internalIds); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Get an array with the column names of a Google DataTable |
|
|
|
* @param {DataTable} dataTable |
|
|
|