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.

207 lines
6.7 KiB

  1. var Hammer = require('../../../module/hammer');
  2. var Item = require('./Item');
  3. var BackgroundGroup = require('../BackgroundGroup');
  4. var RangeItem = require('./RangeItem');
  5. /**
  6. * @constructor BackgroundItem
  7. * @extends Item
  8. * @param {Object} data Object containing parameters start, end
  9. * content, className.
  10. * @param {{toScreen: function, toTime: function}} conversion
  11. * Conversion functions from time to screen and vice versa
  12. * @param {Object} [options] Configuration options
  13. * // TODO: describe options
  14. */
  15. // TODO: implement support for the BackgroundItem just having a start, then being displayed as a sort of an annotation
  16. function BackgroundItem (data, conversion, options) {
  17. this.props = {
  18. content: {
  19. width: 0
  20. }
  21. };
  22. this.overflow = false; // if contents can overflow (css styling), this flag is set to true
  23. // validate data
  24. if (data) {
  25. if (data.start == undefined) {
  26. throw new Error('Property "start" missing in item ' + data.id);
  27. }
  28. if (data.end == undefined) {
  29. throw new Error('Property "end" missing in item ' + data.id);
  30. }
  31. }
  32. Item.call(this, data, conversion, options);
  33. this.emptyContent = false;
  34. }
  35. BackgroundItem.prototype = new Item (null, null, null);
  36. BackgroundItem.prototype.baseClassName = 'item background';
  37. BackgroundItem.prototype.stack = false;
  38. /**
  39. * Check whether this item is visible inside given range
  40. * @returns {{start: Number, end: Number}} range with a timestamp for start and end
  41. * @returns {boolean} True if visible
  42. */
  43. BackgroundItem.prototype.isVisible = function(range) {
  44. // determine visibility
  45. return (this.data.start < range.end) && (this.data.end > range.start);
  46. };
  47. /**
  48. * Repaint the item
  49. */
  50. BackgroundItem.prototype.redraw = function() {
  51. var dom = this.dom;
  52. if (!dom) {
  53. // create DOM
  54. this.dom = {};
  55. dom = this.dom;
  56. // background box
  57. dom.box = document.createElement('div');
  58. // className is updated in redraw()
  59. // contents box
  60. dom.content = document.createElement('div');
  61. dom.content.className = 'content';
  62. dom.box.appendChild(dom.content);
  63. // attach this item as attribute
  64. dom.box['timeline-item'] = this;
  65. this.dirty = true;
  66. }
  67. // append DOM to parent DOM
  68. if (!this.parent) {
  69. throw new Error('Cannot redraw item: no parent attached');
  70. }
  71. if (!dom.box.parentNode) {
  72. var background = this.parent.dom.background;
  73. if (!background) {
  74. throw new Error('Cannot redraw item: parent has no background container element');
  75. }
  76. background.appendChild(dom.box);
  77. }
  78. this.displayed = true;
  79. // Update DOM when item is marked dirty. An item is marked dirty when:
  80. // - the item is not yet rendered
  81. // - the item's data is changed
  82. // - the item is selected/deselected
  83. if (this.dirty) {
  84. this._updateContents(this.dom.content);
  85. this._updateTitle(this.dom.content);
  86. this._updateDataAttributes(this.dom.content);
  87. this._updateStyle(this.dom.box);
  88. // update class
  89. var className = (this.data.className ? (' ' + this.data.className) : '') +
  90. (this.selected ? ' selected' : '');
  91. dom.box.className = this.baseClassName + className;
  92. // determine from css whether this box has overflow
  93. this.overflow = window.getComputedStyle(dom.content).overflow !== 'hidden';
  94. // recalculate size
  95. this.props.content.width = this.dom.content.offsetWidth;
  96. this.height = 0; // set height zero, so this item will be ignored when stacking items
  97. this.dirty = false;
  98. }
  99. };
  100. /**
  101. * Show the item in the DOM (when not already visible). The items DOM will
  102. * be created when needed.
  103. */
  104. BackgroundItem.prototype.show = RangeItem.prototype.show;
  105. /**
  106. * Hide the item from the DOM (when visible)
  107. * @return {Boolean} changed
  108. */
  109. BackgroundItem.prototype.hide = RangeItem.prototype.hide;
  110. /**
  111. * Reposition the item horizontally
  112. * @Override
  113. */
  114. BackgroundItem.prototype.repositionX = RangeItem.prototype.repositionX;
  115. /**
  116. * Reposition the item vertically
  117. * @Override
  118. */
  119. BackgroundItem.prototype.repositionY = function(margin) {
  120. var onTop = this.options.orientation === 'top';
  121. this.dom.content.style.top = onTop ? '' : '0';
  122. this.dom.content.style.bottom = onTop ? '0' : '';
  123. var height;
  124. // special positioning for subgroups
  125. if (this.data.subgroup !== undefined) {
  126. var itemSubgroup = this.data.subgroup;
  127. var subgroups = this.parent.subgroups;
  128. var subgroupIndex = subgroups[itemSubgroup].index;
  129. // if the orientation is top, we need to take the difference in height into account.
  130. if (onTop == true) {
  131. // the first subgroup will have to account for the distance from the top to the first item.
  132. height = this.parent.subgroups[itemSubgroup].height + margin.item.vertical;
  133. height += subgroupIndex == 0 ? margin.axis - 0.5*margin.item.vertical : 0;
  134. var newTop = this.parent.top;
  135. for (var subgroup in subgroups) {
  136. if (subgroups.hasOwnProperty(subgroup)) {
  137. if (subgroups[subgroup].visible == true && subgroups[subgroup].index < subgroupIndex) {
  138. newTop += subgroups[subgroup].height + margin.item.vertical;
  139. }
  140. }
  141. }
  142. // the others will have to be offset downwards with this same distance.
  143. newTop += subgroupIndex != 0 ? margin.axis - 0.5 * margin.item.vertical : 0;
  144. this.dom.box.style.top = newTop + 'px';
  145. this.dom.box.style.bottom = '';
  146. }
  147. // and when the orientation is bottom:
  148. else {
  149. var newTop = this.parent.top;
  150. for (var subgroup in subgroups) {
  151. if (subgroups.hasOwnProperty(subgroup)) {
  152. if (subgroups[subgroup].visible == true && subgroups[subgroup].index > subgroupIndex) {
  153. newTop += subgroups[subgroup].height + margin.item.vertical;
  154. }
  155. }
  156. }
  157. height = this.parent.subgroups[itemSubgroup].height + margin.item.vertical;
  158. this.dom.box.style.top = newTop + 'px';
  159. this.dom.box.style.bottom = '';
  160. }
  161. }
  162. // and in the case of no subgroups:
  163. else {
  164. // we want backgrounds with groups to only show in groups.
  165. if (this.parent instanceof BackgroundGroup) {
  166. // if the item is not in a group:
  167. height = Math.max(this.parent.height,
  168. this.parent.itemSet.body.domProps.center.height,
  169. this.parent.itemSet.body.domProps.centerContainer.height);
  170. this.dom.box.style.top = onTop ? '0' : '';
  171. this.dom.box.style.bottom = onTop ? '' : '0';
  172. }
  173. else {
  174. height = this.parent.height;
  175. // same alignment for items when orientation is top or bottom
  176. this.dom.box.style.top = this.parent.top + 'px';
  177. this.dom.box.style.bottom = '';
  178. }
  179. }
  180. this.dom.box.style.height = height + 'px';
  181. };
  182. module.exports = BackgroundItem;