Browse Source

Merge branch 'develop' into linegraph

Conflicts:
	dist/vis.js
	dist/vis.min.js
	src/graph/Graph.js
css_transitions
Alex de Mulder 10 years ago
parent
commit
10d6e4ebd7
15 changed files with 288 additions and 86 deletions
  1. +11
    -2
      HISTORY.md
  2. +79
    -34
      dist/vis.js
  3. +7
    -7
      dist/vis.min.js
  4. +19
    -7
      docs/timeline.html
  5. +88
    -0
      examples/timeline/16_navigation_menu.html
  6. +1
    -0
      examples/timeline/index.html
  7. +26
    -22
      src/graph/Graph.js
  8. +4
    -4
      src/graph/graphMixins/ManipulationMixin.js
  9. +4
    -4
      src/graph/graphMixins/SelectionMixin.js
  10. +1
    -0
      src/module/exports.js
  11. +14
    -1
      src/timeline/Timeline.js
  12. +22
    -1
      src/timeline/component/Group.js
  13. +1
    -0
      src/timeline/component/RootPanel.js
  14. +7
    -2
      src/timeline/component/TimeAxis.js
  15. +4
    -2
      test/timeline.html

+ 11
- 2
HISTORY.md View File

@ -1,17 +1,26 @@
# vis.js history # vis.js history
http://visjs.org http://visjs.org
## not yet released, version 1.0.2 ## not yet released, version 1.0.2
### Timeline ### Timeline
- Implemented option `minHeight`, similar to option `maxHeight`.
- Added function `repaint()` to force a repaint of the Timeline.
- Some tweaks in snapping dragged items to nice dates. - Some tweaks in snapping dragged items to nice dates.
- Made the instance of moment.js packaged with vis.js accessibly via `vis.moment`.
- A newly created item is initialized with `end` property when option `type`
is `"range"` or `"rangeoverflow"`.
- Fixed a bug in replacing the DataSet of groups via `Timeline.setGroups(groups)`. - Fixed a bug in replacing the DataSet of groups via `Timeline.setGroups(groups)`.
- Fixed a bug when rendering the Timeline inside a hidden container.
### Graph ### Graph
- added zoomable and moveable options.
- changes setOptions to avoid resetting view.
- Added zoomable and moveable options.
- Changes setOptions to avoid resetting view.
- Interchanged canvasToDOM and DOMtoCanvas to correspond with the docs.
## 2014-05-09, version 1.0.1 ## 2014-05-09, version 1.0.1

+ 79
- 34
dist/vis.js View File

