UNPKG

10.6 kBJavaScriptView Raw
1var Item = require('./Item');
2
3/**
4 * @constructor BoxItem
5 * @extends Item
6 * @param {Object} data Object containing parameters start
7 * content, className.
8 * @param {{toScreen: function, toTime: function}} conversion
9 * Conversion functions from time to screen and vice versa
10 * @param {Object} [options] Configuration options
11 * // TODO: describe available options
12 */
13function BoxItem (data, conversion, options) {
14 this.props = {
15 dot: {
16 width: 0,
17 height: 0
18 },
19 line: {
20 width: 0,
21 height: 0
22 }
23 };
24 this.options = options;
25 // validate data
26 if (data) {
27 if (data.start == undefined) {
28 throw new Error('Property "start" missing in item ' + data);
29 }
30 }
31
32 Item.call(this, data, conversion, options);
33}
34
35BoxItem.prototype = new Item (null, null, null);
36
37/**
38 * Check whether this item is visible inside given range
39 * @param {{start: number, end: number}} range with a timestamp for start and end
40 * @returns {boolean} True if visible
41 */
42BoxItem.prototype.isVisible = function(range) {
43 // determine visibility
44 var isVisible;
45 var align = this.options.align;
46 var widthInMs = this.width * range.getMillisecondsPerPixel();
47
48 if (align == 'right') {
49 isVisible = (this.data.start.getTime() > range.start ) && (this.data.start.getTime() - widthInMs < range.end);
50 }
51 else if (align == 'left') {
52 isVisible = (this.data.start.getTime() + widthInMs > range.start ) && (this.data.start.getTime() < range.end);
53 }
54 else {
55 // default or 'center'
56 isVisible = (this.data.start.getTime() + widthInMs/2 > range.start ) && (this.data.start.getTime() - widthInMs/2 < range.end);
57 }
58 return isVisible;
59};
60
61BoxItem.prototype._createDomElement = function() {
62 if (!this.dom) {
63 // create DOM
64 this.dom = {};
65
66 // create main box
67 this.dom.box = document.createElement('DIV');
68
69 // contents box (inside the background box). used for making margins
70 this.dom.content = document.createElement('DIV');
71 this.dom.content.className = 'vis-item-content';
72 this.dom.box.appendChild(this.dom.content);
73
74 // line to axis
75 this.dom.line = document.createElement('DIV');
76 this.dom.line.className = 'vis-line';
77
78 // dot on axis
79 this.dom.dot = document.createElement('DIV');
80 this.dom.dot.className = 'vis-dot';
81
82 // attach this item as attribute
83 this.dom.box['timeline-item'] = this;
84
85 this.dirty = true;
86 }
87}
88
89BoxItem.prototype._appendDomElement = function() {
90 if (!this.parent) {
91 throw new Error('Cannot redraw item: no parent attached');
92 }
93 if (!this.dom.box.parentNode) {
94 var foreground = this.parent.dom.foreground;
95 if (!foreground) throw new Error('Cannot redraw item: parent has no foreground container element');
96 foreground.appendChild(this.dom.box);
97 }
98 if (!this.dom.line.parentNode) {
99 var background = this.parent.dom.background;
100 if (!background) throw new Error('Cannot redraw item: parent has no background container element');
101 background.appendChild(this.dom.line);
102 }
103 if (!this.dom.dot.parentNode) {
104 var axis = this.parent.dom.axis;
105 if (!background) throw new Error('Cannot redraw item: parent has no axis container element');
106 axis.appendChild(this.dom.dot);
107 }
108 this.displayed = true;
109}
110
111BoxItem.prototype._updateDirtyDomComponents = function() {
112 // An item is marked dirty when:
113 // - the item is not yet rendered
114 // - the item's data is changed
115 // - the item is selected/deselected
116 if (this.dirty) {
117 this._updateContents(this.dom.content);
118 this._updateDataAttributes(this.dom.box);
119 this._updateStyle(this.dom.box);
120
121 var editable = (this.editable.updateTime || this.editable.updateGroup);
122
123 // update class
124 var className = (this.data.className? ' ' + this.data.className : '') +
125 (this.selected ? ' vis-selected' : '') +
126 (editable ? ' vis-editable' : ' vis-readonly');
127 this.dom.box.className = 'vis-item vis-box' + className;
128 this.dom.line.className = 'vis-item vis-line' + className;
129 this.dom.dot.className = 'vis-item vis-dot' + className;
130 }
131}
132
133BoxItem.prototype._getDomComponentsSizes = function() {
134 return {
135 previous: {
136 right: this.dom.box.style.right,
137 left: this.dom.box.style.left
138 },
139 dot: {
140 height: this.dom.dot.offsetHeight,
141 width: this.dom.dot.offsetWidth
142 },
143 line: {
144 width: this.dom.line.offsetWidth
145 },
146 box: {
147 width: this.dom.box.offsetWidth,
148 height: this.dom.box.offsetHeight
149 }
150 }
151}
152
153BoxItem.prototype._updateDomComponentsSizes = function(sizes) {
154 if (this.options.rtl) {
155 this.dom.box.style.right = "0px";
156 } else {
157 this.dom.box.style.left = "0px";
158 }
159
160 // recalculate size
161 this.props.dot.height = sizes.dot.height;
162 this.props.dot.width = sizes.dot.width;
163 this.props.line.width = sizes.line.width;
164 this.width = sizes.box.width;
165 this.height = sizes.box.height;
166
167 // restore previous position
168 if (this.options.rtl) {
169 this.dom.box.style.right = sizes.previous.right;
170 } else {
171 this.dom.box.style.left = sizes.previous.left;
172 }
173
174 this.dirty = false;
175}
176
177BoxItem.prototype._repaintDomAdditionals = function() {
178 this._repaintOnItemUpdateTimeTooltip(this.dom.box);
179 this._repaintDragCenter();
180 this._repaintDeleteButton(this.dom.box);
181}
182
183/**
184 * Repaint the item
185 * @param {boolean} [returnQueue=false] return the queue
186 * @return {boolean} the redraw queue if returnQueue=true
187 */
188BoxItem.prototype.redraw = function(returnQueue) {
189 var sizes
190 var queue = [
191 // create item DOM
192 this._createDomElement.bind(this),
193
194 // append DOM to parent DOM
195 this._appendDomElement.bind(this),
196
197 // update dirty DOM
198 this._updateDirtyDomComponents.bind(this),
199
200 (function() {
201 if (this.dirty) {
202 sizes = this._getDomComponentsSizes();
203 }
204 }).bind(this),
205
206 (function() {
207 if (this.dirty) {
208 this._updateDomComponentsSizes.bind(this)(sizes);
209 }
210 }).bind(this),
211
212 // repaint DOM additionals
213 this._repaintDomAdditionals.bind(this)
214 ];
215
216 if (returnQueue) {
217 return queue;
218 } else {
219 var result;
220 queue.forEach(function (fn) {
221 result = fn();
222 });
223 return result;
224 }
225};
226
227/**
228 * Show the item in the DOM (when not already visible). The items DOM will
229 * be created when needed.
230 * @param {boolean} [returnQueue=false] whether to return a queue of functions to execute instead of just executing them
231 * @return {boolean} the redraw queue if returnQueue=true
232 */
233BoxItem.prototype.show = function(returnQueue) {
234 if (!this.displayed) {
235 return this.redraw(returnQueue);
236 }
237};
238
239/**
240 * Hide the item from the DOM (when visible)
241 */
242BoxItem.prototype.hide = function() {
243 if (this.displayed) {
244 var dom = this.dom;
245
246 if (dom.box.parentNode) dom.box.parentNode.removeChild(dom.box);
247 if (dom.line.parentNode) dom.line.parentNode.removeChild(dom.line);
248 if (dom.dot.parentNode) dom.dot.parentNode.removeChild(dom.dot);
249
250 this.displayed = false;
251 }
252};
253
254/**
255 * Reposition the item horizontally
256 * @Override
257 */
258BoxItem.prototype.repositionX = function() {
259 var start = this.conversion.toScreen(this.data.start);
260 var align = this.options.align;
261
262 // calculate left position of the box
263 if (align == 'right') {
264 if (this.options.rtl) {
265 this.right = start - this.width;
266
267 // reposition box, line, and dot
268 this.dom.box.style.right = this.right + 'px';
269 this.dom.line.style.right = (start - this.props.line.width) + 'px';
270 this.dom.dot.style.right = (start - this.props.line.width / 2 - this.props.dot.width / 2) + 'px';
271 } else {
272 this.left = start - this.width;
273
274 // reposition box, line, and dot
275 this.dom.box.style.left = this.left + 'px';
276 this.dom.line.style.left = (start - this.props.line.width) + 'px';
277 this.dom.dot.style.left = (start - this.props.line.width / 2 - this.props.dot.width / 2) + 'px';
278 }
279 }
280 else if (align == 'left') {
281 if (this.options.rtl) {
282 this.right = start;
283
284 // reposition box, line, and dot
285 this.dom.box.style.right = this.right + 'px';
286 this.dom.line.style.right = start + 'px';
287 this.dom.dot.style.right = (start + this.props.line.width / 2 - this.props.dot.width / 2) + 'px';
288 } else {
289 this.left = start;
290
291 // reposition box, line, and dot
292 this.dom.box.style.left = this.left + 'px';
293 this.dom.line.style.left = start + 'px';
294 this.dom.dot.style.left = (start + this.props.line.width / 2 - this.props.dot.width / 2) + 'px';
295 }
296 }
297 else {
298 // default or 'center'
299 if (this.options.rtl) {
300 this.right = start - this.width / 2;
301
302 // reposition box, line, and dot
303 this.dom.box.style.right = this.right + 'px';
304 this.dom.line.style.right = (start - this.props.line.width) + 'px';
305 this.dom.dot.style.right = (start - this.props.dot.width / 2) + 'px';
306 } else {
307 this.left = start - this.width / 2;
308
309 // reposition box, line, and dot
310 this.dom.box.style.left = this.left + 'px';
311 this.dom.line.style.left = (start - this.props.line.width / 2) + 'px';
312 this.dom.dot.style.left = (start - this.props.dot.width / 2) + 'px';
313 }
314 }
315};
316
317/**
318 * Reposition the item vertically
319 * @Override
320 */
321BoxItem.prototype.repositionY = function() {
322 var orientation = this.options.orientation.item;
323 var box = this.dom.box;
324 var line = this.dom.line;
325 var dot = this.dom.dot;
326
327 if (orientation == 'top') {
328 box.style.top = (this.top || 0) + 'px';
329
330 line.style.top = '0';
331 line.style.height = (this.parent.top + this.top + 1) + 'px';
332 line.style.bottom = '';
333 }
334 else { // orientation 'bottom'
335 var itemSetHeight = this.parent.itemSet.props.height; // TODO: this is nasty
336 var lineHeight = itemSetHeight - this.parent.top - this.parent.height + this.top;
337
338 box.style.top = (this.parent.height - this.top - this.height || 0) + 'px';
339 line.style.top = (itemSetHeight - lineHeight) + 'px';
340 line.style.bottom = '0';
341 }
342
343 dot.style.top = (-this.props.dot.height / 2) + 'px';
344};
345
346/**
347 * Return the width of the item left from its start date
348 * @return {number}
349 */
350BoxItem.prototype.getWidthLeft = function () {
351 return this.width / 2;
352};
353
354/**
355 * Return the width of the item right from its start date
356 * @return {number}
357 */
358BoxItem.prototype.getWidthRight = function () {
359 return this.width / 2;
360};
361
362module.exports = BoxItem;