PointItem.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. var Item = require('./Item');
  2. /**
  3. * @constructor PointItem
  4. * @extends Item
  5. * @param {Object} data Object containing parameters start
  6. * content, className.
  7. * @param {{toScreen: function, toTime: function}} conversion
  8. * Conversion functions from time to screen and vice versa
  9. * @param {Object} [options] Configuration options
  10. * // TODO: describe available options
  11. */
  12. function PointItem (data, conversion, options) {
  13. this.props = {
  14. dot: {
  15. top: 0,
  16. width: 0,
  17. height: 0
  18. },
  19. content: {
  20. height: 0,
  21. marginLeft: 0,
  22. marginRight: 0
  23. }
  24. };
  25. this.options = options;
  26. // validate data
  27. if (data) {
  28. if (data.start == undefined) {
  29. throw new Error('Property "start" missing in item ' + data);
  30. }
  31. }
  32. Item.call(this, data, conversion, options);
  33. }
  34. PointItem.prototype = new Item (null, null, null);
  35. /**
  36. * Check whether this item is visible inside given range
  37. * @param {{start: number, end: number}} range with a timestamp for start and end
  38. * @returns {boolean} True if visible
  39. */
  40. PointItem.prototype.isVisible = function(range) {
  41. // determine visibility
  42. var widthInMs = this.width * range.getMillisecondsPerPixel();
  43. return (this.data.start.getTime() + widthInMs > range.start ) && (this.data.start < range.end);
  44. };
  45. PointItem.prototype._createDomElement = function() {
  46. if (!this.dom) {
  47. // create DOM
  48. this.dom = {};
  49. // background box
  50. this.dom.point = document.createElement('div');
  51. // className is updated in redraw()
  52. // contents box, right from the dot
  53. this.dom.content = document.createElement('div');
  54. this.dom.content.className = 'vis-item-content';
  55. this.dom.point.appendChild(this.dom.content);
  56. // dot at start
  57. this.dom.dot = document.createElement('div');
  58. this.dom.point.appendChild(this.dom.dot);
  59. // attach this item as attribute
  60. this.dom.point['timeline-item'] = this;
  61. this.dirty = true;
  62. }
  63. }
  64. PointItem.prototype._appendDomElement = function() {
  65. if (!this.parent) {
  66. throw new Error('Cannot redraw item: no parent attached');
  67. }
  68. if (!this.dom.point.parentNode) {
  69. var foreground = this.parent.dom.foreground;
  70. if (!foreground) {
  71. throw new Error('Cannot redraw item: parent has no foreground container element');
  72. }
  73. foreground.appendChild(this.dom.point);
  74. }
  75. this.displayed = true;
  76. }
  77. PointItem.prototype._updateDirtyDomComponents = function() {
  78. // An item is marked dirty when:
  79. // - the item is not yet rendered
  80. // - the item's data is changed
  81. // - the item is selected/deselected
  82. if (this.dirty) {
  83. this._updateContents(this.dom.content);
  84. this._updateDataAttributes(this.dom.point);
  85. this._updateStyle(this.dom.point);
  86. var editable = (this.editable.updateTime || this.editable.updateGroup);
  87. // update class
  88. var className = (this.data.className ? ' ' + this.data.className : '') +
  89. (this.selected ? ' vis-selected' : '') +
  90. (editable ? ' vis-editable' : ' vis-readonly');
  91. this.dom.point.className = 'vis-item vis-point' + className;
  92. this.dom.dot.className = 'vis-item vis-dot' + className;
  93. }
  94. }
  95. PointItem.prototype._getDomComponentsSizes = function() {
  96. return {
  97. dot: {
  98. width: this.dom.dot.offsetWidth,
  99. height: this.dom.dot.offsetHeight
  100. },
  101. content: {
  102. width: this.dom.content.offsetWidth,
  103. height: this.dom.content.offsetHeight
  104. },
  105. point: {
  106. width: this.dom.point.offsetWidth,
  107. height: this.dom.point.offsetHeight
  108. }
  109. }
  110. }
  111. PointItem.prototype._updateDomComponentsSizes = function(sizes) {
  112. // recalculate size of dot and contents
  113. this.props.dot.width = sizes.dot.width;
  114. this.props.dot.height = sizes.dot.height;
  115. this.props.content.height = sizes.content.height;
  116. // resize contents
  117. if (this.options.rtl) {
  118. this.dom.content.style.marginRight = 2 * this.props.dot.width + 'px';
  119. } else {
  120. this.dom.content.style.marginLeft = 2 * this.props.dot.width + 'px';
  121. }
  122. //this.dom.content.style.marginRight = ... + 'px'; // TODO: margin right
  123. // recalculate size
  124. this.width = sizes.point.width;
  125. this.height = sizes.point.height;
  126. // reposition the dot
  127. this.dom.dot.style.top = ((this.height - this.props.dot.height) / 2) + 'px';
  128. if (this.options.rtl) {
  129. this.dom.dot.style.right = (this.props.dot.width / 2) + 'px';
  130. } else {
  131. this.dom.dot.style.left = (this.props.dot.width / 2) + 'px';
  132. }
  133. this.dirty = false;
  134. }
  135. PointItem.prototype._repaintDomAdditionals = function() {
  136. this._repaintOnItemUpdateTimeTooltip(this.dom.point);
  137. this._repaintDragCenter();
  138. this._repaintDeleteButton(this.dom.point);
  139. }
  140. /**
  141. * Repaint the item
  142. * @param {boolean} [returnQueue=false] return the queue
  143. * @return {boolean} the redraw queue if returnQueue=true
  144. */
  145. PointItem.prototype.redraw = function(returnQueue) {
  146. var sizes
  147. var queue = [
  148. // create item DOM
  149. this._createDomElement.bind(this),
  150. // append DOM to parent DOM
  151. this._appendDomElement.bind(this),
  152. // update dirty DOM
  153. this._updateDirtyDomComponents.bind(this),
  154. (function() {
  155. if (this.dirty) {
  156. sizes = this._getDomComponentsSizes();
  157. }
  158. }).bind(this),
  159. (function() {
  160. if (this.dirty) {
  161. this._updateDomComponentsSizes.bind(this)(sizes);
  162. }
  163. }).bind(this),
  164. // repaint DOM additionals
  165. this._repaintDomAdditionals.bind(this)
  166. ];
  167. if (returnQueue) {
  168. return queue;
  169. } else {
  170. var result;
  171. queue.forEach(function (fn) {
  172. result = fn();
  173. });
  174. return result;
  175. }
  176. };
  177. /**
  178. * Show the item in the DOM (when not already visible). The items DOM will
  179. * be created when needed.
  180. */
  181. PointItem.prototype.show = function() {
  182. if (!this.displayed) {
  183. this.redraw();
  184. }
  185. };
  186. /**
  187. * Hide the item from the DOM (when visible)
  188. */
  189. PointItem.prototype.hide = function() {
  190. if (this.displayed) {
  191. if (this.dom.point.parentNode) {
  192. this.dom.point.parentNode.removeChild(this.dom.point);
  193. }
  194. this.displayed = false;
  195. }
  196. };
  197. /**
  198. * Reposition the item horizontally
  199. * @Override
  200. */
  201. PointItem.prototype.repositionX = function() {
  202. var start = this.conversion.toScreen(this.data.start);
  203. if (this.options.rtl) {
  204. this.right = start - this.props.dot.width;
  205. // reposition point
  206. this.dom.point.style.right = this.right + 'px';
  207. } else {
  208. this.left = start - this.props.dot.width;
  209. // reposition point
  210. this.dom.point.style.left = this.left + 'px';
  211. }
  212. };
  213. /**
  214. * Reposition the item vertically
  215. * @Override
  216. */
  217. PointItem.prototype.repositionY = function() {
  218. var orientation = this.options.orientation.item;
  219. var point = this.dom.point;
  220. if (orientation == 'top') {
  221. point.style.top = this.top + 'px';
  222. }
  223. else {
  224. point.style.top = (this.parent.height - this.top - this.height) + 'px';
  225. }
  226. };
  227. /**
  228. * Return the width of the item left from its start date
  229. * @return {number}
  230. */
  231. PointItem.prototype.getWidthLeft = function () {
  232. return this.props.dot.width;
  233. };
  234. /**
  235. * Return the width of the item right from its start date
  236. * @return {number}
  237. */
  238. PointItem.prototype.getWidthRight = function () {
  239. return this.props.dot.width;
  240. };
  241. module.exports = PointItem;