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.

279 lines
8.3 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
  1. var Emitter = require('emitter-component');
  2. var Hammer = require('../module/hammer');
  3. var util = require('../util');
  4. var DataSet = require('../DataSet');
  5. var DataView = require('../DataView');
  6. var Range = require('./Range');
  7. var Core = require('./Core');
  8. var TimeAxis = require('./component/TimeAxis');
  9. var CurrentTime = require('./component/CurrentTime');
  10. var CustomTime = require('./component/CustomTime');
  11. var ItemSet = require('./component/ItemSet');
  12. /**
  13. * Create a timeline visualization
  14. * @param {HTMLElement} container
  15. * @param {vis.DataSet | Array | google.visualization.DataTable} [items]
  16. * @param {Object} [options] See Timeline.setOptions for the available options.
  17. * @constructor
  18. * @extends Core
  19. */
  20. function Timeline (container, items, options) {
  21. if (!(this instanceof Timeline)) {
  22. throw new SyntaxError('Constructor must be called with the new operator');
  23. }
  24. var me = this;
  25. this.defaultOptions = {
  26. start: null,
  27. end: null,
  28. autoResize: true,
  29. orientation: 'bottom',
  30. width: null,
  31. height: null,
  32. maxHeight: null,
  33. minHeight: null
  34. };
  35. this.options = util.deepExtend({}, this.defaultOptions);
  36. // Create the DOM, props, and emitter
  37. this._create(container);
  38. // all components listed here will be repainted automatically
  39. this.components = [];
  40. this.body = {
  41. dom: this.dom,
  42. domProps: this.props,
  43. emitter: {
  44. on: this.on.bind(this),
  45. off: this.off.bind(this),
  46. emit: this.emit.bind(this)
  47. },
  48. util: {
  49. snap: null, // will be specified after TimeAxis is created
  50. toScreen: me._toScreen.bind(me),
  51. toGlobalScreen: me._toGlobalScreen.bind(me), // this refers to the root.width
  52. toTime: me._toTime.bind(me),
  53. toGlobalTime : me._toGlobalTime.bind(me)
  54. }
  55. };
  56. // range
  57. this.range = new Range(this.body);
  58. this.components.push(this.range);
  59. this.body.range = this.range;
  60. // time axis
  61. this.timeAxis = new TimeAxis(this.body);
  62. this.components.push(this.timeAxis);
  63. this.body.util.snap = this.timeAxis.snap.bind(this.timeAxis);
  64. // current time bar
  65. this.currentTime = new CurrentTime(this.body);
  66. this.components.push(this.currentTime);
  67. // custom time bar
  68. // Note: time bar will be attached in this.setOptions when selected
  69. this.customTime = new CustomTime(this.body);
  70. this.components.push(this.customTime);
  71. // item set
  72. this.itemSet = new ItemSet(this.body);
  73. this.components.push(this.itemSet);
  74. this.itemsData = null; // DataSet
  75. this.groupsData = null; // DataSet
  76. // apply options
  77. if (options) {
  78. this.setOptions(options);
  79. }
  80. // create itemset
  81. if (items) {
  82. this.setItems(items);
  83. }
  84. else {
  85. this.redraw();
  86. }
  87. }
  88. // Extend the functionality from Core
  89. Timeline.prototype = new Core();
  90. /**
  91. * Set options. Options will be passed to all components loaded in the Timeline.
  92. * @param {Object} [options]
  93. * {String} orientation
  94. * Vertical orientation for the Timeline,
  95. * can be 'bottom' (default) or 'top'.
  96. * {String | Number} width
  97. * Width for the timeline, a number in pixels or
  98. * a css string like '1000px' or '75%'. '100%' by default.
  99. * {String | Number} height
  100. * Fixed height for the Timeline, a number in pixels or
  101. * a css string like '400px' or '75%'. If undefined,
  102. * The Timeline will automatically size such that
  103. * its contents fit.
  104. * {String | Number} minHeight
  105. * Minimum height for the Timeline, a number in pixels or
  106. * a css string like '400px' or '75%'.
  107. * {String | Number} maxHeight
  108. * Maximum height for the Timeline, a number in pixels or
  109. * a css string like '400px' or '75%'.
  110. * {Number | Date | String} start
  111. * Start date for the visible window
  112. * {Number | Date | String} end
  113. * End date for the visible window
  114. */
  115. Timeline.prototype.setOptions = function (options) {
  116. if (options) {
  117. // copy the known options
  118. var fields = ['width', 'height', 'minHeight', 'maxHeight', 'autoResize', 'start', 'end', 'orientation', 'activatable'];
  119. util.selectiveExtend(fields, this.options, options);
  120. // enable/disable autoResize
  121. this._initAutoResize();
  122. }
  123. // propagate options to all components
  124. this.components.forEach(function (component) {
  125. component.setOptions(options);
  126. });
  127. // TODO: remove deprecation error one day (deprecated since version 0.8.0)
  128. if (options && options.order) {
  129. throw new Error('Option order is deprecated. There is no replacement for this feature.');
  130. }
  131. // redraw everything
  132. this.redraw();
  133. };
  134. /**
  135. * Set items
  136. * @param {vis.DataSet | Array | google.visualization.DataTable | null} items
  137. */
  138. Timeline.prototype.setItems = function(items) {
  139. var initialLoad = (this.itemsData == null);
  140. // convert to type DataSet when needed
  141. var newDataSet;
  142. if (!items) {
  143. newDataSet = null;
  144. }
  145. else if (items instanceof DataSet || items instanceof DataView) {
  146. newDataSet = items;
  147. }
  148. else {
  149. // turn an array into a dataset
  150. newDataSet = new DataSet(items, {
  151. type: {
  152. start: 'Date',
  153. end: 'Date'
  154. }
  155. });
  156. }
  157. // set items
  158. this.itemsData = newDataSet;
  159. this.itemSet && this.itemSet.setItems(newDataSet);
  160. if (initialLoad && ('start' in this.options || 'end' in this.options)) {
  161. this.fit();
  162. var start = ('start' in this.options) ? util.convert(this.options.start, 'Date') : null;
  163. var end = ('end' in this.options) ? util.convert(this.options.end, 'Date') : null;
  164. this.setWindow(start, end);
  165. }
  166. };
  167. /**
  168. * Set groups
  169. * @param {vis.DataSet | Array | google.visualization.DataTable} groups
  170. */
  171. Timeline.prototype.setGroups = function(groups) {
  172. // convert to type DataSet when needed
  173. var newDataSet;
  174. if (!groups) {
  175. newDataSet = null;
  176. }
  177. else if (groups instanceof DataSet || groups instanceof DataView) {
  178. newDataSet = groups;
  179. }
  180. else {
  181. // turn an array into a dataset
  182. newDataSet = new DataSet(groups);
  183. }
  184. this.groupsData = newDataSet;
  185. this.itemSet.setGroups(newDataSet);
  186. };
  187. /**
  188. * Set selected items by their id. Replaces the current selection
  189. * Unknown id's are silently ignored.
  190. * @param {Array} [ids] An array with zero or more id's of the items to be
  191. * selected. If ids is an empty array, all items will be
  192. * unselected.
  193. */
  194. Timeline.prototype.setSelection = function(ids) {
  195. this.itemSet && this.itemSet.setSelection(ids);
  196. };
  197. /**
  198. * Get the selected items by their id
  199. * @return {Array} ids The ids of the selected items
  200. */
  201. Timeline.prototype.getSelection = function() {
  202. return this.itemSet && this.itemSet.getSelection() || [];
  203. };
  204. /**
  205. * Get the data range of the item set.
  206. * @returns {{min: Date, max: Date}} range A range with a start and end Date.
  207. * When no minimum is found, min==null
  208. * When no maximum is found, max==null
  209. */
  210. Timeline.prototype.getItemRange = function() {
  211. // calculate min from start filed
  212. var dataset = this.itemsData.getDataSet(),
  213. min = null,
  214. max = null;
  215. if (dataset) {
  216. // calculate the minimum value of the field 'start'
  217. var minItem = dataset.min('start');
  218. min = minItem ? util.convert(minItem.start, 'Date').valueOf() : null;
  219. // Note: we convert first to Date and then to number because else
  220. // a conversion from ISODate to Number will fail
  221. // calculate maximum value of fields 'start' and 'end'
  222. var maxStartItem = dataset.max('start');
  223. if (maxStartItem) {
  224. max = util.convert(maxStartItem.start, 'Date').valueOf();
  225. }
  226. var maxEndItem = dataset.max('end');
  227. if (maxEndItem) {
  228. if (max == null) {
  229. max = util.convert(maxEndItem.end, 'Date').valueOf();
  230. }
  231. else {
  232. max = Math.max(max, util.convert(maxEndItem.end, 'Date').valueOf());
  233. }
  234. }
  235. }
  236. return {
  237. min: (min != null) ? new Date(min) : null,
  238. max: (max != null) ? new Date(max) : null
  239. };
  240. };
  241. module.exports = Timeline;