UNPKG

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