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.

301 lines
8.5 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 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 LineGraph = require('./component/LineGraph');
  12. /**
  13. * Create a timeline visualization
  14. * @param {HTMLElement} container
  15. * @param {vis.DataSet | Array} [items]
  16. * @param {Object} [options] See Graph2d.setOptions for the available options.
  17. * @constructor
  18. * @extends Core
  19. */
  20. function Graph2d (container, items, groups, options) {
  21. // if the third element is options, the forth is groups (optionally);
  22. if (!(Array.isArray(groups) || groups instanceof DataSet) && groups instanceof Object) {
  23. var forthArgument = options;
  24. options = groups;
  25. groups = forthArgument;
  26. }
  27. var me = this;
  28. this.defaultOptions = {
  29. start: null,
  30. end: null,
  31. autoResize: true,
  32. orientation: {
  33. axis: 'bottom', // axis orientation: 'bottom', 'top', or 'both'
  34. item: 'bottom' // not relevant for Graph2d
  35. },
  36. width: null,
  37. height: null,
  38. maxHeight: null,
  39. minHeight: null
  40. };
  41. this.options = util.deepExtend({}, this.defaultOptions);
  42. // Create the DOM, props, and emitter
  43. this._create(container);
  44. // all components listed here will be repainted automatically
  45. this.components = [];
  46. this.body = {
  47. dom: this.dom,
  48. domProps: this.props,
  49. emitter: {
  50. on: this.on.bind(this),
  51. off: this.off.bind(this),
  52. emit: this.emit.bind(this)
  53. },
  54. hiddenDates: [],
  55. util: {
  56. toScreen: me._toScreen.bind(me),
  57. toGlobalScreen: me._toGlobalScreen.bind(me), // this refers to the root.width
  58. toTime: me._toTime.bind(me),
  59. toGlobalTime : me._toGlobalTime.bind(me)
  60. }
  61. };
  62. // range
  63. this.range = new Range(this.body);
  64. this.components.push(this.range);
  65. this.body.range = this.range;
  66. // time axis
  67. this.timeAxis = new TimeAxis(this.body);
  68. this.components.push(this.timeAxis);
  69. //this.body.util.snap = this.timeAxis.snap.bind(this.timeAxis);
  70. // current time bar
  71. this.currentTime = new CurrentTime(this.body);
  72. this.components.push(this.currentTime);
  73. // item set
  74. this.linegraph = new LineGraph(this.body);
  75. this.components.push(this.linegraph);
  76. this.itemsData = null; // DataSet
  77. this.groupsData = null; // DataSet
  78. this.on('tap', function (event) {
  79. me.emit('click', me.getEventProperties(event))
  80. });
  81. this.on('doubletap', function (event) {
  82. me.emit('doubleClick', me.getEventProperties(event))
  83. });
  84. this.dom.root.oncontextmenu = function (event) {
  85. me.emit('contextmenu', me.getEventProperties(event))
  86. };
  87. // apply options
  88. if (options) {
  89. this.setOptions(options);
  90. }
  91. // IMPORTANT: THIS HAPPENS BEFORE SET ITEMS!
  92. if (groups) {
  93. this.setGroups(groups);
  94. }
  95. // create itemset
  96. if (items) {
  97. this.setItems(items);
  98. }
  99. else {
  100. this._redraw();
  101. }
  102. }
  103. // Extend the functionality from Core
  104. Graph2d.prototype = new Core();
  105. /**
  106. * Set items
  107. * @param {vis.DataSet | Array | null} items
  108. */
  109. Graph2d.prototype.setItems = function(items) {
  110. var initialLoad = (this.itemsData == null);
  111. // convert to type DataSet when needed
  112. var newDataSet;
  113. if (!items) {
  114. newDataSet = null;
  115. }
  116. else if (items instanceof DataSet || items instanceof DataView) {
  117. newDataSet = items;
  118. }
  119. else {
  120. // turn an array into a dataset
  121. newDataSet = new DataSet(items, {
  122. type: {
  123. start: 'Date',
  124. end: 'Date'
  125. }
  126. });
  127. }
  128. // set items
  129. this.itemsData = newDataSet;
  130. this.linegraph && this.linegraph.setItems(newDataSet);
  131. if (initialLoad) {
  132. if (this.options.start != undefined || this.options.end != undefined) {
  133. var start = this.options.start != undefined ? this.options.start : null;
  134. var end = this.options.end != undefined ? this.options.end : null;
  135. this.setWindow(start, end, {animation: false});
  136. }
  137. else {
  138. this.fit({animation: false});
  139. }
  140. }
  141. };
  142. /**
  143. * Set groups
  144. * @param {vis.DataSet | Array} groups
  145. */
  146. Graph2d.prototype.setGroups = function(groups) {
  147. // convert to type DataSet when needed
  148. var newDataSet;
  149. if (!groups) {
  150. newDataSet = null;
  151. }
  152. else if (groups instanceof DataSet || groups instanceof DataView) {
  153. newDataSet = groups;
  154. }
  155. else {
  156. // turn an array into a dataset
  157. newDataSet = new DataSet(groups);
  158. }
  159. this.groupsData = newDataSet;
  160. this.linegraph.setGroups(newDataSet);
  161. };
  162. /**
  163. * Returns an object containing an SVG element with the icon of the group (size determined by iconWidth and iconHeight), the label of the group (content) and the yAxisOrientation of the group (left or right).
  164. * @param groupId
  165. * @param width
  166. * @param height
  167. */
  168. Graph2d.prototype.getLegend = function(groupId, width, height) {
  169. if (width === undefined) {width = 15;}
  170. if (height === undefined) {height = 15;}
  171. if (this.linegraph.groups[groupId] !== undefined) {
  172. return this.linegraph.groups[groupId].getLegend(width,height);
  173. }
  174. else {
  175. return "cannot find group:" + groupId;
  176. }
  177. };
  178. /**
  179. * This checks if the visible option of the supplied group (by ID) is true or false.
  180. * @param groupId
  181. * @returns {*}
  182. */
  183. Graph2d.prototype.isGroupVisible = function(groupId) {
  184. if (this.linegraph.groups[groupId] !== undefined) {
  185. return (this.linegraph.groups[groupId].visible && (this.linegraph.options.groups.visibility[groupId] === undefined || this.linegraph.options.groups.visibility[groupId] == true));
  186. }
  187. else {
  188. return false;
  189. }
  190. };
  191. /**
  192. * Get the data range of the item set.
  193. * @returns {{min: Date, max: Date}} range A range with a start and end Date.
  194. * When no minimum is found, min==null
  195. * When no maximum is found, max==null
  196. */
  197. Graph2d.prototype.getItemRange = function() {
  198. var min = null;
  199. var max = null;
  200. // calculate min from start filed
  201. for (var groupId in this.linegraph.groups) {
  202. if (this.linegraph.groups.hasOwnProperty(groupId)) {
  203. if (this.linegraph.groups[groupId].visible == true) {
  204. for (var i = 0; i < this.linegraph.groups[groupId].itemsData.length; i++) {
  205. var item = this.linegraph.groups[groupId].itemsData[i];
  206. var value = util.convert(item.x, 'Date').valueOf();
  207. min = min == null ? value : min > value ? value : min;
  208. max = max == null ? value : max < value ? value : max;
  209. }
  210. }
  211. }
  212. }
  213. return {
  214. min: (min != null) ? new Date(min) : null,
  215. max: (max != null) ? new Date(max) : null
  216. };
  217. };
  218. /**
  219. * Generate Timeline related information from an event
  220. * @param {Event} event
  221. * @return {Object} An object with related information, like on which area
  222. * The event happened, whether clicked on an item, etc.
  223. */
  224. Graph2d.prototype.getEventProperties = function (event) {
  225. var pageX = event.center ? event.center.x : event.pageX;
  226. var pageY = event.center ? event.center.y : event.pageY;
  227. var x = pageX - util.getAbsoluteLeft(this.dom.centerContainer);
  228. var y = pageY - util.getAbsoluteTop(this.dom.centerContainer);
  229. var time = this._toTime(x);
  230. var customTime = CustomTime.customTimeFromTarget(event);
  231. var element = util.getTarget(event);
  232. var what = null;
  233. if (util.hasParent(element, this.timeAxis.dom.foreground)) {what = 'axis';}
  234. else if (this.timeAxis2 && util.hasParent(element, this.timeAxis2.dom.foreground)) {what = 'axis';}
  235. else if (util.hasParent(element, this.linegraph.yAxisLeft.dom.frame)) {what = 'data-axis';}
  236. else if (util.hasParent(element, this.linegraph.yAxisRight.dom.frame)) {what = 'data-axis';}
  237. else if (util.hasParent(element, this.linegraph.legendLeft.dom.frame)) {what = 'legend';}
  238. else if (util.hasParent(element, this.linegraph.legendRight.dom.frame)) {what = 'legend';}
  239. else if (customTime != null) {what = 'custom-time';}
  240. else if (util.hasParent(element, this.currentTime.bar)) {what = 'current-time';}
  241. else if (util.hasParent(element, this.dom.center)) {what = 'background';}
  242. var value = [];
  243. var yAxisLeft = this.linegraph.yAxisLeft;
  244. var yAxisRight = this.linegraph.yAxisRight;
  245. if (!yAxisLeft.hidden) {
  246. value.push(yAxisLeft.screenToValue(y));
  247. }
  248. if (!yAxisRight.hidden) {
  249. value.push(yAxisRight.screenToValue(y));
  250. }
  251. return {
  252. event: event,
  253. what: what,
  254. pageX: pageX,
  255. pageY: pageY,
  256. x: x,
  257. y: y,
  258. time: time,
  259. value: value
  260. }
  261. };
  262. module.exports = Graph2d;