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.

263 lines
6.8 KiB

12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
  1. /**
  2. * @constructor ItemRange
  3. * @extends Item
  4. * @param {Object} data Object containing parameters start, end
  5. * content, className.
  6. * @param {Object} [options] Options to set initial property values
  7. * @param {Object} [defaultOptions] default options
  8. * // TODO: describe available options
  9. */
  10. function ItemRange (data, options, defaultOptions) {
  11. this.props = {
  12. content: {
  13. width: 0
  14. }
  15. };
  16. // validate data
  17. if (data) {
  18. if (data.start == undefined) {
  19. throw new Error('Property "start" missing in item ' + data.id);
  20. }
  21. if (data.end == undefined) {
  22. throw new Error('Property "end" missing in item ' + data.id);
  23. }
  24. }
  25. Item.call(this, data, options, defaultOptions);
  26. }
  27. ItemRange.prototype = new Item (null);
  28. ItemRange.prototype.baseClassName = 'item range';
  29. /**
  30. * Check whether this item is visible inside given range
  31. * @returns {{start: Number, end: Number}} range with a timestamp for start and end
  32. * @returns {boolean} True if visible
  33. */
  34. ItemRange.prototype.isVisible = function isVisible (range) {
  35. // determine visibility
  36. return (this.data.start < range.end) && (this.data.end > range.start);
  37. };
  38. /**
  39. * Repaint the item
  40. */
  41. ItemRange.prototype.repaint = function repaint() {
  42. var dom = this.dom;
  43. if (!dom) {
  44. // create DOM
  45. this.dom = {};
  46. dom = this.dom;
  47. // background box
  48. dom.box = document.createElement('div');
  49. // className is updated in repaint()
  50. // contents box
  51. dom.content = document.createElement('div');
  52. dom.content.className = 'content';
  53. dom.box.appendChild(dom.content);
  54. // attach this item as attribute
  55. dom.box['timeline-item'] = this;
  56. }
  57. // append DOM to parent DOM
  58. if (!this.parent) {
  59. throw new Error('Cannot repaint item: no parent attached');
  60. }
  61. if (!dom.box.parentNode) {
  62. var foreground = this.parent.getForeground();
  63. if (!foreground) {
  64. throw new Error('Cannot repaint time axis: parent has no foreground container element');
  65. }
  66. foreground.appendChild(dom.box);
  67. }
  68. this.displayed = true;
  69. // update contents
  70. if (this.data.content != this.content) {
  71. this.content = this.data.content;
  72. if (this.content instanceof Element) {
  73. dom.content.innerHTML = '';
  74. dom.content.appendChild(this.content);
  75. }
  76. else if (this.data.content != undefined) {
  77. dom.content.innerHTML = this.content;
  78. }
  79. else {
  80. throw new Error('Property "content" missing in item ' + this.data.id);
  81. }
  82. this.dirty = true;
  83. }
  84. // update class
  85. var className = (this.data.className ? (' ' + this.data.className) : '') +
  86. (this.selected ? ' selected' : '');
  87. if (this.className != className) {
  88. this.className = className;
  89. dom.box.className = this.baseClassName + className;
  90. this.dirty = true;
  91. }
  92. // recalculate size
  93. if (this.dirty) {
  94. this.props.content.width = this.dom.content.offsetWidth;
  95. this.height = this.dom.box.offsetHeight;
  96. this.dirty = false;
  97. }
  98. this._repaintDeleteButton(dom.box);
  99. this._repaintDragLeft();
  100. this._repaintDragRight();
  101. };
  102. /**
  103. * Show the item in the DOM (when not already visible). The items DOM will
  104. * be created when needed.
  105. */
  106. ItemRange.prototype.show = function show() {
  107. if (!this.displayed) {
  108. this.repaint();
  109. }
  110. };
  111. /**
  112. * Hide the item from the DOM (when visible)
  113. * @return {Boolean} changed
  114. */
  115. ItemRange.prototype.hide = function hide() {
  116. if (this.displayed) {
  117. var box = this.dom.box;
  118. if (box.parentNode) {
  119. box.parentNode.removeChild(box);
  120. }
  121. this.top = null;
  122. this.left = null;
  123. this.displayed = false;
  124. }
  125. };
  126. /**
  127. * Reposition the item horizontally
  128. * @Override
  129. */
  130. ItemRange.prototype.repositionX = function repositionX() {
  131. var props = this.props,
  132. parentWidth = this.parent.width,
  133. start = this.defaultOptions.toScreen(this.data.start),
  134. end = this.defaultOptions.toScreen(this.data.end),
  135. padding = 'padding' in this.options ? this.options.padding : this.defaultOptions.padding,
  136. contentLeft;
  137. // limit the width of the this, as browsers cannot draw very wide divs
  138. if (start < -parentWidth) {
  139. start = -parentWidth;
  140. }
  141. if (end > 2 * parentWidth) {
  142. end = 2 * parentWidth;
  143. }
  144. // when range exceeds left of the window, position the contents at the left of the visible area
  145. if (start < 0) {
  146. contentLeft = Math.min(-start,
  147. (end - start - props.content.width - 2 * padding));
  148. // TODO: remove the need for options.padding. it's terrible.
  149. }
  150. else {
  151. contentLeft = 0;
  152. }
  153. this.left = start;
  154. this.width = Math.max(end - start, 1);
  155. this.dom.box.style.left = this.left + 'px';
  156. this.dom.box.style.width = this.width + 'px';
  157. this.dom.content.style.left = contentLeft + 'px';
  158. };
  159. /**
  160. * Reposition the item vertically
  161. * @Override
  162. */
  163. ItemRange.prototype.repositionY = function repositionY() {
  164. var orientation = this.options.orientation || this.defaultOptions.orientation,
  165. box = this.dom.box;
  166. if (orientation == 'top') {
  167. box.style.top = this.top + 'px';
  168. box.style.bottom = '';
  169. }
  170. else {
  171. box.style.top = '';
  172. box.style.bottom = this.top + 'px';
  173. }
  174. };
  175. /**
  176. * Repaint a drag area on the left side of the range when the range is selected
  177. * @protected
  178. */
  179. ItemRange.prototype._repaintDragLeft = function () {
  180. if (this.selected && this.options.editable && !this.dom.dragLeft) {
  181. // create and show drag area
  182. var dragLeft = document.createElement('div');
  183. dragLeft.className = 'drag-left';
  184. dragLeft.dragLeftItem = this;
  185. // TODO: this should be redundant?
  186. Hammer(dragLeft, {
  187. preventDefault: true
  188. }).on('drag', function () {
  189. //console.log('drag left')
  190. });
  191. this.dom.box.appendChild(dragLeft);
  192. this.dom.dragLeft = dragLeft;
  193. }
  194. else if (!this.selected && this.dom.dragLeft) {
  195. // delete drag area
  196. if (this.dom.dragLeft.parentNode) {
  197. this.dom.dragLeft.parentNode.removeChild(this.dom.dragLeft);
  198. }
  199. this.dom.dragLeft = null;
  200. }
  201. };
  202. /**
  203. * Repaint a drag area on the right side of the range when the range is selected
  204. * @protected
  205. */
  206. ItemRange.prototype._repaintDragRight = function () {
  207. if (this.selected && this.options.editable && !this.dom.dragRight) {
  208. // create and show drag area
  209. var dragRight = document.createElement('div');
  210. dragRight.className = 'drag-right';
  211. dragRight.dragRightItem = this;
  212. // TODO: this should be redundant?
  213. Hammer(dragRight, {
  214. preventDefault: true
  215. }).on('drag', function () {
  216. //console.log('drag right')
  217. });
  218. this.dom.box.appendChild(dragRight);
  219. this.dom.dragRight = dragRight;
  220. }
  221. else if (!this.selected && this.dom.dragRight) {
  222. // delete drag area
  223. if (this.dom.dragRight.parentNode) {
  224. this.dom.dragRight.parentNode.removeChild(this.dom.dragRight);
  225. }
  226. this.dom.dragRight = null;
  227. }
  228. };