diff --git a/examples/timeline/other/stressPerformance.html b/examples/timeline/other/stressPerformance.html
new file mode 100644
index 00000000..94906e06
--- /dev/null
+++ b/examples/timeline/other/stressPerformance.html
@@ -0,0 +1,66 @@
+
+
+
+
+
+ Timeline | Stress Performance example
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lib/timeline/Timeline.js b/lib/timeline/Timeline.js
index c1089613..d98d3529 100644
--- a/lib/timeline/Timeline.js
+++ b/lib/timeline/Timeline.js
@@ -469,13 +469,30 @@ Timeline.prototype.getItemRange = function () {
}
var factor = interval / this.props.center.width;
- // calculate the date of the left side and right side of the items given
- util.forEach(this.itemSet.items, function (item) {
+ var redrawQueue = {};
+ var redrawQueueLength = 0;
+
+ // collect redraw functions
+ util.forEach(this.itemSet.items, function (item, key) {
if (item.groupShowing) {
- item.show();
- item.repositionX();
+ var returnQueue = true;
+ redrawQueue[key] = item.redraw(returnQueue);
+ redrawQueueLength = redrawQueue[key].length;
+ }
+ })
+
+ var needRedraw = redrawQueueLength > 0;
+ if (needRedraw) {
+ // redraw all regular items
+ for (var i = 0; i < redrawQueueLength; i++) {
+ util.forEach(redrawQueue, function (fns) {
+ fns[i]();
+ });
}
+ }
+ // calculate the date of the left side and right side of the items given
+ util.forEach(this.itemSet.items, function (item) {
var start = getStart(item);
var end = getEnd(item);
var startSide;
@@ -489,7 +506,6 @@ Timeline.prototype.getItemRange = function () {
endSide = end + (item.getWidthRight() + 10) * factor;
}
-
if (startSide < min) {
min = startSide;
minItem = item;
diff --git a/lib/timeline/component/Group.js b/lib/timeline/component/Group.js
index 9d2301e6..734e0d15 100644
--- a/lib/timeline/component/Group.js
+++ b/lib/timeline/component/Group.js
@@ -210,19 +210,38 @@ Group.prototype._didMarkerHeightChange = function() {
var markerHeight = this.dom.marker.clientHeight;
if (markerHeight != this.lastMarkerHeight) {
this.lastMarkerHeight = markerHeight;
- util.forEach(this.items, function (item) {
+ var redrawQueue = {};
+ var redrawQueueLength = 0;
+
+ util.forEach(this.items, function (item, key) {
item.dirty = true;
- if (item.displayed) item.redraw();
- });
+ if (item.displayed) {
+ var returnQueue = true;
+ redrawQueue[key] = item.redraw(returnQueue);
+ redrawQueueLength = redrawQueue[key].length;
+ }
+ })
+
+ var needRedraw = redrawQueueLength > 0;
+ if (needRedraw) {
+ // redraw all regular items
+ for (var i = 0; i < redrawQueueLength; i++) {
+ util.forEach(redrawQueue, function (fns) {
+ fns[i]();
+ });
+ }
+ }
return true;
}
}
Group.prototype._calculateGroupSizeAndPosition = function() {
- var foreground = this.dom.foreground;
- this.top = foreground.offsetTop;
- this.right = foreground.offsetLeft;
- this.width = foreground.offsetWidth;
+ var offsetTop = this.dom.foreground.offsetTop
+ var offsetLeft = this.dom.foreground.offsetLeft
+ var offsetWidth = this.dom.foreground.offsetWidth
+ this.top = offsetTop;
+ this.right = offsetLeft;
+ this.width = offsetWidth;
}
Group.prototype._redrawItems = function(forceRestack, lastIsVisible, margin, range) {
@@ -237,13 +256,32 @@ Group.prototype._redrawItems = function(forceRestack, lastIsVisible, margin, ran
// show all items
var me = this;
var limitSize = false;
- util.forEach(this.items, function (item) {
+
+ var redrawQueue = {};
+ var redrawQueueLength = 0;
+
+ util.forEach(this.items, function (item, key) {
if (!item.displayed) {
- item.redraw();
+ var returnQueue = true;
+ redrawQueue[key] = item.redraw(returnQueue);
+ redrawQueueLength = redrawQueue[key].length;
me.visibleItems.push(item);
}
- item.repositionX(limitSize);
- });
+ })
+
+ var needRedraw = redrawQueueLength > 0;
+ if (needRedraw) {
+ // redraw all regular items
+ for (var i = 0; i < redrawQueueLength; i++) {
+ util.forEach(redrawQueue, function (fns) {
+ fns[i]();
+ });
+ }
+ }
+
+ for (i = 0; i < this.items.length; i++) {
+ this.items[i].repositionX(limitSize);
+ }
// order all items and force a restacking
var customOrderedItems = this.orderedItems.byStart.slice().sort(function (a, b) {
@@ -727,14 +765,31 @@ Group.prototype._updateItemsInRange = function(orderedItems, oldVisibleItems, ra
});
}
- // finally, we reposition all the visible items.
+ var redrawQueue = {};
+ var redrawQueueLength = 0;
+
for (i = 0; i < visibleItems.length; i++) {
var item = visibleItems[i];
- if (!item.displayed) item.show();
- // reposition item horizontally
- item.repositionX();
+ if (!item.displayed) {
+ var returnQueue = true;
+ redrawQueue[i] = item.redraw(returnQueue);
+ redrawQueueLength = redrawQueue[i].length;
+ }
+ }
+
+ var needRedraw = redrawQueueLength > 0;
+ if (needRedraw) {
+ // redraw all regular items
+ for (var j = 0; j < redrawQueueLength; j++) {
+ util.forEach(redrawQueue, function (fns) {
+ fns[j]();
+ });
+ }
+ }
+
+ for (i = 0; i < visibleItems.length; i++) {
+ visibleItems[i].repositionX();
}
-
return visibleItems;
};
diff --git a/lib/timeline/component/ItemSet.js b/lib/timeline/component/ItemSet.js
index 5bd9bb41..967d1132 100644
--- a/lib/timeline/component/ItemSet.js
+++ b/lib/timeline/component/ItemSet.js
@@ -693,7 +693,8 @@ ItemSet.prototype.redraw = function() {
redrawQueueLength = redrawQueue[key].length;
});
- if (redrawQueueLength) {
+ var needRedraw = redrawQueueLength > 0;
+ if (needRedraw) {
var redrawResults = {};
for (var i = 0; i < redrawQueueLength; i++) {
diff --git a/lib/timeline/component/item/BackgroundItem.js b/lib/timeline/component/item/BackgroundItem.js
index 9e1e3b19..4545c586 100644
--- a/lib/timeline/component/item/BackgroundItem.js
+++ b/lib/timeline/component/item/BackgroundItem.js
@@ -37,6 +37,7 @@ function BackgroundItem (data, conversion, options) {
BackgroundItem.prototype = new Item (null, null, null);
BackgroundItem.prototype.baseClassName = 'vis-item vis-background';
+
BackgroundItem.prototype.stack = false;
/**
@@ -49,51 +50,49 @@ BackgroundItem.prototype.isVisible = function(range) {
return (this.data.start < range.end) && (this.data.end > range.start);
};
-/**
- * Repaint the item
- */
-BackgroundItem.prototype.redraw = function() {
- var dom = this.dom;
- if (!dom) {
+BackgroundItem.prototype._createDomElement = function() {
+ if (!this.dom) {
// create DOM
this.dom = {};
- dom = this.dom;
// background box
- dom.box = document.createElement('div');
+ this.dom.box = document.createElement('div');
// className is updated in redraw()
// frame box (to prevent the item contents from overflowing
- dom.frame = document.createElement('div');
- dom.frame.className = 'vis-item-overflow';
- dom.box.appendChild(dom.frame);
+ this.dom.frame = document.createElement('div');
+ this.dom.frame.className = 'vis-item-overflow';
+ this.dom.box.appendChild(this.dom.frame);
// contents box
- dom.content = document.createElement('div');
- dom.content.className = 'vis-item-content';
- dom.frame.appendChild(dom.content);
+ this.dom.content = document.createElement('div');
+ this.dom.content.className = 'vis-item-content';
+ this.dom.frame.appendChild(this.dom.content);
// Note: we do NOT attach this item as attribute to the DOM,
// such that background items cannot be selected
- //dom.box['timeline-item'] = this;
+ //this.dom.box['timeline-item'] = this;
this.dirty = true;
}
+}
- // append DOM to parent DOM
+BackgroundItem.prototype._appendDomElement = function() {
if (!this.parent) {
throw new Error('Cannot redraw item: no parent attached');
}
- if (!dom.box.parentNode) {
+ if (!this.dom.box.parentNode) {
var background = this.parent.dom.background;
if (!background) {
throw new Error('Cannot redraw item: parent has no background container element');
}
- background.appendChild(dom.box);
+ background.appendChild(this.dom.box);
}
this.displayed = true;
+}
- // Update DOM when item is marked dirty. An item is marked dirty when:
+BackgroundItem.prototype._updateDirtyDomComponents = function() {
+ // update dirty DOM. An item is marked dirty when:
// - the item is not yet rendered
// - the item's data is changed
// - the item is selected/deselected
@@ -105,16 +104,71 @@ BackgroundItem.prototype.redraw = function() {
// update class
var className = (this.data.className ? (' ' + this.data.className) : '') +
(this.selected ? ' vis-selected' : '');
- dom.box.className = this.baseClassName + className;
+ this.dom.box.className = this.baseClassName + className;
+ }
+}
- // determine from css whether this box has overflow
- this.overflow = window.getComputedStyle(dom.content).overflow !== 'hidden';
+BackgroundItem.prototype._getDomComponentsSizes = function() {
+ // determine from css whether this box has overflow
+ this.overflow = window.getComputedStyle(this.dom.content).overflow !== 'hidden';
+ return {
+ content: {
+ width: this.dom.content.offsetWidth
+ }
+ }
+}
- // recalculate size
- this.props.content.width = this.dom.content.offsetWidth;
- this.height = 0; // set height zero, so this item will be ignored when stacking items
+BackgroundItem.prototype._updateDomComponentsSizes = function(sizes) {
+ // recalculate size
+ this.props.content.width = sizes.content.width;
+ this.height = 0; // set height zero, so this item will be ignored when stacking items
- this.dirty = false;
+ this.dirty = false;
+}
+
+BackgroundItem.prototype._repaintDomAdditionals = function() {
+}
+
+/**
+ * Repaint the item
+ * @param {boolean} [returnQueue=false] return the queue
+ * @return {boolean} the redraw result or the redraw queue if returnQueue=true
+ */
+BackgroundItem.prototype.redraw = function(returnQueue) {
+ var sizes
+ var queue = [
+ // create item DOM
+ this._createDomElement.bind(this),
+
+ // append DOM to parent DOM
+ this._appendDomElement.bind(this),
+
+ this._updateDirtyDomComponents.bind(this),
+
+ (function() {
+ if (this.dirty) {
+ sizes = this._getDomComponentsSizes.bind(this)();
+ }
+ }).bind(this),
+
+ (function() {
+ if (this.dirty) {
+ this._updateDomComponentsSizes.bind(this)(sizes);
+ }
+ }).bind(this),
+
+ // repaint DOM additionals
+ this._repaintDomAdditionals.bind(this)
+ ];
+
+ if (returnQueue) {
+ return queue;
+ } else {
+ var result;
+ queue.forEach(function (fn) {
+ result = fn();
+ });
+ return result;
}
};
diff --git a/lib/timeline/component/item/BoxItem.js b/lib/timeline/component/item/BoxItem.js
index f9b6fc8b..e93b0990 100644
--- a/lib/timeline/component/item/BoxItem.js
+++ b/lib/timeline/component/item/BoxItem.js
@@ -58,60 +58,58 @@ BoxItem.prototype.isVisible = function(range) {
return isVisible;
};
-/**
- * Repaint the item
- */
-BoxItem.prototype.redraw = function() {
- var dom = this.dom;
- if (!dom) {
+BoxItem.prototype._createDomElement = function() {
+ if (!this.dom) {
// create DOM
this.dom = {};
- dom = this.dom;
// create main box
- dom.box = document.createElement('DIV');
+ this.dom.box = document.createElement('DIV');
// contents box (inside the background box). used for making margins
- dom.content = document.createElement('DIV');
- dom.content.className = 'vis-item-content';
- dom.box.appendChild(dom.content);
+ this.dom.content = document.createElement('DIV');
+ this.dom.content.className = 'vis-item-content';
+ this.dom.box.appendChild(this.dom.content);
// line to axis
- dom.line = document.createElement('DIV');
- dom.line.className = 'vis-line';
+ this.dom.line = document.createElement('DIV');
+ this.dom.line.className = 'vis-line';
// dot on axis
- dom.dot = document.createElement('DIV');
- dom.dot.className = 'vis-dot';
+ this.dom.dot = document.createElement('DIV');
+ this.dom.dot.className = 'vis-dot';
// attach this item as attribute
- dom.box['timeline-item'] = this;
+ this.dom.box['timeline-item'] = this;
this.dirty = true;
}
+}
- // append DOM to parent DOM
+BoxItem.prototype._appendDomElement = function() {
if (!this.parent) {
throw new Error('Cannot redraw item: no parent attached');
}
- if (!dom.box.parentNode) {
+ if (!this.dom.box.parentNode) {
var foreground = this.parent.dom.foreground;
if (!foreground) throw new Error('Cannot redraw item: parent has no foreground container element');
- foreground.appendChild(dom.box);
+ foreground.appendChild(this.dom.box);
}
- if (!dom.line.parentNode) {
+ if (!this.dom.line.parentNode) {
var background = this.parent.dom.background;
if (!background) throw new Error('Cannot redraw item: parent has no background container element');
- background.appendChild(dom.line);
+ background.appendChild(this.dom.line);
}
- if (!dom.dot.parentNode) {
+ if (!this.dom.dot.parentNode) {
var axis = this.parent.dom.axis;
if (!background) throw new Error('Cannot redraw item: parent has no axis container element');
- axis.appendChild(dom.dot);
+ axis.appendChild(this.dom.dot);
}
this.displayed = true;
+}
- // Update DOM when item is marked dirty. An item is marked dirty when:
+BoxItem.prototype._updateDirtyDomComponents = function() {
+ // An item is marked dirty when:
// - the item is not yet rendered
// - the item's data is changed
// - the item is selected/deselected
@@ -126,41 +124,104 @@ BoxItem.prototype.redraw = function() {
var className = (this.data.className? ' ' + this.data.className : '') +
(this.selected ? ' vis-selected' : '') +
(editable ? ' vis-editable' : ' vis-readonly');
- dom.box.className = 'vis-item vis-box' + className;
- dom.line.className = 'vis-item vis-line' + className;
- dom.dot.className = 'vis-item vis-dot' + className;
-
- // set initial position in the visible range of the grid so that the
- // rendered box size can be determinated correctly, even the content
- // has a dynamic width (fixes #2032).
- var previousRight = dom.box.style.right;
- var previousLeft = dom.box.style.left;
- if (this.options.rtl) {
- dom.box.style.right = "0px";
- } else {
- dom.box.style.left = "0px";
- }
-
- // recalculate size
- this.props.dot.height = dom.dot.offsetHeight;
- this.props.dot.width = dom.dot.offsetWidth;
- this.props.line.width = dom.line.offsetWidth;
- this.width = dom.box.offsetWidth;
- this.height = dom.box.offsetHeight;
+ this.dom.box.className = 'vis-item vis-box' + className;
+ this.dom.line.className = 'vis-item vis-line' + className;
+ this.dom.dot.className = 'vis-item vis-dot' + className;
+ }
+}
- // restore previous position
- if (this.options.rtl) {
- dom.box.style.right = previousRight;
- } else {
- dom.box.style.left = previousLeft;
+BoxItem.prototype._getDomComponentsSizes = function() {
+ return {
+ previous: {
+ right: this.dom.box.style.right,
+ left: this.dom.box.style.left
+ },
+ dot: {
+ height: this.dom.dot.offsetHeight,
+ width: this.dom.dot.offsetWidth
+ },
+ line: {
+ width: this.dom.line.offsetWidth
+ },
+ box: {
+ width: this.dom.box.offsetWidth,
+ height: this.dom.box.offsetHeight
}
+ }
+}
+
+BoxItem.prototype._updateDomComponentsSizes = function(sizes) {
+ if (this.options.rtl) {
+ this.dom.box.style.right = "0px";
+ } else {
+ this.dom.box.style.left = "0px";
+ }
- this.dirty = false;
+ // recalculate size
+ this.props.dot.height = sizes.dot.height;
+ this.props.dot.width = sizes.dot.width;
+ this.props.line.width = sizes.line.width;
+ this.width = sizes.box.width;
+ this.height = sizes.box.height;
+
+ // restore previous position
+ if (this.options.rtl) {
+ this.dom.box.style.right = sizes.previous.right;
+ } else {
+ this.dom.box.style.left = sizes.previous.left;
}
- this._repaintOnItemUpdateTimeTooltip(dom.box);
+ this.dirty = false;
+}
+
+BoxItem.prototype._repaintDomAdditionals = function() {
+ this._repaintOnItemUpdateTimeTooltip(this.dom.box);
this._repaintDragCenter();
- this._repaintDeleteButton(dom.box);
+ this._repaintDeleteButton(this.dom.box);
+}
+
+/**
+ * Repaint the item
+ * @param {boolean} [returnQueue=false] return the queue
+ * @return {boolean} the redraw queue if returnQueue=true
+ */
+BoxItem.prototype.redraw = function(returnQueue) {
+ var sizes
+ var queue = [
+ // create item DOM
+ this._createDomElement.bind(this),
+
+ // append DOM to parent DOM
+ this._appendDomElement.bind(this),
+
+ // update dirty DOM
+ this._updateDirtyDomComponents.bind(this),
+
+ (function() {
+ if (this.dirty) {
+ sizes = this._getDomComponentsSizes();
+ }
+ }).bind(this),
+
+ (function() {
+ if (this.dirty) {
+ this._updateDomComponentsSizes.bind(this)(sizes);
+ }
+ }).bind(this),
+
+ // repaint DOM additionals
+ this._repaintDomAdditionals.bind(this)
+ ];
+
+ if (returnQueue) {
+ return queue;
+ } else {
+ var result;
+ queue.forEach(function (fn) {
+ result = fn();
+ });
+ return result;
+ }
};
/**
diff --git a/lib/timeline/component/item/PointItem.js b/lib/timeline/component/item/PointItem.js
index 4d98d670..721a8a82 100644
--- a/lib/timeline/component/item/PointItem.js
+++ b/lib/timeline/component/item/PointItem.js
@@ -48,49 +48,48 @@ PointItem.prototype.isVisible = function(range) {
return (this.data.start.getTime() + widthInMs > range.start ) && (this.data.start < range.end);
};
-/**
- * Repaint the item
- */
-PointItem.prototype.redraw = function() {
- var dom = this.dom;
- if (!dom) {
+
+PointItem.prototype._createDomElement = function() {
+ if (!this.dom) {
// create DOM
this.dom = {};
- dom = this.dom;
// background box
- dom.point = document.createElement('div');
+ this.dom.point = document.createElement('div');
// className is updated in redraw()
// contents box, right from the dot
- dom.content = document.createElement('div');
- dom.content.className = 'vis-item-content';
- dom.point.appendChild(dom.content);
+ this.dom.content = document.createElement('div');
+ this.dom.content.className = 'vis-item-content';
+ this.dom.point.appendChild(this.dom.content);
// dot at start
- dom.dot = document.createElement('div');
- dom.point.appendChild(dom.dot);
+ this.dom.dot = document.createElement('div');
+ this.dom.point.appendChild(this.dom.dot);
// attach this item as attribute
- dom.point['timeline-item'] = this;
+ this.dom.point['timeline-item'] = this;
this.dirty = true;
}
+}
- // append DOM to parent DOM
+PointItem.prototype._appendDomElement = function() {
if (!this.parent) {
throw new Error('Cannot redraw item: no parent attached');
}
- if (!dom.point.parentNode) {
+ if (!this.dom.point.parentNode) {
var foreground = this.parent.dom.foreground;
if (!foreground) {
throw new Error('Cannot redraw item: parent has no foreground container element');
}
- foreground.appendChild(dom.point);
+ foreground.appendChild(this.dom.point);
}
this.displayed = true;
+}
- // Update DOM when item is marked dirty. An item is marked dirty when:
+PointItem.prototype._updateDirtyDomComponents = function() {
+ // An item is marked dirty when:
// - the item is not yet rendered
// - the item's data is changed
// - the item is selected/deselected
@@ -104,40 +103,105 @@ PointItem.prototype.redraw = function() {
var className = (this.data.className ? ' ' + this.data.className : '') +
(this.selected ? ' vis-selected' : '') +
(editable ? ' vis-editable' : ' vis-readonly');
- dom.point.className = 'vis-item vis-point' + className;
- dom.dot.className = 'vis-item vis-dot' + className;
-
- // recalculate size of dot and contents
- this.props.dot.width = dom.dot.offsetWidth;
- this.props.dot.height = dom.dot.offsetHeight;
- this.props.content.height = dom.content.offsetHeight;
-
- // resize contents
- if (this.options.rtl) {
- dom.content.style.marginRight = 2 * this.props.dot.width + 'px';
- } else {
- dom.content.style.marginLeft = 2 * this.props.dot.width + 'px';
- }
- //dom.content.style.marginRight = ... + 'px'; // TODO: margin right
-
- // recalculate size
- this.width = dom.point.offsetWidth;
- this.height = dom.point.offsetHeight;
-
- // reposition the dot
- dom.dot.style.top = ((this.height - this.props.dot.height) / 2) + 'px';
- if (this.options.rtl) {
- dom.dot.style.right = (this.props.dot.width / 2) + 'px';
- } else {
- dom.dot.style.left = (this.props.dot.width / 2) + 'px';
+ this.dom.point.className = 'vis-item vis-point' + className;
+ this.dom.dot.className = 'vis-item vis-dot' + className;
+ }
+}
+
+PointItem.prototype._getDomComponentsSizes = function() {
+ return {
+ dot: {
+ width: this.dom.dot.offsetWidth,
+ height: this.dom.dot.offsetHeight
+ },
+ content: {
+ width: this.dom.content.offsetWidth,
+ height: this.dom.content.offsetHeight
+ },
+ point: {
+ width: this.dom.point.offsetWidth,
+ height: this.dom.point.offsetHeight
}
+ }
+}
- this.dirty = false;
+PointItem.prototype._updateDomComponentsSizes = function(sizes) {
+ // recalculate size of dot and contents
+ this.props.dot.width = sizes.dot.width;
+ this.props.dot.height = sizes.dot.height;
+ this.props.content.height = sizes.content.height;
+
+ // resize contents
+ if (this.options.rtl) {
+ this.dom.content.style.marginRight = 2 * this.props.dot.width + 'px';
+ } else {
+ this.dom.content.style.marginLeft = 2 * this.props.dot.width + 'px';
}
-
- this._repaintOnItemUpdateTimeTooltip(dom.point);
+ //this.dom.content.style.marginRight = ... + 'px'; // TODO: margin right
+
+ // recalculate size
+ this.width = sizes.point.width;
+ this.height = sizes.point.height;
+
+ // reposition the dot
+ this.dom.dot.style.top = ((this.height - this.props.dot.height) / 2) + 'px';
+ if (this.options.rtl) {
+ this.dom.dot.style.right = (this.props.dot.width / 2) + 'px';
+ } else {
+ this.dom.dot.style.left = (this.props.dot.width / 2) + 'px';
+ }
+
+ this.dirty = false;
+}
+
+PointItem.prototype._repaintDomAdditionals = function() {
+ this._repaintOnItemUpdateTimeTooltip(this.dom.point);
this._repaintDragCenter();
- this._repaintDeleteButton(dom.point);
+ this._repaintDeleteButton(this.dom.point);
+}
+
+/**
+ * Repaint the item
+ * @param {boolean} [returnQueue=false] return the queue
+ * @return {boolean} the redraw queue if returnQueue=true
+ */
+PointItem.prototype.redraw = function(returnQueue) {
+ var sizes
+ var queue = [
+ // create item DOM
+ this._createDomElement.bind(this),
+
+ // append DOM to parent DOM
+ this._appendDomElement.bind(this),
+
+ // update dirty DOM
+ this._updateDirtyDomComponents.bind(this),
+
+ (function() {
+ if (this.dirty) {
+ sizes = this._getDomComponentsSizes();
+ }
+ }).bind(this),
+
+ (function() {
+ if (this.dirty) {
+ this._updateDomComponentsSizes.bind(this)(sizes);
+ }
+ }).bind(this),
+
+ // repaint DOM additionals
+ this._repaintDomAdditionals.bind(this)
+ ];
+
+ if (returnQueue) {
+ return queue;
+ } else {
+ var result;
+ queue.forEach(function (fn) {
+ result = fn();
+ });
+ return result;
+ }
};
/**
diff --git a/lib/timeline/component/item/RangeItem.js b/lib/timeline/component/item/RangeItem.js
index e1dbef4f..7457b266 100644
--- a/lib/timeline/component/item/RangeItem.js
+++ b/lib/timeline/component/item/RangeItem.js
@@ -46,55 +46,54 @@ RangeItem.prototype.isVisible = function(range) {
return (this.data.start < range.end) && (this.data.end > range.start);
};
-/**
- * Repaint the item
- */
-RangeItem.prototype.redraw = function() {
- var dom = this.dom;
- if (!dom) {
+RangeItem.prototype._createDomElement = function() {
+ if (!this.dom) {
// create DOM
this.dom = {};
- dom = this.dom;
// background box
- dom.box = document.createElement('div');
+ this.dom.box = document.createElement('div');
// className is updated in redraw()
// frame box (to prevent the item contents from overflowing)
- dom.frame = document.createElement('div');
- dom.frame.className = 'vis-item-overflow';
- dom.box.appendChild(dom.frame);
+ this.dom.frame = document.createElement('div');
+ this.dom.frame.className = 'vis-item-overflow';
+ this.dom.box.appendChild(this.dom.frame);
// visible frame box (showing the frame that is always visible)
- dom.visibleFrame = document.createElement('div');
- dom.visibleFrame.className = 'vis-item-visible-frame';
- dom.box.appendChild(dom.visibleFrame);
+ this.dom.visibleFrame = document.createElement('div');
+ this.dom.visibleFrame.className = 'vis-item-visible-frame';
+ this.dom.box.appendChild(this.dom.visibleFrame);
// contents box
- dom.content = document.createElement('div');
- dom.content.className = 'vis-item-content';
- dom.frame.appendChild(dom.content);
+ this.dom.content = document.createElement('div');
+ this.dom.content.className = 'vis-item-content';
+ this.dom.frame.appendChild(this.dom.content);
// attach this item as attribute
- dom.box['timeline-item'] = this;
+ this.dom.box['timeline-item'] = this;
this.dirty = true;
}
- // append DOM to parent DOM
+}
+
+RangeItem.prototype._appendDomElement = function() {
if (!this.parent) {
throw new Error('Cannot redraw item: no parent attached');
}
- if (!dom.box.parentNode) {
+ if (!this.dom.box.parentNode) {
var foreground = this.parent.dom.foreground;
if (!foreground) {
throw new Error('Cannot redraw item: parent has no foreground container element');
}
- foreground.appendChild(dom.box);
+ foreground.appendChild(this.dom.box);
}
this.displayed = true;
+}
- // Update DOM when item is marked dirty. An item is marked dirty when:
+RangeItem.prototype._updateDirtyDomComponents = function() {
+ // update dirty DOM. An item is marked dirty when:
// - the item is not yet rendered
// - the item's data is changed
// - the item is selected/deselected
@@ -109,27 +108,84 @@ RangeItem.prototype.redraw = function() {
var className = (this.data.className ? (' ' + this.data.className) : '') +
(this.selected ? ' vis-selected' : '') +
(editable ? ' vis-editable' : ' vis-readonly');
- dom.box.className = this.baseClassName + className;
+ this.dom.box.className = this.baseClassName + className;
- // determine from css whether this box has overflow
- this.overflow = window.getComputedStyle(dom.frame).overflow !== 'hidden';
-
- // recalculate size
// turn off max-width to be able to calculate the real width
// this causes an extra browser repaint/reflow, but so be it
this.dom.content.style.maxWidth = 'none';
- this.props.content.width = this.dom.content.offsetWidth;
- this.height = this.dom.box.offsetHeight;
- this.dom.content.style.maxWidth = '';
+ }
+}
- this.dirty = false;
+RangeItem.prototype._getDomComponentsSizes = function() {
+ // determine from css whether this box has overflow
+ this.overflow = window.getComputedStyle(this.dom.frame).overflow !== 'hidden';
+ return {
+ content: {
+ width: this.dom.content.offsetWidth,
+ },
+ box: {
+ height: this.dom.box.offsetHeight
+ }
}
+}
- this._repaintOnItemUpdateTimeTooltip(dom.box);
- this._repaintDeleteButton(dom.box);
+RangeItem.prototype._updateDomComponentsSizes = function(sizes) {
+ this.props.content.width = sizes.content.width;
+ this.height = sizes.box.height;
+ this.dom.content.style.maxWidth = '';
+ this.dirty = false;
+}
+
+RangeItem.prototype._repaintDomAdditionals = function() {
+ this._repaintOnItemUpdateTimeTooltip(this.dom.box);
+ this._repaintDeleteButton(this.dom.box);
this._repaintDragCenter();
this._repaintDragLeft();
this._repaintDragRight();
+}
+
+/**
+ * Repaint the item
+ * @param {boolean} [returnQueue=false] return the queue
+ * @return {boolean} the redraw queue if returnQueue=true
+ */
+RangeItem.prototype.redraw = function(returnQueue) {
+ var sizes;
+ var queue = [
+ // create item DOM
+ this._createDomElement.bind(this),
+
+ // append DOM to parent DOM
+ this._appendDomElement.bind(this),
+
+ // update dirty DOM
+ this._updateDirtyDomComponents.bind(this),
+
+ (function() {
+ if (this.dirty) {
+ sizes = this._getDomComponentsSizes.bind(this)();
+ }
+ }).bind(this),
+
+ (function() {
+ if (this.dirty) {
+ this._updateDomComponentsSizes.bind(this)(sizes);
+ }
+ }).bind(this),
+
+ // repaint DOM additionals
+ this._repaintDomAdditionals.bind(this)
+ ];
+
+ if (returnQueue) {
+ return queue;
+ } else {
+ var result;
+ queue.forEach(function (fn) {
+ result = fn();
+ });
+ return result;
+ }
};
/**