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.

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