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.

282 lines
8.5 KiB

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