vis.js is a dynamic, browser-based visualization library
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

957 lines
25 KiB

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