vis-rails 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.gitmodules +3 -0
- data/.project +11 -0
- data/Gemfile +4 -0
- data/LICENSE +202 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/lib/vis/rails/engine.rb +6 -0
- data/lib/vis/rails/version.rb +5 -0
- data/lib/vis/rails.rb +7 -0
- data/vendor/assets/javascripts/vis.js +1 -0
- data/vendor/assets/stylesheets/vis.css +3 -0
- data/vendor/assets/vis/DataSet.js +936 -0
- data/vendor/assets/vis/DataView.js +281 -0
- data/vendor/assets/vis/EventBus.js +89 -0
- data/vendor/assets/vis/events.js +116 -0
- data/vendor/assets/vis/graph/ClusterMixin.js +1019 -0
- data/vendor/assets/vis/graph/Edge.js +620 -0
- data/vendor/assets/vis/graph/Graph.js +2111 -0
- data/vendor/assets/vis/graph/Groups.js +80 -0
- data/vendor/assets/vis/graph/Images.js +41 -0
- data/vendor/assets/vis/graph/NavigationMixin.js +245 -0
- data/vendor/assets/vis/graph/Node.js +978 -0
- data/vendor/assets/vis/graph/Popup.js +105 -0
- data/vendor/assets/vis/graph/SectorsMixin.js +547 -0
- data/vendor/assets/vis/graph/SelectionMixin.js +515 -0
- data/vendor/assets/vis/graph/dotparser.js +829 -0
- data/vendor/assets/vis/graph/img/downarrow.png +0 -0
- data/vendor/assets/vis/graph/img/leftarrow.png +0 -0
- data/vendor/assets/vis/graph/img/minus.png +0 -0
- data/vendor/assets/vis/graph/img/plus.png +0 -0
- data/vendor/assets/vis/graph/img/rightarrow.png +0 -0
- data/vendor/assets/vis/graph/img/uparrow.png +0 -0
- data/vendor/assets/vis/graph/img/zoomExtends.png +0 -0
- data/vendor/assets/vis/graph/shapes.js +225 -0
- data/vendor/assets/vis/module/exports.js +68 -0
- data/vendor/assets/vis/module/header.js +24 -0
- data/vendor/assets/vis/module/imports.js +32 -0
- data/vendor/assets/vis/shim.js +252 -0
- data/vendor/assets/vis/timeline/Controller.js +172 -0
- data/vendor/assets/vis/timeline/Range.js +553 -0
- data/vendor/assets/vis/timeline/Stack.js +192 -0
- data/vendor/assets/vis/timeline/TimeStep.js +449 -0
- data/vendor/assets/vis/timeline/Timeline.js +476 -0
- data/vendor/assets/vis/timeline/component/Component.js +148 -0
- data/vendor/assets/vis/timeline/component/ContentPanel.js +113 -0
- data/vendor/assets/vis/timeline/component/CurrentTime.js +101 -0
- data/vendor/assets/vis/timeline/component/CustomTime.js +255 -0
- data/vendor/assets/vis/timeline/component/Group.js +129 -0
- data/vendor/assets/vis/timeline/component/GroupSet.js +546 -0
- data/vendor/assets/vis/timeline/component/ItemSet.js +612 -0
- data/vendor/assets/vis/timeline/component/Panel.js +112 -0
- data/vendor/assets/vis/timeline/component/RootPanel.js +215 -0
- data/vendor/assets/vis/timeline/component/TimeAxis.js +522 -0
- data/vendor/assets/vis/timeline/component/css/currenttime.css +5 -0
- data/vendor/assets/vis/timeline/component/css/customtime.css +6 -0
- data/vendor/assets/vis/timeline/component/css/groupset.css +59 -0
- data/vendor/assets/vis/timeline/component/css/item.css +93 -0
- data/vendor/assets/vis/timeline/component/css/itemset.css +17 -0
- data/vendor/assets/vis/timeline/component/css/panel.css +14 -0
- data/vendor/assets/vis/timeline/component/css/timeaxis.css +41 -0
- data/vendor/assets/vis/timeline/component/css/timeline.css +2 -0
- data/vendor/assets/vis/timeline/component/item/Item.js +81 -0
- data/vendor/assets/vis/timeline/component/item/ItemBox.js +302 -0
- data/vendor/assets/vis/timeline/component/item/ItemPoint.js +237 -0
- data/vendor/assets/vis/timeline/component/item/ItemRange.js +251 -0
- data/vendor/assets/vis/timeline/component/item/ItemRangeOverflow.js +91 -0
- data/vendor/assets/vis/util.js +673 -0
- data/vis-rails.gemspec +47 -0
- metadata +142 -0
@@ -0,0 +1,936 @@
|
|
1
|
+
/**
|
2
|
+
* DataSet
|
3
|
+
*
|
4
|
+
* Usage:
|
5
|
+
* var dataSet = new DataSet({
|
6
|
+
* fieldId: '_id',
|
7
|
+
* convert: {
|
8
|
+
* // ...
|
9
|
+
* }
|
10
|
+
* });
|
11
|
+
*
|
12
|
+
* dataSet.add(item);
|
13
|
+
* dataSet.add(data);
|
14
|
+
* dataSet.update(item);
|
15
|
+
* dataSet.update(data);
|
16
|
+
* dataSet.remove(id);
|
17
|
+
* dataSet.remove(ids);
|
18
|
+
* var data = dataSet.get();
|
19
|
+
* var data = dataSet.get(id);
|
20
|
+
* var data = dataSet.get(ids);
|
21
|
+
* var data = dataSet.get(ids, options, data);
|
22
|
+
* dataSet.clear();
|
23
|
+
*
|
24
|
+
* A data set can:
|
25
|
+
* - add/remove/update data
|
26
|
+
* - gives triggers upon changes in the data
|
27
|
+
* - can import/export data in various data formats
|
28
|
+
*
|
29
|
+
* @param {Object} [options] Available options:
|
30
|
+
* {String} fieldId Field name of the id in the
|
31
|
+
* items, 'id' by default.
|
32
|
+
* {Object.<String, String} convert
|
33
|
+
* A map with field names as key,
|
34
|
+
* and the field type as value.
|
35
|
+
* @constructor DataSet
|
36
|
+
*/
|
37
|
+
// TODO: add a DataSet constructor DataSet(data, options)
|
38
|
+
function DataSet (options) {
|
39
|
+
this.id = util.randomUUID();
|
40
|
+
|
41
|
+
this.options = options || {};
|
42
|
+
this.data = {}; // map with data indexed by id
|
43
|
+
this.fieldId = this.options.fieldId || 'id'; // name of the field containing id
|
44
|
+
this.convert = {}; // field types by field name
|
45
|
+
this.showInternalIds = this.options.showInternalIds || false; // show internal ids with the get function
|
46
|
+
|
47
|
+
if (this.options.convert) {
|
48
|
+
for (var field in this.options.convert) {
|
49
|
+
if (this.options.convert.hasOwnProperty(field)) {
|
50
|
+
var value = this.options.convert[field];
|
51
|
+
if (value == 'Date' || value == 'ISODate' || value == 'ASPDate') {
|
52
|
+
this.convert[field] = 'Date';
|
53
|
+
}
|
54
|
+
else {
|
55
|
+
this.convert[field] = value;
|
56
|
+
}
|
57
|
+
}
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
// event subscribers
|
62
|
+
this.subscribers = {};
|
63
|
+
|
64
|
+
this.internalIds = {}; // internally generated id's
|
65
|
+
}
|
66
|
+
|
67
|
+
/**
|
68
|
+
* Subscribe to an event, add an event listener
|
69
|
+
* @param {String} event Event name. Available events: 'put', 'update',
|
70
|
+
* 'remove'
|
71
|
+
* @param {function} callback Callback method. Called with three parameters:
|
72
|
+
* {String} event
|
73
|
+
* {Object | null} params
|
74
|
+
* {String | Number} senderId
|
75
|
+
*/
|
76
|
+
DataSet.prototype.subscribe = function (event, callback) {
|
77
|
+
var subscribers = this.subscribers[event];
|
78
|
+
if (!subscribers) {
|
79
|
+
subscribers = [];
|
80
|
+
this.subscribers[event] = subscribers;
|
81
|
+
}
|
82
|
+
|
83
|
+
subscribers.push({
|
84
|
+
callback: callback
|
85
|
+
});
|
86
|
+
};
|
87
|
+
|
88
|
+
/**
|
89
|
+
* Unsubscribe from an event, remove an event listener
|
90
|
+
* @param {String} event
|
91
|
+
* @param {function} callback
|
92
|
+
*/
|
93
|
+
DataSet.prototype.unsubscribe = function (event, callback) {
|
94
|
+
var subscribers = this.subscribers[event];
|
95
|
+
if (subscribers) {
|
96
|
+
this.subscribers[event] = subscribers.filter(function (listener) {
|
97
|
+
return (listener.callback != callback);
|
98
|
+
});
|
99
|
+
}
|
100
|
+
};
|
101
|
+
|
102
|
+
/**
|
103
|
+
* Trigger an event
|
104
|
+
* @param {String} event
|
105
|
+
* @param {Object | null} params
|
106
|
+
* @param {String} [senderId] Optional id of the sender.
|
107
|
+
* @private
|
108
|
+
*/
|
109
|
+
DataSet.prototype._trigger = function (event, params, senderId) {
|
110
|
+
if (event == '*') {
|
111
|
+
throw new Error('Cannot trigger event *');
|
112
|
+
}
|
113
|
+
|
114
|
+
var subscribers = [];
|
115
|
+
if (event in this.subscribers) {
|
116
|
+
subscribers = subscribers.concat(this.subscribers[event]);
|
117
|
+
}
|
118
|
+
if ('*' in this.subscribers) {
|
119
|
+
subscribers = subscribers.concat(this.subscribers['*']);
|
120
|
+
}
|
121
|
+
|
122
|
+
for (var i = 0; i < subscribers.length; i++) {
|
123
|
+
var subscriber = subscribers[i];
|
124
|
+
if (subscriber.callback) {
|
125
|
+
subscriber.callback(event, params, senderId || null);
|
126
|
+
}
|
127
|
+
}
|
128
|
+
};
|
129
|
+
|
130
|
+
/**
|
131
|
+
* Add data.
|
132
|
+
* Adding an item will fail when there already is an item with the same id.
|
133
|
+
* @param {Object | Array | DataTable} data
|
134
|
+
* @param {String} [senderId] Optional sender id
|
135
|
+
* @return {Array} addedIds Array with the ids of the added items
|
136
|
+
*/
|
137
|
+
DataSet.prototype.add = function (data, senderId) {
|
138
|
+
var addedIds = [],
|
139
|
+
id,
|
140
|
+
me = this;
|
141
|
+
|
142
|
+
if (data instanceof Array) {
|
143
|
+
// Array
|
144
|
+
for (var i = 0, len = data.length; i < len; i++) {
|
145
|
+
id = me._addItem(data[i]);
|
146
|
+
addedIds.push(id);
|
147
|
+
}
|
148
|
+
}
|
149
|
+
else if (util.isDataTable(data)) {
|
150
|
+
// Google DataTable
|
151
|
+
var columns = this._getColumnNames(data);
|
152
|
+
for (var row = 0, rows = data.getNumberOfRows(); row < rows; row++) {
|
153
|
+
var item = {};
|
154
|
+
for (var col = 0, cols = columns.length; col < cols; col++) {
|
155
|
+
var field = columns[col];
|
156
|
+
item[field] = data.getValue(row, col);
|
157
|
+
}
|
158
|
+
|
159
|
+
id = me._addItem(item);
|
160
|
+
addedIds.push(id);
|
161
|
+
}
|
162
|
+
}
|
163
|
+
else if (data instanceof Object) {
|
164
|
+
// Single item
|
165
|
+
id = me._addItem(data);
|
166
|
+
addedIds.push(id);
|
167
|
+
}
|
168
|
+
else {
|
169
|
+
throw new Error('Unknown dataType');
|
170
|
+
}
|
171
|
+
|
172
|
+
if (addedIds.length) {
|
173
|
+
this._trigger('add', {items: addedIds}, senderId);
|
174
|
+
}
|
175
|
+
|
176
|
+
return addedIds;
|
177
|
+
};
|
178
|
+
|
179
|
+
/**
|
180
|
+
* Update existing items. When an item does not exist, it will be created
|
181
|
+
* @param {Object | Array | DataTable} data
|
182
|
+
* @param {String} [senderId] Optional sender id
|
183
|
+
* @return {Array} updatedIds The ids of the added or updated items
|
184
|
+
*/
|
185
|
+
DataSet.prototype.update = function (data, senderId) {
|
186
|
+
var addedIds = [],
|
187
|
+
updatedIds = [],
|
188
|
+
me = this,
|
189
|
+
fieldId = me.fieldId;
|
190
|
+
|
191
|
+
var addOrUpdate = function (item) {
|
192
|
+
var id = item[fieldId];
|
193
|
+
if (me.data[id]) {
|
194
|
+
// update item
|
195
|
+
id = me._updateItem(item);
|
196
|
+
updatedIds.push(id);
|
197
|
+
}
|
198
|
+
else {
|
199
|
+
// add new item
|
200
|
+
id = me._addItem(item);
|
201
|
+
addedIds.push(id);
|
202
|
+
}
|
203
|
+
};
|
204
|
+
|
205
|
+
if (data instanceof Array) {
|
206
|
+
// Array
|
207
|
+
for (var i = 0, len = data.length; i < len; i++) {
|
208
|
+
addOrUpdate(data[i]);
|
209
|
+
}
|
210
|
+
}
|
211
|
+
else if (util.isDataTable(data)) {
|
212
|
+
// Google DataTable
|
213
|
+
var columns = this._getColumnNames(data);
|
214
|
+
for (var row = 0, rows = data.getNumberOfRows(); row < rows; row++) {
|
215
|
+
var item = {};
|
216
|
+
for (var col = 0, cols = columns.length; col < cols; col++) {
|
217
|
+
var field = columns[col];
|
218
|
+
item[field] = data.getValue(row, col);
|
219
|
+
}
|
220
|
+
|
221
|
+
addOrUpdate(item);
|
222
|
+
}
|
223
|
+
}
|
224
|
+
else if (data instanceof Object) {
|
225
|
+
// Single item
|
226
|
+
addOrUpdate(data);
|
227
|
+
}
|
228
|
+
else {
|
229
|
+
throw new Error('Unknown dataType');
|
230
|
+
}
|
231
|
+
|
232
|
+
if (addedIds.length) {
|
233
|
+
this._trigger('add', {items: addedIds}, senderId);
|
234
|
+
}
|
235
|
+
if (updatedIds.length) {
|
236
|
+
this._trigger('update', {items: updatedIds}, senderId);
|
237
|
+
}
|
238
|
+
|
239
|
+
return addedIds.concat(updatedIds);
|
240
|
+
};
|
241
|
+
|
242
|
+
/**
|
243
|
+
* Get a data item or multiple items.
|
244
|
+
*
|
245
|
+
* Usage:
|
246
|
+
*
|
247
|
+
* get()
|
248
|
+
* get(options: Object)
|
249
|
+
* get(options: Object, data: Array | DataTable)
|
250
|
+
*
|
251
|
+
* get(id: Number | String)
|
252
|
+
* get(id: Number | String, options: Object)
|
253
|
+
* get(id: Number | String, options: Object, data: Array | DataTable)
|
254
|
+
*
|
255
|
+
* get(ids: Number[] | String[])
|
256
|
+
* get(ids: Number[] | String[], options: Object)
|
257
|
+
* get(ids: Number[] | String[], options: Object, data: Array | DataTable)
|
258
|
+
*
|
259
|
+
* Where:
|
260
|
+
*
|
261
|
+
* {Number | String} id The id of an item
|
262
|
+
* {Number[] | String{}} ids An array with ids of items
|
263
|
+
* {Object} options An Object with options. Available options:
|
264
|
+
* {String} [type] Type of data to be returned. Can
|
265
|
+
* be 'DataTable' or 'Array' (default)
|
266
|
+
* {Object.<String, String>} [convert]
|
267
|
+
* {String[]} [fields] field names to be returned
|
268
|
+
* {function} [filter] filter items
|
269
|
+
* {String | function} [order] Order the items by
|
270
|
+
* a field name or custom sort function.
|
271
|
+
* {Array | DataTable} [data] If provided, items will be appended to this
|
272
|
+
* array or table. Required in case of Google
|
273
|
+
* DataTable.
|
274
|
+
*
|
275
|
+
* @throws Error
|
276
|
+
*/
|
277
|
+
DataSet.prototype.get = function (args) {
|
278
|
+
var me = this;
|
279
|
+
var globalShowInternalIds = this.showInternalIds;
|
280
|
+
|
281
|
+
// parse the arguments
|
282
|
+
var id, ids, options, data;
|
283
|
+
var firstType = util.getType(arguments[0]);
|
284
|
+
if (firstType == 'String' || firstType == 'Number') {
|
285
|
+
// get(id [, options] [, data])
|
286
|
+
id = arguments[0];
|
287
|
+
options = arguments[1];
|
288
|
+
data = arguments[2];
|
289
|
+
}
|
290
|
+
else if (firstType == 'Array') {
|
291
|
+
// get(ids [, options] [, data])
|
292
|
+
ids = arguments[0];
|
293
|
+
options = arguments[1];
|
294
|
+
data = arguments[2];
|
295
|
+
}
|
296
|
+
else {
|
297
|
+
// get([, options] [, data])
|
298
|
+
options = arguments[0];
|
299
|
+
data = arguments[1];
|
300
|
+
}
|
301
|
+
|
302
|
+
// determine the return type
|
303
|
+
var type;
|
304
|
+
if (options && options.type) {
|
305
|
+
type = (options.type == 'DataTable') ? 'DataTable' : 'Array';
|
306
|
+
|
307
|
+
if (data && (type != util.getType(data))) {
|
308
|
+
throw new Error('Type of parameter "data" (' + util.getType(data) + ') ' +
|
309
|
+
'does not correspond with specified options.type (' + options.type + ')');
|
310
|
+
}
|
311
|
+
if (type == 'DataTable' && !util.isDataTable(data)) {
|
312
|
+
throw new Error('Parameter "data" must be a DataTable ' +
|
313
|
+
'when options.type is "DataTable"');
|
314
|
+
}
|
315
|
+
}
|
316
|
+
else if (data) {
|
317
|
+
type = (util.getType(data) == 'DataTable') ? 'DataTable' : 'Array';
|
318
|
+
}
|
319
|
+
else {
|
320
|
+
type = 'Array';
|
321
|
+
}
|
322
|
+
|
323
|
+
// we allow the setting of this value for a single get request.
|
324
|
+
if (options != undefined) {
|
325
|
+
if (options.showInternalIds != undefined) {
|
326
|
+
this.showInternalIds = options.showInternalIds;
|
327
|
+
}
|
328
|
+
}
|
329
|
+
|
330
|
+
// build options
|
331
|
+
var convert = options && options.convert || this.options.convert;
|
332
|
+
var filter = options && options.filter;
|
333
|
+
var items = [], item, itemId, i, len;
|
334
|
+
|
335
|
+
// convert items
|
336
|
+
if (id != undefined) {
|
337
|
+
// return a single item
|
338
|
+
item = me._getItem(id, convert);
|
339
|
+
if (filter && !filter(item)) {
|
340
|
+
item = null;
|
341
|
+
}
|
342
|
+
}
|
343
|
+
else if (ids != undefined) {
|
344
|
+
// return a subset of items
|
345
|
+
for (i = 0, len = ids.length; i < len; i++) {
|
346
|
+
item = me._getItem(ids[i], convert);
|
347
|
+
if (!filter || filter(item)) {
|
348
|
+
items.push(item);
|
349
|
+
}
|
350
|
+
}
|
351
|
+
}
|
352
|
+
else {
|
353
|
+
// return all items
|
354
|
+
for (itemId in this.data) {
|
355
|
+
if (this.data.hasOwnProperty(itemId)) {
|
356
|
+
item = me._getItem(itemId, convert);
|
357
|
+
if (!filter || filter(item)) {
|
358
|
+
items.push(item);
|
359
|
+
}
|
360
|
+
}
|
361
|
+
}
|
362
|
+
}
|
363
|
+
|
364
|
+
// restore the global value of showInternalIds
|
365
|
+
this.showInternalIds = globalShowInternalIds;
|
366
|
+
|
367
|
+
// order the results
|
368
|
+
if (options && options.order && id == undefined) {
|
369
|
+
this._sort(items, options.order);
|
370
|
+
}
|
371
|
+
|
372
|
+
// filter fields of the items
|
373
|
+
if (options && options.fields) {
|
374
|
+
var fields = options.fields;
|
375
|
+
if (id != undefined) {
|
376
|
+
item = this._filterFields(item, fields);
|
377
|
+
}
|
378
|
+
else {
|
379
|
+
for (i = 0, len = items.length; i < len; i++) {
|
380
|
+
items[i] = this._filterFields(items[i], fields);
|
381
|
+
}
|
382
|
+
}
|
383
|
+
}
|
384
|
+
|
385
|
+
// return the results
|
386
|
+
if (type == 'DataTable') {
|
387
|
+
var columns = this._getColumnNames(data);
|
388
|
+
if (id != undefined) {
|
389
|
+
// append a single item to the data table
|
390
|
+
me._appendRow(data, columns, item);
|
391
|
+
}
|
392
|
+
else {
|
393
|
+
// copy the items to the provided data table
|
394
|
+
for (i = 0, len = items.length; i < len; i++) {
|
395
|
+
me._appendRow(data, columns, items[i]);
|
396
|
+
}
|
397
|
+
}
|
398
|
+
return data;
|
399
|
+
}
|
400
|
+
else {
|
401
|
+
// return an array
|
402
|
+
if (id != undefined) {
|
403
|
+
// a single item
|
404
|
+
return item;
|
405
|
+
}
|
406
|
+
else {
|
407
|
+
// multiple items
|
408
|
+
if (data) {
|
409
|
+
// copy the items to the provided array
|
410
|
+
for (i = 0, len = items.length; i < len; i++) {
|
411
|
+
data.push(items[i]);
|
412
|
+
}
|
413
|
+
return data;
|
414
|
+
}
|
415
|
+
else {
|
416
|
+
// just return our array
|
417
|
+
return items;
|
418
|
+
}
|
419
|
+
}
|
420
|
+
}
|
421
|
+
};
|
422
|
+
|
423
|
+
/**
|
424
|
+
* Get ids of all items or from a filtered set of items.
|
425
|
+
* @param {Object} [options] An Object with options. Available options:
|
426
|
+
* {function} [filter] filter items
|
427
|
+
* {String | function} [order] Order the items by
|
428
|
+
* a field name or custom sort function.
|
429
|
+
* @return {Array} ids
|
430
|
+
*/
|
431
|
+
DataSet.prototype.getIds = function (options) {
|
432
|
+
var data = this.data,
|
433
|
+
filter = options && options.filter,
|
434
|
+
order = options && options.order,
|
435
|
+
convert = options && options.convert || this.options.convert,
|
436
|
+
i,
|
437
|
+
len,
|
438
|
+
id,
|
439
|
+
item,
|
440
|
+
items,
|
441
|
+
ids = [];
|
442
|
+
|
443
|
+
if (filter) {
|
444
|
+
// get filtered items
|
445
|
+
if (order) {
|
446
|
+
// create ordered list
|
447
|
+
items = [];
|
448
|
+
for (id in data) {
|
449
|
+
if (data.hasOwnProperty(id)) {
|
450
|
+
item = this._getItem(id, convert);
|
451
|
+
if (filter(item)) {
|
452
|
+
items.push(item);
|
453
|
+
}
|
454
|
+
}
|
455
|
+
}
|
456
|
+
|
457
|
+
this._sort(items, order);
|
458
|
+
|
459
|
+
for (i = 0, len = items.length; i < len; i++) {
|
460
|
+
ids[i] = items[i][this.fieldId];
|
461
|
+
}
|
462
|
+
}
|
463
|
+
else {
|
464
|
+
// create unordered list
|
465
|
+
for (id in data) {
|
466
|
+
if (data.hasOwnProperty(id)) {
|
467
|
+
item = this._getItem(id, convert);
|
468
|
+
if (filter(item)) {
|
469
|
+
ids.push(item[this.fieldId]);
|
470
|
+
}
|
471
|
+
}
|
472
|
+
}
|
473
|
+
}
|
474
|
+
}
|
475
|
+
else {
|
476
|
+
// get all items
|
477
|
+
if (order) {
|
478
|
+
// create an ordered list
|
479
|
+
items = [];
|
480
|
+
for (id in data) {
|
481
|
+
if (data.hasOwnProperty(id)) {
|
482
|
+
items.push(data[id]);
|
483
|
+
}
|
484
|
+
}
|
485
|
+
|
486
|
+
this._sort(items, order);
|
487
|
+
|
488
|
+
for (i = 0, len = items.length; i < len; i++) {
|
489
|
+
ids[i] = items[i][this.fieldId];
|
490
|
+
}
|
491
|
+
}
|
492
|
+
else {
|
493
|
+
// create unordered list
|
494
|
+
for (id in data) {
|
495
|
+
if (data.hasOwnProperty(id)) {
|
496
|
+
item = data[id];
|
497
|
+
ids.push(item[this.fieldId]);
|
498
|
+
}
|
499
|
+
}
|
500
|
+
}
|
501
|
+
}
|
502
|
+
|
503
|
+
return ids;
|
504
|
+
};
|
505
|
+
|
506
|
+
/**
|
507
|
+
* Execute a callback function for every item in the dataset.
|
508
|
+
* The order of the items is not determined.
|
509
|
+
* @param {function} callback
|
510
|
+
* @param {Object} [options] Available options:
|
511
|
+
* {Object.<String, String>} [convert]
|
512
|
+
* {String[]} [fields] filter fields
|
513
|
+
* {function} [filter] filter items
|
514
|
+
* {String | function} [order] Order the items by
|
515
|
+
* a field name or custom sort function.
|
516
|
+
*/
|
517
|
+
DataSet.prototype.forEach = function (callback, options) {
|
518
|
+
var filter = options && options.filter,
|
519
|
+
convert = options && options.convert || this.options.convert,
|
520
|
+
data = this.data,
|
521
|
+
item,
|
522
|
+
id;
|
523
|
+
|
524
|
+
if (options && options.order) {
|
525
|
+
// execute forEach on ordered list
|
526
|
+
var items = this.get(options);
|
527
|
+
|
528
|
+
for (var i = 0, len = items.length; i < len; i++) {
|
529
|
+
item = items[i];
|
530
|
+
id = item[this.fieldId];
|
531
|
+
callback(item, id);
|
532
|
+
}
|
533
|
+
}
|
534
|
+
else {
|
535
|
+
// unordered
|
536
|
+
for (id in data) {
|
537
|
+
if (data.hasOwnProperty(id)) {
|
538
|
+
item = this._getItem(id, convert);
|
539
|
+
if (!filter || filter(item)) {
|
540
|
+
callback(item, id);
|
541
|
+
}
|
542
|
+
}
|
543
|
+
}
|
544
|
+
}
|
545
|
+
};
|
546
|
+
|
547
|
+
/**
|
548
|
+
* Map every item in the dataset.
|
549
|
+
* @param {function} callback
|
550
|
+
* @param {Object} [options] Available options:
|
551
|
+
* {Object.<String, String>} [convert]
|
552
|
+
* {String[]} [fields] filter fields
|
553
|
+
* {function} [filter] filter items
|
554
|
+
* {String | function} [order] Order the items by
|
555
|
+
* a field name or custom sort function.
|
556
|
+
* @return {Object[]} mappedItems
|
557
|
+
*/
|
558
|
+
DataSet.prototype.map = function (callback, options) {
|
559
|
+
var filter = options && options.filter,
|
560
|
+
convert = options && options.convert || this.options.convert,
|
561
|
+
mappedItems = [],
|
562
|
+
data = this.data,
|
563
|
+
item;
|
564
|
+
|
565
|
+
// convert and filter items
|
566
|
+
for (var id in data) {
|
567
|
+
if (data.hasOwnProperty(id)) {
|
568
|
+
item = this._getItem(id, convert);
|
569
|
+
if (!filter || filter(item)) {
|
570
|
+
mappedItems.push(callback(item, id));
|
571
|
+
}
|
572
|
+
}
|
573
|
+
}
|
574
|
+
|
575
|
+
// order items
|
576
|
+
if (options && options.order) {
|
577
|
+
this._sort(mappedItems, options.order);
|
578
|
+
}
|
579
|
+
|
580
|
+
return mappedItems;
|
581
|
+
};
|
582
|
+
|
583
|
+
/**
|
584
|
+
* Filter the fields of an item
|
585
|
+
* @param {Object} item
|
586
|
+
* @param {String[]} fields Field names
|
587
|
+
* @return {Object} filteredItem
|
588
|
+
* @private
|
589
|
+
*/
|
590
|
+
DataSet.prototype._filterFields = function (item, fields) {
|
591
|
+
var filteredItem = {};
|
592
|
+
|
593
|
+
for (var field in item) {
|
594
|
+
if (item.hasOwnProperty(field) && (fields.indexOf(field) != -1)) {
|
595
|
+
filteredItem[field] = item[field];
|
596
|
+
}
|
597
|
+
}
|
598
|
+
|
599
|
+
return filteredItem;
|
600
|
+
};
|
601
|
+
|
602
|
+
/**
|
603
|
+
* Sort the provided array with items
|
604
|
+
* @param {Object[]} items
|
605
|
+
* @param {String | function} order A field name or custom sort function.
|
606
|
+
* @private
|
607
|
+
*/
|
608
|
+
DataSet.prototype._sort = function (items, order) {
|
609
|
+
if (util.isString(order)) {
|
610
|
+
// order by provided field name
|
611
|
+
var name = order; // field name
|
612
|
+
items.sort(function (a, b) {
|
613
|
+
var av = a[name];
|
614
|
+
var bv = b[name];
|
615
|
+
return (av > bv) ? 1 : ((av < bv) ? -1 : 0);
|
616
|
+
});
|
617
|
+
}
|
618
|
+
else if (typeof order === 'function') {
|
619
|
+
// order by sort function
|
620
|
+
items.sort(order);
|
621
|
+
}
|
622
|
+
// TODO: extend order by an Object {field:String, direction:String}
|
623
|
+
// where direction can be 'asc' or 'desc'
|
624
|
+
else {
|
625
|
+
throw new TypeError('Order must be a function or a string');
|
626
|
+
}
|
627
|
+
};
|
628
|
+
|
629
|
+
/**
|
630
|
+
* Remove an object by pointer or by id
|
631
|
+
* @param {String | Number | Object | Array} id Object or id, or an array with
|
632
|
+
* objects or ids to be removed
|
633
|
+
* @param {String} [senderId] Optional sender id
|
634
|
+
* @return {Array} removedIds
|
635
|
+
*/
|
636
|
+
DataSet.prototype.remove = function (id, senderId) {
|
637
|
+
var removedIds = [],
|
638
|
+
i, len, removedId;
|
639
|
+
|
640
|
+
if (id instanceof Array) {
|
641
|
+
for (i = 0, len = id.length; i < len; i++) {
|
642
|
+
removedId = this._remove(id[i]);
|
643
|
+
if (removedId != null) {
|
644
|
+
removedIds.push(removedId);
|
645
|
+
}
|
646
|
+
}
|
647
|
+
}
|
648
|
+
else {
|
649
|
+
removedId = this._remove(id);
|
650
|
+
if (removedId != null) {
|
651
|
+
removedIds.push(removedId);
|
652
|
+
}
|
653
|
+
}
|
654
|
+
|
655
|
+
if (removedIds.length) {
|
656
|
+
this._trigger('remove', {items: removedIds}, senderId);
|
657
|
+
}
|
658
|
+
|
659
|
+
return removedIds;
|
660
|
+
};
|
661
|
+
|
662
|
+
/**
|
663
|
+
* Remove an item by its id
|
664
|
+
* @param {Number | String | Object} id id or item
|
665
|
+
* @returns {Number | String | null} id
|
666
|
+
* @private
|
667
|
+
*/
|
668
|
+
DataSet.prototype._remove = function (id) {
|
669
|
+
if (util.isNumber(id) || util.isString(id)) {
|
670
|
+
if (this.data[id]) {
|
671
|
+
delete this.data[id];
|
672
|
+
delete this.internalIds[id];
|
673
|
+
return id;
|
674
|
+
}
|
675
|
+
}
|
676
|
+
else if (id instanceof Object) {
|
677
|
+
var itemId = id[this.fieldId];
|
678
|
+
if (itemId && this.data[itemId]) {
|
679
|
+
delete this.data[itemId];
|
680
|
+
delete this.internalIds[itemId];
|
681
|
+
return itemId;
|
682
|
+
}
|
683
|
+
}
|
684
|
+
return null;
|
685
|
+
};
|
686
|
+
|
687
|
+
/**
|
688
|
+
* Clear the data
|
689
|
+
* @param {String} [senderId] Optional sender id
|
690
|
+
* @return {Array} removedIds The ids of all removed items
|
691
|
+
*/
|
692
|
+
DataSet.prototype.clear = function (senderId) {
|
693
|
+
var ids = Object.keys(this.data);
|
694
|
+
|
695
|
+
this.data = {};
|
696
|
+
this.internalIds = {};
|
697
|
+
|
698
|
+
this._trigger('remove', {items: ids}, senderId);
|
699
|
+
|
700
|
+
return ids;
|
701
|
+
};
|
702
|
+
|
703
|
+
/**
|
704
|
+
* Find the item with maximum value of a specified field
|
705
|
+
* @param {String} field
|
706
|
+
* @return {Object | null} item Item containing max value, or null if no items
|
707
|
+
*/
|
708
|
+
DataSet.prototype.max = function (field) {
|
709
|
+
var data = this.data,
|
710
|
+
max = null,
|
711
|
+
maxField = null;
|
712
|
+
|
713
|
+
for (var id in data) {
|
714
|
+
if (data.hasOwnProperty(id)) {
|
715
|
+
var item = data[id];
|
716
|
+
var itemField = item[field];
|
717
|
+
if (itemField != null && (!max || itemField > maxField)) {
|
718
|
+
max = item;
|
719
|
+
maxField = itemField;
|
720
|
+
}
|
721
|
+
}
|
722
|
+
}
|
723
|
+
|
724
|
+
return max;
|
725
|
+
};
|
726
|
+
|
727
|
+
/**
|
728
|
+
* Find the item with minimum value of a specified field
|
729
|
+
* @param {String} field
|
730
|
+
* @return {Object | null} item Item containing max value, or null if no items
|
731
|
+
*/
|
732
|
+
DataSet.prototype.min = function (field) {
|
733
|
+
var data = this.data,
|
734
|
+
min = null,
|
735
|
+
minField = null;
|
736
|
+
|
737
|
+
for (var id in data) {
|
738
|
+
if (data.hasOwnProperty(id)) {
|
739
|
+
var item = data[id];
|
740
|
+
var itemField = item[field];
|
741
|
+
if (itemField != null && (!min || itemField < minField)) {
|
742
|
+
min = item;
|
743
|
+
minField = itemField;
|
744
|
+
}
|
745
|
+
}
|
746
|
+
}
|
747
|
+
|
748
|
+
return min;
|
749
|
+
};
|
750
|
+
|
751
|
+
/**
|
752
|
+
* Find all distinct values of a specified field
|
753
|
+
* @param {String} field
|
754
|
+
* @return {Array} values Array containing all distinct values. If the data
|
755
|
+
* items do not contain the specified field, an array
|
756
|
+
* containing a single value undefined is returned.
|
757
|
+
* The returned array is unordered.
|
758
|
+
*/
|
759
|
+
DataSet.prototype.distinct = function (field) {
|
760
|
+
var data = this.data,
|
761
|
+
values = [],
|
762
|
+
fieldType = this.options.convert[field],
|
763
|
+
count = 0;
|
764
|
+
|
765
|
+
for (var prop in data) {
|
766
|
+
if (data.hasOwnProperty(prop)) {
|
767
|
+
var item = data[prop];
|
768
|
+
var value = util.convert(item[field], fieldType);
|
769
|
+
var exists = false;
|
770
|
+
for (var i = 0; i < count; i++) {
|
771
|
+
if (values[i] == value) {
|
772
|
+
exists = true;
|
773
|
+
break;
|
774
|
+
}
|
775
|
+
}
|
776
|
+
if (!exists) {
|
777
|
+
values[count] = value;
|
778
|
+
count++;
|
779
|
+
}
|
780
|
+
}
|
781
|
+
}
|
782
|
+
|
783
|
+
return values;
|
784
|
+
};
|
785
|
+
|
786
|
+
/**
|
787
|
+
* Add a single item. Will fail when an item with the same id already exists.
|
788
|
+
* @param {Object} item
|
789
|
+
* @return {String} id
|
790
|
+
* @private
|
791
|
+
*/
|
792
|
+
DataSet.prototype._addItem = function (item) {
|
793
|
+
var id = item[this.fieldId];
|
794
|
+
|
795
|
+
if (id != undefined) {
|
796
|
+
// check whether this id is already taken
|
797
|
+
if (this.data[id]) {
|
798
|
+
// item already exists
|
799
|
+
throw new Error('Cannot add item: item with id ' + id + ' already exists');
|
800
|
+
}
|
801
|
+
}
|
802
|
+
else {
|
803
|
+
// generate an id
|
804
|
+
id = util.randomUUID();
|
805
|
+
item[this.fieldId] = id;
|
806
|
+
this.internalIds[id] = item;
|
807
|
+
}
|
808
|
+
|
809
|
+
var d = {};
|
810
|
+
for (var field in item) {
|
811
|
+
if (item.hasOwnProperty(field)) {
|
812
|
+
var fieldType = this.convert[field]; // type may be undefined
|
813
|
+
d[field] = util.convert(item[field], fieldType);
|
814
|
+
}
|
815
|
+
}
|
816
|
+
this.data[id] = d;
|
817
|
+
|
818
|
+
return id;
|
819
|
+
};
|
820
|
+
|
821
|
+
/**
|
822
|
+
* Get an item. Fields can be converted to a specific type
|
823
|
+
* @param {String} id
|
824
|
+
* @param {Object.<String, String>} [convert] field types to convert
|
825
|
+
* @return {Object | null} item
|
826
|
+
* @private
|
827
|
+
*/
|
828
|
+
DataSet.prototype._getItem = function (id, convert) {
|
829
|
+
var field, value;
|
830
|
+
|
831
|
+
// get the item from the dataset
|
832
|
+
var raw = this.data[id];
|
833
|
+
if (!raw) {
|
834
|
+
return null;
|
835
|
+
}
|
836
|
+
|
837
|
+
// convert the items field types
|
838
|
+
var converted = {},
|
839
|
+
fieldId = this.fieldId,
|
840
|
+
internalIds = this.internalIds;
|
841
|
+
if (convert) {
|
842
|
+
for (field in raw) {
|
843
|
+
if (raw.hasOwnProperty(field)) {
|
844
|
+
value = raw[field];
|
845
|
+
// output all fields, except internal ids
|
846
|
+
if ((field != fieldId) || (!(value in internalIds) || this.showInternalIds)) {
|
847
|
+
converted[field] = util.convert(value, convert[field]);
|
848
|
+
}
|
849
|
+
}
|
850
|
+
}
|
851
|
+
}
|
852
|
+
else {
|
853
|
+
// no field types specified, no converting needed
|
854
|
+
for (field in raw) {
|
855
|
+
if (raw.hasOwnProperty(field)) {
|
856
|
+
value = raw[field];
|
857
|
+
// output all fields, except internal ids
|
858
|
+
if ((field != fieldId) || (!(value in internalIds) || this.showInternalIds)) {
|
859
|
+
converted[field] = value;
|
860
|
+
}
|
861
|
+
}
|
862
|
+
}
|
863
|
+
}
|
864
|
+
return converted;
|
865
|
+
};
|
866
|
+
|
867
|
+
/**
|
868
|
+
* Update a single item: merge with existing item.
|
869
|
+
* Will fail when the item has no id, or when there does not exist an item
|
870
|
+
* with the same id.
|
871
|
+
* @param {Object} item
|
872
|
+
* @return {String} id
|
873
|
+
* @private
|
874
|
+
*/
|
875
|
+
DataSet.prototype._updateItem = function (item) {
|
876
|
+
var id = item[this.fieldId];
|
877
|
+
if (id == undefined) {
|
878
|
+
throw new Error('Cannot update item: item has no id (item: ' + JSON.stringify(item) + ')');
|
879
|
+
}
|
880
|
+
var d = this.data[id];
|
881
|
+
if (!d) {
|
882
|
+
// item doesn't exist
|
883
|
+
throw new Error('Cannot update item: no item with id ' + id + ' found');
|
884
|
+
}
|
885
|
+
|
886
|
+
// merge with current item
|
887
|
+
for (var field in item) {
|
888
|
+
if (item.hasOwnProperty(field)) {
|
889
|
+
var fieldType = this.convert[field]; // type may be undefined
|
890
|
+
d[field] = util.convert(item[field], fieldType);
|
891
|
+
}
|
892
|
+
}
|
893
|
+
|
894
|
+
return id;
|
895
|
+
};
|
896
|
+
|
897
|
+
/**
|
898
|
+
* check if an id is an internal or external id
|
899
|
+
* @param id
|
900
|
+
* @returns {boolean}
|
901
|
+
* @private
|
902
|
+
*/
|
903
|
+
DataSet.prototype.isInternalId = function(id) {
|
904
|
+
return (id in this.internalIds);
|
905
|
+
};
|
906
|
+
|
907
|
+
|
908
|
+
/**
|
909
|
+
* Get an array with the column names of a Google DataTable
|
910
|
+
* @param {DataTable} dataTable
|
911
|
+
* @return {String[]} columnNames
|
912
|
+
* @private
|
913
|
+
*/
|
914
|
+
DataSet.prototype._getColumnNames = function (dataTable) {
|
915
|
+
var columns = [];
|
916
|
+
for (var col = 0, cols = dataTable.getNumberOfColumns(); col < cols; col++) {
|
917
|
+
columns[col] = dataTable.getColumnId(col) || dataTable.getColumnLabel(col);
|
918
|
+
}
|
919
|
+
return columns;
|
920
|
+
};
|
921
|
+
|
922
|
+
/**
|
923
|
+
* Append an item as a row to the dataTable
|
924
|
+
* @param dataTable
|
925
|
+
* @param columns
|
926
|
+
* @param item
|
927
|
+
* @private
|
928
|
+
*/
|
929
|
+
DataSet.prototype._appendRow = function (dataTable, columns, item) {
|
930
|
+
var row = dataTable.addRow();
|
931
|
+
|
932
|
+
for (var col = 0, cols = columns.length; col < cols; col++) {
|
933
|
+
var field = columns[col];
|
934
|
+
dataTable.setValue(row, col, item[field]);
|
935
|
+
}
|
936
|
+
};
|