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.

270 lines
6.7 KiB

  1. var Hammer = require('../../module/hammer');
  2. var util = require('../../util');
  3. var Component = require('./Component');
  4. var moment = require('../../module/moment');
  5. var locales = require('../locales');
  6. /**
  7. * A custom time bar
  8. * @param {{range: Range, dom: Object}} body
  9. * @param {Object} [options] Available parameters:
  10. * {number | string} id
  11. * {string} locales
  12. * {string} locale
  13. * @constructor CustomTime
  14. * @extends Component
  15. */
  16. function CustomTime (body, options) {
  17. this.body = body;
  18. // default options
  19. this.defaultOptions = {
  20. moment: moment,
  21. locales: locales,
  22. locale: 'en',
  23. id: undefined,
  24. title: undefined,
  25. editable: true
  26. };
  27. this.options = util.extend({}, this.defaultOptions);
  28. if (options && options.time) {
  29. this.customTime = options.time;
  30. } else {
  31. this.customTime = new Date();
  32. }
  33. this.eventParams = {}; // stores state parameters while dragging the bar
  34. this.setOptions(options);
  35. // create the DOM
  36. this._create();
  37. }
  38. CustomTime.prototype = new Component();
  39. /**
  40. * Set options for the component. Options will be merged in current options.
  41. * @param {Object} options Available parameters:
  42. * {number | string} id
  43. * {string} locales
  44. * {string} locale
  45. */
  46. CustomTime.prototype.setOptions = function(options) {
  47. if (options) {
  48. // copy all options that we know
  49. util.selectiveExtend(['moment', 'locale', 'locales', 'id', 'editable'], this.options, options);
  50. }
  51. };
  52. /**
  53. * Create the DOM for the custom time
  54. * @private
  55. */
  56. CustomTime.prototype._create = function() {
  57. var bar = document.createElement('div');
  58. bar['custom-time'] = this;
  59. bar.className = 'vis-custom-time ' + (!this.options.editable ? 'disabled ' : '') + (this.options.id || '');
  60. bar.style.position = 'absolute';
  61. bar.style.top = '0px';
  62. bar.style.height = '100%';
  63. this.bar = bar;
  64. var drag = document.createElement('div');
  65. drag.style.position = 'relative';
  66. drag.style.top = '0px';
  67. drag.style.left = '-10px';
  68. drag.style.height = '100%';
  69. drag.style.width = '20px';
  70. /**
  71. *
  72. * @param {WheelEvent} e
  73. */
  74. function onMouseWheel (e) {
  75. this.body.range._onMouseWheel(e);
  76. }
  77. if (drag.addEventListener) {
  78. // IE9, Chrome, Safari, Opera
  79. drag.addEventListener("mousewheel", onMouseWheel.bind(this), false);
  80. // Firefox
  81. drag.addEventListener("DOMMouseScroll", onMouseWheel.bind(this), false);
  82. } else {
  83. // IE 6/7/8
  84. drag.attachEvent("onmousewheel", onMouseWheel.bind(this));
  85. }
  86. bar.appendChild(drag);
  87. // if bar is editable by the user, attach drag handlers
  88. if(this.options.editable){
  89. // attach event listeners
  90. this.hammer = new Hammer(drag);
  91. this.hammer.on('panstart', this._onDragStart.bind(this));
  92. this.hammer.on('panmove', this._onDrag.bind(this));
  93. this.hammer.on('panend', this._onDragEnd.bind(this));
  94. this.hammer.get('pan').set({ threshold: 5, direction: Hammer.DIRECTION_HORIZONTAL });
  95. }
  96. };
  97. /**
  98. * Destroy the CustomTime bar
  99. */
  100. CustomTime.prototype.destroy = function () {
  101. this.hide();
  102. this.hammer.destroy();
  103. this.hammer = null;
  104. this.body = null;
  105. };
  106. /**
  107. * Repaint the component
  108. * @return {boolean} Returns true if the component is resized
  109. */
  110. CustomTime.prototype.redraw = function () {
  111. var parent = this.body.dom.backgroundVertical;
  112. if (this.bar.parentNode != parent) {
  113. // attach to the dom
  114. if (this.bar.parentNode) {
  115. this.bar.parentNode.removeChild(this.bar);
  116. }
  117. parent.appendChild(this.bar);
  118. }
  119. var x = this.body.util.toScreen(this.customTime);
  120. var locale = this.options.locales[this.options.locale];
  121. if (!locale) {
  122. if (!this.warned) {
  123. console.log('WARNING: options.locales[\'' + this.options.locale + '\'] not found. See http://visjs.org/docs/timeline/#Localization');
  124. this.warned = true;
  125. }
  126. locale = this.options.locales['en']; // fall back on english when not available
  127. }
  128. var title = this.options.title;
  129. // To hide the title completely use empty string ''.
  130. if (title === undefined) {
  131. title = locale.time + ': ' + this.options.moment(this.customTime).format('dddd, MMMM Do YYYY, H:mm:ss');
  132. title = title.charAt(0).toUpperCase() + title.substring(1);
  133. } else if (typeof title === "function") {
  134. title = title.call(this.customTime);
  135. }
  136. this.bar.style.left = x + 'px';
  137. this.bar.title = title;
  138. return false;
  139. };
  140. /**
  141. * Remove the CustomTime from the DOM
  142. */
  143. CustomTime.prototype.hide = function () {
  144. // remove the line from the DOM
  145. if (this.bar.parentNode) {
  146. this.bar.parentNode.removeChild(this.bar);
  147. }
  148. };
  149. /**
  150. * Set custom time.
  151. * @param {Date | number | string} time
  152. */
  153. CustomTime.prototype.setCustomTime = function(time) {
  154. this.customTime = util.convert(time, 'Date');
  155. this.redraw();
  156. };
  157. /**
  158. * Retrieve the current custom time.
  159. * @return {Date} customTime
  160. */
  161. CustomTime.prototype.getCustomTime = function() {
  162. return new Date(this.customTime.valueOf());
  163. };
  164. /**
  165. * Set custom title.
  166. * @param {Date | number | string} title
  167. */
  168. CustomTime.prototype.setCustomTitle = function(title) {
  169. this.options.title = title;
  170. };
  171. /**
  172. * Start moving horizontally
  173. * @param {Event} event
  174. * @private
  175. */
  176. CustomTime.prototype._onDragStart = function(event) {
  177. this.eventParams.dragging = true;
  178. this.eventParams.customTime = this.customTime;
  179. event.stopPropagation();
  180. };
  181. /**
  182. * Perform moving operating.
  183. * @param {Event} event
  184. * @private
  185. */
  186. CustomTime.prototype._onDrag = function (event) {
  187. if (!this.eventParams.dragging) return;
  188. var x = this.body.util.toScreen(this.eventParams.customTime) + event.deltaX;
  189. var time = this.body.util.toTime(x);
  190. this.setCustomTime(time);
  191. // fire a timechange event
  192. this.body.emitter.emit('timechange', {
  193. id: this.options.id,
  194. time: new Date(this.customTime.valueOf()),
  195. event: event
  196. });
  197. event.stopPropagation();
  198. };
  199. /**
  200. * Stop moving operating.
  201. * @param {Event} event
  202. * @private
  203. */
  204. CustomTime.prototype._onDragEnd = function (event) {
  205. if (!this.eventParams.dragging) return;
  206. // fire a timechanged event
  207. this.body.emitter.emit('timechanged', {
  208. id: this.options.id,
  209. time: new Date(this.customTime.valueOf()),
  210. event: event
  211. });
  212. event.stopPropagation();
  213. };
  214. /**
  215. * Find a custom time from an event target:
  216. * searches for the attribute 'custom-time' in the event target's element tree
  217. * @param {Event} event
  218. * @return {CustomTime | null} customTime
  219. */
  220. CustomTime.customTimeFromTarget = function(event) {
  221. var target = event.target;
  222. while (target) {
  223. if (target.hasOwnProperty('custom-time')) {
  224. return target['custom-time'];
  225. }
  226. target = target.parentNode;
  227. }
  228. return null;
  229. };
  230. module.exports = CustomTime;