@ -4154,6 +4154,7 @@ RootPanel.prototype.repaint = function repaint() {
// update frame size // update frame size
this.frame.style.maxHeight = util.option.asSize(this.options.maxHeight, ''); this.frame.style.maxHeight = util.option.asSize(this.options.maxHeight, '');
this.frame.style.minHeight = util.option.asSize(this.options.minHeight, '');
this._updateSize(); this._updateSize();
// if the root panel or any of its childs is resized, repaint again, // if the root panel or any of its childs is resized, repaint again,
@ -5034,8 +5035,12 @@ TimeAxis.prototype._repaintLine = function() {
* @private * @private
*/ */
TimeAxis.prototype._calculateCharSize = function () { TimeAxis.prototype._calculateCharSize = function () {
// Note: We only calculate char size once, but in case it is calculated as zero,
// we will recalculate. This is the case if any of the timelines parents
// has display:none for example.
// determine the char width and height on the minor axis // determine the char width and height on the minor axis
if (!('minorCharHeight' in this.props)) {
if (!('minorCharHeight' in this.props) || this.props.minorCharHeight == 0) {
var textMinor = document.createTextNode('0'); var textMinor = document.createTextNode('0');
var measureCharMinor = document.createElement('DIV'); var measureCharMinor = document.createElement('DIV');
measureCharMinor.className = 'text minor measure'; measureCharMinor.className = 'text minor measure';
@ -5048,7 +5053,8 @@ TimeAxis.prototype._calculateCharSize = function () {
this.frame.removeChild(measureCharMinor); this.frame.removeChild(measureCharMinor);
} }
if (!('majorCharHeight' in this.props)) {
// determine the char width and height on the major axis
if (!('majorCharHeight' in this.props) || this.props.majorCharHeight == 0) {
var textMajor = document.createTextNode('0'); var textMajor = document.createTextNode('0');
var measureCharMajor = document.createElement('DIV'); var measureCharMajor = document.createElement('DIV');
measureCharMajor.className = 'text major measure'; measureCharMajor.className = 'text major measure';
@ -7744,6 +7750,14 @@ Group.prototype._create = function() {
this.dom.background = document.createElement('div'); this.dom.background = document.createElement('div');
this.dom.axis = document.createElement('div'); this.dom.axis = document.createElement('div');
// create a hidden marker to detect when the Timelines container is attached
// to the DOM, or the style of a parent of the Timeline is changed from
// display:none is changed to visible.
this.dom.marker = document.createElement('div');
this.dom.marker.style.visibility = 'hidden';
this.dom.marker.innerHTML = '?';
this.dom.background.appendChild(this.dom.marker);
}; };
/** /**
@ -7815,6 +7829,20 @@ Group.prototype.repaint = function repaint(range, margin, restack) {
this.visibleItems = this._updateVisibleItems(this.orderedItems, this.visibleItems, range); this.visibleItems = this._updateVisibleItems(this.orderedItems, this.visibleItems, range);
// force recalculation of the height of the items when the marker height changed
// (due to the Timeline being attached to the DOM or changed from display:none to visible)
var markerHeight = this.dom.marker.clientHeight;
if (markerHeight != this.lastMarkerHeight) {
this.lastMarkerHeight = markerHeight;
util.forEach(this.items, function (item) {
item.dirty = true;
if (item.displayed) item.repaint();
});
restack = true;
}
// reposition visible items vertically // reposition visible items vertically
if (this.itemSet.options.stack) { // TODO: ugly way to access options... if (this.itemSet.options.stack) { // TODO: ugly way to access options...
stack.stack(this.visibleItems, margin, restack); stack.stack(this.visibleItems, margin, restack);
@ -7822,7 +7850,6 @@ Group.prototype.repaint = function repaint(range, margin, restack) {
else { // no stacking else { // no stacking
stack.nostack(this.visibleItems, margin); stack.nostack(this.visibleItems, margin);
} }
this.stackDirty = false;
for (var i = 0, ii = this.visibleItems.length; i < ii; i++) { for (var i = 0, ii = this.visibleItems.length; i < ii; i++) {
var item = this.visibleItems[i]; var item = this.visibleItems[i];
item.repositionY(); item.repositionY();
@ -8690,7 +8717,7 @@ Timeline.prototype.getSelection = function getSelection() {
* Where start and end can be a Date, number, or string, and range is an * Where start and end can be a Date, number, or string, and range is an
* object with properties start and end. * object with properties start and end.
* *
* @param {Date | Number | String} [start] Start date of visible window
* @param {Date | Number | String | Object} [start] Start date of visible window
* @param {Date | Number | String} [end] End date of visible window * @param {Date | Number | String} [end] End date of visible window
*/ */
Timeline.prototype.setWindow = function setWindow(start, end) { Timeline.prototype.setWindow = function setWindow(start, end) {
@ -8715,6 +8742,14 @@ Timeline.prototype.getWindow = function setWindow() {
}; };
}; };
/**
* Force a repaint of the Timeline. Can be useful to manually repaint when
* option autoResize=false
*/
Timeline.prototype.repaint = function repaint() {
this.rootPanel.repaint();
};
/** /**
* Handle selecting/deselecting an item when tapping it * Handle selecting/deselecting an item when tapping it
* @param {Event} event * @param {Event} event
@ -8781,6 +8816,11 @@ Timeline.prototype._onAddItem = function (event) {
content: 'new item' content: 'new item'
}; };
// when default type is a range, add a default end date to the new item
if (this.options.type === 'range' || this.options.type == 'rangeoverflow') {
newItem.end = this.timeAxis.snap(this._toTime(x + this.rootPanel.width / 5));
}
var id = util.randomUUID(); var id = util.randomUUID();
newItem[this.itemsData.fieldId] = id; newItem[this.itemsData.fieldId] = id;
@ -13694,10 +13734,10 @@ var manipulationMixin = {
this.cachedFunctions["_handleOnDrag"] = this._handleOnDrag; this.cachedFunctions["_handleOnDrag"] = this._handleOnDrag;
this._handleOnDrag = function(event) { this._handleOnDrag = function(event) {
var pointer = this._getPointer(event.gesture.center); var pointer = this._getPointer(event.gesture.center);
this.sectors['support']['nodes']['targetNode'].x = this._canvasToX(pointer.x);
this.sectors['support']['nodes']['targetNode'].y = this._canvasToY(pointer.y);
this.sectors['support']['nodes']['targetViaNode'].x = 0.5 * (this._canvasToX(pointer.x) + this.edges['connectionEdge'].from.x);
this.sectors['support']['nodes']['targetViaNode'].y = this._canvasToY(pointer.y);
this.sectors['support']['nodes']['targetNode'].x = this._XconvertDOMtoCanvas(pointer.x);
this.sectors['support']['nodes']['targetNode'].y = this._YconvertDOMtoCanvas(pointer.y);
this.sectors['support']['nodes']['targetViaNode'].x = 0.5 * (this._XconvertDOMtoCanvas(pointer.x) + this.edges['connectionEdge'].from.x);
this.sectors['support']['nodes']['targetViaNode'].y = this._YconvertDOMtoCanvas(pointer.y);
}; };
this.moving = true; this.moving = true;
@ -15620,8 +15660,8 @@ var SelectionMixin = {
* @private * @private
*/ */
_pointerToPositionObject : function(pointer) { _pointerToPositionObject : function(pointer) {
var x = this._canvasToX(pointer.x);
var y = this._canvasToY(pointer.y);
var x = this._XconvertDOMtoCanvas(pointer.x);
var y = this._YconvertDOMtoCanvas(pointer.y);
return {left: x, return {left: x,
top: y, top: y,
@ -16013,8 +16053,8 @@ var SelectionMixin = {
var node = this._getNodeAt(pointer); var node = this._getNodeAt(pointer);
if (node != null && node !== undefined) { if (node != null && node !== undefined) {
// we reset the areaCenter here so the opening of the node will occur // we reset the areaCenter here so the opening of the node will occur
this.areaCenter = {"x" : this._canvasToX(pointer.x),
"y" : this._canvasToY(pointer.y)};
this.areaCenter = {"x" : this._XconvertDOMtoCanvas(pointer.x),
"y" : this._YconvertDOMtoCanvas(pointer.y)};
this.openCluster(node); this.openCluster(node);
} }
this.emit("doubleClick", this.getSelection()); this.emit("doubleClick", this.getSelection());
@ -17513,11 +17553,11 @@ Graph.prototype._handleOnDrag = function(event) {
var node = s.node; var node = s.node;
if (!s.xFixed) { if (!s.xFixed) {
node.x = me._canvasToX(me._xToCanvas(s.x) + deltaX);
node.x = me._XconvertDOMtoCanvas(me._XconvertCanvasToDOM(s.x) + deltaX);
} }
if (!s.yFixed) { if (!s.yFixed) {
node.y = me._canvasToY(me._yToCanvas(s.y) + deltaY);
node.y = me._YconvertDOMtoCanvas(me._YconvertCanvasToDOM(s.y) + deltaY);
} }
}); });
@ -17643,8 +17683,8 @@ Graph.prototype._zoom = function(scale, pointer) {
var tx = (1 - scaleFrac) * pointer.x + translation.x * scaleFrac; var tx = (1 - scaleFrac) * pointer.x + translation.x * scaleFrac;
var ty = (1 - scaleFrac) * pointer.y + translation.y * scaleFrac; var ty = (1 - scaleFrac) * pointer.y + translation.y * scaleFrac;
this.areaCenter = {"x" : this._canvasToX(pointer.x),
"y" : this._canvasToY(pointer.y)};
this.areaCenter = {"x" : this._XconvertDOMtoCanvas(pointer.x),
"y" : this._YconvertDOMtoCanvas(pointer.y)};
this._setScale(scale); this._setScale(scale);
this._setTranslation(tx, ty); this._setTranslation(tx, ty);
@ -17745,10 +17785,10 @@ Graph.prototype._onMouseMoveTitle = function (event) {
*/ */
Graph.prototype._checkShowPopup = function (pointer) { Graph.prototype._checkShowPopup = function (pointer) {
var obj = { var obj = {
left: this._canvasToX(pointer.x),
top: this._canvasToY(pointer.y),
right: this._canvasToX(pointer.x),
bottom: this._canvasToY(pointer.y)
left: this._XconvertDOMtoCanvas(pointer.x),
top: this._YconvertDOMtoCanvas(pointer.y),
right: this._XconvertDOMtoCanvas(pointer.x),
bottom: this._YconvertDOMtoCanvas(pointer.y)
}; };
var id; var id;
@ -18212,12 +18252,12 @@ Graph.prototype._redraw = function() {
ctx.scale(this.scale, this.scale); ctx.scale(this.scale, this.scale);
this.canvasTopLeft = { this.canvasTopLeft = {
"x": this._canvasToX(0),
"y": this._canvasToY(0)
"x": this._XconvertDOMtoCanvas(0),
"y": this._YconvertDOMtoCanvas(0)
}; };
this.canvasBottomRight = { this.canvasBottomRight = {
"x": this._canvasToX(this.frame.canvas.clientWidth),
"y": this._canvasToY(this.frame.canvas.clientHeight)
"x": this._XconvertDOMtoCanvas(this.frame.canvas.clientWidth),
"y": this._YconvertDOMtoCanvas(this.frame.canvas.clientHeight)
}; };
this._doInAllSectors("_drawAllSectorNodes",ctx); this._doInAllSectors("_drawAllSectorNodes",ctx);
@ -18286,42 +18326,46 @@ Graph.prototype._getScale = function() {
}; };
/** /**
* Convert a horizontal point on the HTML canvas to the x-value of the model
* Convert the X coordinate in DOM-space (coordinate point in browser relative to the container div) to
* the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon)
* @param {number} x * @param {number} x
* @returns {number} * @returns {number}
* @private * @private
*/ */
Graph.prototype._canvasToX = function(x) {
Graph.prototype._XconvertDOMtoCanvas = function(x) {
return (x - this.translation.x) / this.scale; return (x - this.translation.x) / this.scale;
}; };
/** /**
* Convert an x-value in the model to a horizontal point on the HTML canvas
* Convert the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to
* the X coordinate in DOM-space (coordinate point in browser relative to the container div)
* @param {number} x * @param {number} x
* @returns {number} * @returns {number}
* @private * @private
*/ */
Graph.prototype._xToCanvas = function(x) {
Graph.prototype._XconvertCanvasToDOM = function(x) {
return x * this.scale + this.translation.x; return x * this.scale + this.translation.x;
}; };
/** /**
* Convert a vertical point on the HTML canvas to the y-value of the model
* Convert the Y coordinate in DOM-space (coordinate point in browser relative to the container div) to
* the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon)
* @param {number} y * @param {number} y
* @returns {number} * @returns {number}
* @private * @private
*/ */
Graph.prototype._canvasToY = function(y) {
Graph.prototype._YconvertDOMtoCanvas = function(y) {
return (y - this.translation.y) / this.scale; return (y - this.translation.y) / this.scale;
}; };
/** /**
* Convert an y-value in the model to a vertical point on the HTML canvas
* Convert the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to
* the Y coordinate in DOM-space (coordinate point in browser relative to the container div)
* @param {number} y * @param {number} y
* @returns {number} * @returns {number}
* @private * @private
*/ */
Graph.prototype._yToCanvas = function(y) {
Graph.prototype._YconvertCanvasToDOM = function(y) {
return y * this.scale + this.translation.y ; return y * this.scale + this.translation.y ;
}; };
@ -18333,7 +18377,7 @@ Graph.prototype._yToCanvas = function(y) {
* @constructor * @constructor
*/ */
Graph.prototype.canvasToDOM = function(pos) { Graph.prototype.canvasToDOM = function(pos) {
return {x:this._xToCanvas(pos.x),y:this._yToCanvas(pos.y)};
return {x:this._XconvertCanvasToDOM(pos.x),y:this._YconvertCanvasToDOM(pos.y)};
} }
/** /**
@ -18343,7 +18387,7 @@ Graph.prototype.canvasToDOM = function(pos) {
* @constructor * @constructor
*/ */
Graph.prototype.DOMtoCanvas = function(pos) { Graph.prototype.DOMtoCanvas = function(pos) {
return {x:this._canvasToX(pos.x),y:this._canvasToY(pos.y)};
return {x:this._XconvertDOMtoCanvas(pos.x),y:this._YconvertDOMtoCanvas(pos.y)};
} }
/** /**
@ -18753,6 +18797,7 @@ Graph.prototype.storePosition = function() {
*/ */
var vis = { var vis = {
util: util, util: util,
moment: moment,
DataSet: DataSet, DataSet: DataSet,
DataView: DataView, DataView: DataView,

+ 7
- 7
dist/vis.min.js
File diff suppressed because it is too large
View File


+ 19
- 7
docs/timeline.html View File

@ -341,8 +341,7 @@ var options = {
<td>autoResize</td> <td>autoResize</td>
<td>boolean</td> <td>boolean</td>
<td>true</td> <td>true</td>
<td>If true, the Timeline will automatically detect when its
container is resized, and redraw itself accordingly.</td>
<td>If true, the Timeline will automatically detect when its container is resized, and redraw itself accordingly. If false, the Timeline can be forced to repaint after its container has been resized using the function <code>repaint()</code>.</td>
</tr> </tr>
<tr> <tr>
@ -402,7 +401,7 @@ var options = {
<tr> <tr>
<td>height</td> <td>height</td>
<td>String</td>
<td>Number | String</td>
<td>none</td> <td>none</td>
<td>The height of the timeline in pixels or as a percentage. <td>The height of the timeline in pixels or as a percentage.
When height is undefined or null, the height of the timeline is automatically When height is undefined or null, the height of the timeline is automatically
@ -438,10 +437,9 @@ var options = {
<tr> <tr>
<td>maxHeight</td> <td>maxHeight</td>
<td>Number</td>
<td>Number | String</td>
<td>none</td> <td>none</td>
<td>Specifies a maximum height for the Timeline in pixels.
</td>
<td>Specifies the maximum height for the Timeline. Can be a number in pixels or a string like "300px".</td>
</tr> </tr>
<tr> <tr>
@ -453,6 +451,13 @@ var options = {
</td> </td>
</tr> </tr>
<tr>
<td>minHeight</td>
<td>Number | String</td>
<td>none</td>
<td>Specifies the minimum height for the Timeline. Can be a number in pixels or a string like "300px".</td>
</tr>
<tr> <tr>
<td>onAdd</td> <td>onAdd</td>
<td>Function</td> <td>Function</td>
@ -583,7 +588,7 @@ var options = {
<td>type</td> <td>type</td>
<td>String</td> <td>String</td>
<td>'box'</td> <td>'box'</td>
<td>Specifies the type for the timeline items. Choose from 'box', 'point', 'range', and 'rangeoverflow'. Note that individual items can override this global type.
<td>Specifies the default type for the timeline items. Choose from 'box', 'point', 'range', and 'rangeoverflow'. Note that individual items can override this default type.
</td> </td>
</tr> </tr>
@ -673,6 +678,13 @@ var options = {
<td>Remove an event listener created before via function <code>on(event, callback)</code>. See section <a href="#Events">Events for more information</a>.</td> <td>Remove an event listener created before via function <code>on(event, callback)</code>. See section <a href="#Events">Events for more information</a>.</td>
</tr> </tr>
<tr>
<td>repaint()</td>
<td>none</td>
<td>Force a repaint of the Timeline. Can be useful to manually repaint when option autoResize=false.
</td>
</tr>
<tr> <tr>
<td>setGroups(groups)</td> <td>setGroups(groups)</td>
<td>none</td> <td>none</td>

+ 88
- 0
examples/timeline/16_navigation_menu.html View File

@ -0,0 +1,88 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Timeline | navigation menu</title>
<style type="text/css">
body, html, input {
font-family: sans-serif;
font-size: 12pt;
}
#visualization {
position: relative;
}
.menu {
position: absolute;
top: 0;
right: 0;
margin: 10px;
z-index: 9999;
}
</style>
<script src="../../dist/vis.js"></script>
<link href="../../dist/vis.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="visualization">
<div class="menu">
<input type="button" id="zoomIn" value="Zoom in"/>
<input type="button" id="zoomOut" value="Zoom out"/>
<input type="button" id="moveLeft" value="Move left"/>
<input type="button" id="moveRight" value="Move right"/>
</div>
</div>
<script type="text/javascript">
// create a timeline with some data
var container = document.getElementById('visualization');
var items = [
{id: 1, content: 'item 1', start: '2014-04-20'},
{id: 2, content: 'item 2', start: '2014-04-14'},
{id: 3, content: 'item 3', start: '2014-04-18'},
{id: 4, content: 'item 4', start: '2014-04-16', end: '2014-04-19'},
{id: 5, content: 'item 5', start: '2014-04-25'},
{id: 6, content: 'item 6', start: '2014-04-27', type: 'point'}
];
var options = {};
var timeline = new vis.Timeline(container, items, options);
/**
* Move the timeline a given percentage to left or right
* @param {Number} percentage For example 0.1 (left) or -0.1 (right)
*/
function move (percentage) {
var range = timeline.getWindow();
var interval = range.end - range.start;
timeline.setWindow({
start: range.start.valueOf() - interval * percentage,
end: range.end.valueOf() - interval * percentage
});
}
/**
* Zoom the timeline a given percentage in or out
* @param {Number} percentage For example 0.1 (zoom out) or -0.1 (zoom in)
*/
function zoom (percentage) {
var range = timeline.getWindow();
var interval = range.end - range.start;
timeline.setWindow({
start: range.start.valueOf() - interval * percentage,
end: range.end.valueOf() + interval * percentage
});
}
// attach events to the navigation buttons
document.getElementById('zoomIn').onclick = function () { zoom(-0.2); };
document.getElementById('zoomOut').onclick = function () { zoom( 0.2); };
document.getElementById('moveLeft').onclick = function () { move( 0.2); };
document.getElementById('moveRight').onclick = function () { move(-0.2); };
</script>
</body>
</html>

+ 1
- 0
examples/timeline/index.html View File

@ -27,6 +27,7 @@
<p><a href="13_past_and_future.html">13_past_and_future.html</a></p> <p><a href="13_past_and_future.html">13_past_and_future.html</a></p>
<p><a href="14_a_lot_of_grouped_data.html">14_a_lot_of_grouped_data.html</a></p> <p><a href="14_a_lot_of_grouped_data.html">14_a_lot_of_grouped_data.html</a></p>
<p><a href="15_item_class_names.html">15_item_class_names.html</a></p> <p><a href="15_item_class_names.html">15_item_class_names.html</a></p>
<p><a href="16_navigation_menu.html">16_navigation_menu.html</a></p>
<p><a href="requirejs/requirejs_example.html">requirejs_example.html</a></p> <p><a href="requirejs/requirejs_example.html">requirejs_example.html</a></p>

+ 26
- 22
src/graph/Graph.js View File

@ -956,11 +956,11 @@ Graph.prototype._handleOnDrag = function(event) {
var node = s.node; var node = s.node;
if (!s.xFixed) { if (!s.xFixed) {
node.x = me._canvasToX(me._xToCanvas(s.x) + deltaX);
node.x = me._XconvertDOMtoCanvas(me._XconvertCanvasToDOM(s.x) + deltaX);
} }
if (!s.yFixed) { if (!s.yFixed) {
node.y = me._canvasToY(me._yToCanvas(s.y) + deltaY);
node.y = me._YconvertDOMtoCanvas(me._YconvertCanvasToDOM(s.y) + deltaY);
} }
}); });
@ -1086,8 +1086,8 @@ Graph.prototype._zoom = function(scale, pointer) {
var tx = (1 - scaleFrac) * pointer.x + translation.x * scaleFrac; var tx = (1 - scaleFrac) * pointer.x + translation.x * scaleFrac;
var ty = (1 - scaleFrac) * pointer.y + translation.y * scaleFrac; var ty = (1 - scaleFrac) * pointer.y + translation.y * scaleFrac;
this.areaCenter = {"x" : this._canvasToX(pointer.x),
"y" : this._canvasToY(pointer.y)};
this.areaCenter = {"x" : this._XconvertDOMtoCanvas(pointer.x),
"y" : this._YconvertDOMtoCanvas(pointer.y)};
this._setScale(scale); this._setScale(scale);
this._setTranslation(tx, ty); this._setTranslation(tx, ty);
@ -1188,10 +1188,10 @@ Graph.prototype._onMouseMoveTitle = function (event) {
*/ */
Graph.prototype._checkShowPopup = function (pointer) { Graph.prototype._checkShowPopup = function (pointer) {
var obj = { var obj = {
left: this._canvasToX(pointer.x),
top: this._canvasToY(pointer.y),
right: this._canvasToX(pointer.x),
bottom: this._canvasToY(pointer.y)
left: this._XconvertDOMtoCanvas(pointer.x),
top: this._YconvertDOMtoCanvas(pointer.y),
right: this._XconvertDOMtoCanvas(pointer.x),
bottom: this._YconvertDOMtoCanvas(pointer.y)
}; };
var id; var id;
@ -1655,12 +1655,12 @@ Graph.prototype._redraw = function() {
ctx.scale(this.scale, this.scale); ctx.scale(this.scale, this.scale);
this.canvasTopLeft = { this.canvasTopLeft = {
"x": this._canvasToX(0),
"y": this._canvasToY(0)
"x": this._XconvertDOMtoCanvas(0),
"y": this._YconvertDOMtoCanvas(0)
}; };
this.canvasBottomRight = { this.canvasBottomRight = {
"x": this._canvasToX(this.frame.canvas.clientWidth),
"y": this._canvasToY(this.frame.canvas.clientHeight)
"x": this._XconvertDOMtoCanvas(this.frame.canvas.clientWidth),
"y": this._YconvertDOMtoCanvas(this.frame.canvas.clientHeight)
}; };
this._doInAllSectors("_drawAllSectorNodes",ctx); this._doInAllSectors("_drawAllSectorNodes",ctx);
@ -1729,42 +1729,46 @@ Graph.prototype._getScale = function() {
}; };
/** /**
* Convert a horizontal point on the HTML canvas to the x-value of the model
* Convert the X coordinate in DOM-space (coordinate point in browser relative to the container div) to
* the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon)
* @param {number} x * @param {number} x
* @returns {number} * @returns {number}
* @private * @private
*/ */
Graph.prototype._canvasToX = function(x) {
Graph.prototype._XconvertDOMtoCanvas = function(x) {
return (x - this.translation.x) / this.scale; return (x - this.translation.x) / this.scale;
}; };
/** /**
* Convert an x-value in the model to a horizontal point on the HTML canvas
* Convert the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to
* the X coordinate in DOM-space (coordinate point in browser relative to the container div)
* @param {number} x * @param {number} x
* @returns {number} * @returns {number}
* @private * @private
*/ */
Graph.prototype._xToCanvas = function(x) {
Graph.prototype._XconvertCanvasToDOM = function(x) {
return x * this.scale + this.translation.x; return x * this.scale + this.translation.x;
}; };
/** /**
* Convert a vertical point on the HTML canvas to the y-value of the model
* Convert the Y coordinate in DOM-space (coordinate point in browser relative to the container div) to
* the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon)
* @param {number} y * @param {number} y
* @returns {number} * @returns {number}
* @private * @private
*/ */
Graph.prototype._canvasToY = function(y) {
Graph.prototype._YconvertDOMtoCanvas = function(y) {
return (y - this.translation.y) / this.scale; return (y - this.translation.y) / this.scale;
}; };
/** /**
* Convert an y-value in the model to a vertical point on the HTML canvas
* Convert the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to
* the Y coordinate in DOM-space (coordinate point in browser relative to the container div)
* @param {number} y * @param {number} y
* @returns {number} * @returns {number}
* @private * @private
*/ */
Graph.prototype._yToCanvas = function(y) {
Graph.prototype._YconvertCanvasToDOM = function(y) {
return y * this.scale + this.translation.y ; return y * this.scale + this.translation.y ;
}; };
@ -1776,7 +1780,7 @@ Graph.prototype._yToCanvas = function(y) {
* @constructor * @constructor
*/ */
Graph.prototype.canvasToDOM = function(pos) { Graph.prototype.canvasToDOM = function(pos) {
return {x:this._xToCanvas(pos.x),y:this._yToCanvas(pos.y)};
return {x:this._XconvertCanvasToDOM(pos.x),y:this._YconvertCanvasToDOM(pos.y)};
} }
/** /**
@ -1786,7 +1790,7 @@ Graph.prototype.canvasToDOM = function(pos) {
* @constructor * @constructor
*/ */
Graph.prototype.DOMtoCanvas = function(pos) { Graph.prototype.DOMtoCanvas = function(pos) {
return {x:this._canvasToX(pos.x),y:this._canvasToY(pos.y)};
return {x:this._XconvertDOMtoCanvas(pos.x),y:this._YconvertDOMtoCanvas(pos.y)};
} }
/** /**

+ 4
- 4
src/graph/graphMixins/ManipulationMixin.js View File

@ -243,10 +243,10 @@ var manipulationMixin = {
this.cachedFunctions["_handleOnDrag"] = this._handleOnDrag; this.cachedFunctions["_handleOnDrag"] = this._handleOnDrag;
this._handleOnDrag = function(event) { this._handleOnDrag = function(event) {
var pointer = this._getPointer(event.gesture.center); var pointer = this._getPointer(event.gesture.center);
this.sectors['support']['nodes']['targetNode'].x = this._canvasToX(pointer.x);
this.sectors['support']['nodes']['targetNode'].y = this._canvasToY(pointer.y);
this.sectors['support']['nodes']['targetViaNode'].x = 0.5 * (this._canvasToX(pointer.x) + this.edges['connectionEdge'].from.x);
this.sectors['support']['nodes']['targetViaNode'].y = this._canvasToY(pointer.y);
this.sectors['support']['nodes']['targetNode'].x = this._XconvertDOMtoCanvas(pointer.x);
this.sectors['support']['nodes']['targetNode'].y = this._YconvertDOMtoCanvas(pointer.y);
this.sectors['support']['nodes']['targetViaNode'].x = 0.5 * (this._XconvertDOMtoCanvas(pointer.x) + this.edges['connectionEdge'].from.x);
this.sectors['support']['nodes']['targetViaNode'].y = this._YconvertDOMtoCanvas(pointer.y);
}; };
this.moving = true; this.moving = true;

+ 4
- 4
src/graph/graphMixins/SelectionMixin.js View File

@ -40,8 +40,8 @@ var SelectionMixin = {
* @private * @private
*/ */
_pointerToPositionObject : function(pointer) { _pointerToPositionObject : function(pointer) {
var x = this._canvasToX(pointer.x);
var y = this._canvasToY(pointer.y);
var x = this._XconvertDOMtoCanvas(pointer.x);
var y = this._YconvertDOMtoCanvas(pointer.y);
return {left: x, return {left: x,
top: y, top: y,
@ -433,8 +433,8 @@ var SelectionMixin = {
var node = this._getNodeAt(pointer); var node = this._getNodeAt(pointer);
if (node != null && node !== undefined) { if (node != null && node !== undefined) {
// we reset the areaCenter here so the opening of the node will occur // we reset the areaCenter here so the opening of the node will occur
this.areaCenter = {"x" : this._canvasToX(pointer.x),
"y" : this._canvasToY(pointer.y)};
this.areaCenter = {"x" : this._XconvertDOMtoCanvas(pointer.x),
"y" : this._YconvertDOMtoCanvas(pointer.y)};
this.openCluster(node); this.openCluster(node);
} }
this.emit("doubleClick", this.getSelection()); this.emit("doubleClick", this.getSelection());

+ 1
- 0
src/module/exports.js View File

@ -3,6 +3,7 @@
*/ */
var vis = { var vis = {
util: util, util: util,
moment: moment,
DataSet: DataSet, DataSet: DataSet,
DataView: DataView, DataView: DataView,

+ 14
- 1
src/timeline/Timeline.js View File

@ -544,7 +544,7 @@ Timeline.prototype.getSelection = function getSelection() {
* Where start and end can be a Date, number, or string, and range is an * Where start and end can be a Date, number, or string, and range is an
* object with properties start and end. * object with properties start and end.
* *
* @param {Date | Number | String} [start] Start date of visible window
* @param {Date | Number | String | Object} [start] Start date of visible window
* @param {Date | Number | String} [end] End date of visible window * @param {Date | Number | String} [end] End date of visible window
*/ */
Timeline.prototype.setWindow = function setWindow(start, end) { Timeline.prototype.setWindow = function setWindow(start, end) {
@ -569,6 +569,14 @@ Timeline.prototype.getWindow = function setWindow() {
}; };
}; };
/**
* Force a repaint of the Timeline. Can be useful to manually repaint when
* option autoResize=false
*/
Timeline.prototype.repaint = function repaint() {
this.rootPanel.repaint();
};
/** /**
* Handle selecting/deselecting an item when tapping it * Handle selecting/deselecting an item when tapping it
* @param {Event} event * @param {Event} event
@ -635,6 +643,11 @@ Timeline.prototype._onAddItem = function (event) {
content: 'new item' content: 'new item'
}; };
// when default type is a range, add a default end date to the new item
if (this.options.type === 'range' || this.options.type == 'rangeoverflow') {
newItem.end = this.timeAxis.snap(this._toTime(x + this.rootPanel.width / 5));
}
var id = util.randomUUID(); var id = util.randomUUID();
newItem[this.itemsData.fieldId] = id; newItem[this.itemsData.fieldId] = id;

+ 22
- 1
src/timeline/component/Group.js View File

@ -51,6 +51,14 @@ Group.prototype._create = function() {
this.dom.background = document.createElement('div'); this.dom.background = document.createElement('div');
this.dom.axis = document.createElement('div'); this.dom.axis = document.createElement('div');
// create a hidden marker to detect when the Timelines container is attached
// to the DOM, or the style of a parent of the Timeline is changed from
// display:none is changed to visible.
this.dom.marker = document.createElement('div');
this.dom.marker.style.visibility = 'hidden';
this.dom.marker.innerHTML = '?';
this.dom.background.appendChild(this.dom.marker);
}; };
/** /**
@ -122,6 +130,20 @@ Group.prototype.repaint = function repaint(range, margin, restack) {
this.visibleItems = this._updateVisibleItems(this.orderedItems, this.visibleItems, range); this.visibleItems = this._updateVisibleItems(this.orderedItems, this.visibleItems, range);
// force recalculation of the height of the items when the marker height changed
// (due to the Timeline being attached to the DOM or changed from display:none to visible)
var markerHeight = this.dom.marker.clientHeight;
if (markerHeight != this.lastMarkerHeight) {
this.lastMarkerHeight = markerHeight;
util.forEach(this.items, function (item) {
item.dirty = true;
if (item.displayed) item.repaint();
});
restack = true;
}
// reposition visible items vertically // reposition visible items vertically
if (this.itemSet.options.stack) { // TODO: ugly way to access options... if (this.itemSet.options.stack) { // TODO: ugly way to access options...
stack.stack(this.visibleItems, margin, restack); stack.stack(this.visibleItems, margin, restack);
@ -129,7 +151,6 @@ Group.prototype.repaint = function repaint(range, margin, restack) {
else { // no stacking else { // no stacking
stack.nostack(this.visibleItems, margin); stack.nostack(this.visibleItems, margin);
} }
this.stackDirty = false;
for (var i = 0, ii = this.visibleItems.length; i < ii; i++) { for (var i = 0, ii = this.visibleItems.length; i < ii; i++) {
var item = this.visibleItems[i]; var item = this.visibleItems[i];
item.repositionY(); item.repositionY();

+ 1
- 0
src/timeline/component/RootPanel.js View File

@ -101,6 +101,7 @@ RootPanel.prototype.repaint = function repaint() {
// update frame size // update frame size
this.frame.style.maxHeight = util.option.asSize(this.options.maxHeight, ''); this.frame.style.maxHeight = util.option.asSize(this.options.maxHeight, '');
this.frame.style.minHeight = util.option.asSize(this.options.minHeight, '');
this._updateSize(); this._updateSize();
// if the root panel or any of its childs is resized, repaint again, // if the root panel or any of its childs is resized, repaint again,

+ 7
- 2
src/timeline/component/TimeAxis.js View File

@ -406,8 +406,12 @@ TimeAxis.prototype._repaintLine = function() {
* @private * @private
*/ */
TimeAxis.prototype._calculateCharSize = function () { TimeAxis.prototype._calculateCharSize = function () {
// Note: We only calculate char size once, but in case it is calculated as zero,
// we will recalculate. This is the case if any of the timelines parents
// has display:none for example.
// determine the char width and height on the minor axis // determine the char width and height on the minor axis
if (!('minorCharHeight' in this.props)) {
if (!('minorCharHeight' in this.props) || this.props.minorCharHeight == 0) {
var textMinor = document.createTextNode('0'); var textMinor = document.createTextNode('0');
var measureCharMinor = document.createElement('DIV'); var measureCharMinor = document.createElement('DIV');
measureCharMinor.className = 'text minor measure'; measureCharMinor.className = 'text minor measure';
@ -420,7 +424,8 @@ TimeAxis.prototype._calculateCharSize = function () {
this.frame.removeChild(measureCharMinor); this.frame.removeChild(measureCharMinor);
} }
if (!('majorCharHeight' in this.props)) {
// determine the char width and height on the major axis
if (!('majorCharHeight' in this.props) || this.props.majorCharHeight == 0) {
var textMajor = document.createTextNode('0'); var textMajor = document.createTextNode('0');
var measureCharMajor = document.createElement('DIV'); var measureCharMajor = document.createElement('DIV');
measureCharMajor.className = 'text major measure'; measureCharMajor.className = 'text major measure';

+ 4
- 2
test/timeline.html View File

@ -69,9 +69,11 @@
{_id: 1, content: 'item 1<br>start', start: now.clone().add('days', 4).toDate()}, {_id: 1, content: 'item 1<br>start', start: now.clone().add('days', 4).toDate()},
{_id: 2, content: 'item 2', start: now.clone().add('days', -2).toDate() }, {_id: 2, content: 'item 2', start: now.clone().add('days', -2).toDate() },
{_id: 3, content: 'item 3', start: now.clone().add('days', 2).toDate()}, {_id: 3, content: 'item 3', start: now.clone().add('days', 2).toDate()},
{_id: 4, content: 'item 4',
{
_id: 4, content: 'item 4',
start: now.clone().add('days', 0).toDate(), start: now.clone().add('days', 0).toDate(),
end: now.clone().add('days', 7).toDate()},
end: now.clone().add('days', 7).toDate()
},
{_id: 5, content: 'item 5', start: now.clone().add('days', 9).toDate(), type:'point'}, {_id: 5, content: 'item 5', start: now.clone().add('days', 9).toDate(), type:'point'},
{_id: 6, content: 'item 6', start: now.clone().add('days', 11).toDate()} {_id: 6, content: 'item 6', start: now.clone().add('days', 11).toDate()}
]); ]);

Loading…
Cancel
Save