Browse Source

Merge branch 'develop' into es6

flowchartTest
jos 9 years ago
parent
commit
7b55f66094
22 changed files with 10924 additions and 10617 deletions
  1. +33
    -0
      HISTORY.md
  2. +1
    -1
      bower.json
  3. +10557
    -10532
      dist/vis.js
  4. +1
    -1
      dist/vis.map
  5. +1
    -1
      dist/vis.min.css
  6. +16
    -16
      dist/vis.min.js
  7. +24
    -0
      docs/dataset.html
  8. +104
    -2
      docs/dataview.html
  9. +16
    -0
      docs/graph2d.html
  10. +18
    -10
      docs/network.html
  11. +20
    -0
      docs/timeline.html
  12. +5
    -0
      lib/DataSet.js
  13. +5
    -0
      lib/DataView.js
  14. +1
    -1
      lib/network/Edge.js
  15. +58
    -34
      lib/network/Network.js
  16. +3
    -3
      lib/network/Node.js
  17. +12
    -13
      lib/timeline/TimeStep.js
  18. +7
    -2
      lib/timeline/component/TimeAxis.js
  19. +1
    -1
      package.json
  20. +9
    -0
      test/DataSet.test.js
  21. +28
    -0
      test/DataView.test.js
  22. +4
    -0
      test/timeline.html

+ 33
- 0
HISTORY.md View File

@ -2,6 +2,39 @@
http://visjs.org http://visjs.org
## not yet released, version 3.9.2-SNAPSHOT
### Network
- Added option bindToWindow (default true) to choose whether the keyboard binds are global or to the network div.
### DataSet
- Added property `length` holding the total number of items to the `DataSet`
and `DataView`.
### Timeline
- Implemented option `timeAxis: {scale: string, step: number}` to set a
fixed scale.
## 2015-01-16, version 3.9.1
### General
- Fixed wrong distribution file deployed on the website and the downloadable
zip file.
### Network
- Fixed bug where opening a cluster with smoothCurves off caused one child to go crazy.
- Fixed bug where zoomExtent does not work as expected.
- Fixed nodes color data being overridden when having a group and a dataset update query.
- Decoupled animation from physics simulation.
- Fixed scroll being blocked if zoomable is false.
## 2015-01-16, version 3.9.0 ## 2015-01-16, version 3.9.0
### Network ### Network

+ 1
- 1
bower.json View File

@ -1,6 +1,6 @@
{ {
"name": "vis", "name": "vis",
"version": "3.9.0",
"version": "3.9.2-SNAPSHOT",
"main": ["dist/vis.min.js", "dist/vis.min.css"], "main": ["dist/vis.min.js", "dist/vis.min.css"],
"description": "A dynamic, browser-based visualization library.", "description": "A dynamic, browser-based visualization library.",
"homepage": "http://visjs.org/", "homepage": "http://visjs.org/",

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


+ 1
- 1
dist/vis.map
File diff suppressed because it is too large
View File


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


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


+ 24
- 0
docs/dataset.html View File

@ -21,6 +21,7 @@
<li><a href="#Example">Example</a></li> <li><a href="#Example">Example</a></li>
<li><a href="#Construction">Construction</a></li> <li><a href="#Construction">Construction</a></li>
<li><a href="#Methods">Methods</a></li> <li><a href="#Methods">Methods</a></li>
<li><a href="#Properties">Properties</a></li>
<li><a href="#Subscriptions">Subscriptions</a></li> <li><a href="#Subscriptions">Subscriptions</a></li>
<li><a href="#Data_Manipulation">Data Manipulation</a></li> <li><a href="#Data_Manipulation">Data Manipulation</a></li>
<li><a href="#Data_Selection">Data Selection</a></li> <li><a href="#Data_Selection">Data Selection</a></li>
@ -373,6 +374,29 @@ var data = new vis.DataSet([data] [, options])
</table> </table>
<h2 id="Properties">Properties</h2>
<p>DataSet contains the following properties.</p>
<table>
<colgroup>
<col width="200">
</colgroup>
<tr>
<th>Property</th>
<th>Type</th>
<th>Description</th>
</tr>
<tr>
<td>length</td>
<td>Number</td>
<td>The number of items in the DataSet.</td>
</tr>
</table>
<h2 id="Subscriptions">Subscriptions</h2> <h2 id="Subscriptions">Subscriptions</h2>
<p> <p>

+ 104
- 2
docs/dataview.html View File

@ -20,6 +20,8 @@
<li><a href="#Overview">Overview</a></li> <li><a href="#Overview">Overview</a></li>
<li><a href="#Example">Example</a></li> <li><a href="#Example">Example</a></li>
<li><a href="#Construction">Construction</a></li> <li><a href="#Construction">Construction</a></li>
<li><a href="#Methods">Methods</a></li>
<li><a href="#Properties">Properties</a></li>
<li><a href="#Getting_Data">Getting Data</a></li> <li><a href="#Getting_Data">Getting Data</a></li>
<li><a href="#Subscriptions">Subscriptions</a></li> <li><a href="#Subscriptions">Subscriptions</a></li>
<li><a href="#Data_Policy">Data Policy</a></li> <li><a href="#Data_Policy">Data Policy</a></li>
@ -152,6 +154,106 @@ var data = new vis.DataView(dataset, options)
</li> </li>
</ul> </ul>
<h2 id="Methods">Methods</h2>
<p>DataView contains the following methods.</p>
<table>
<colgroup>
<col width="200">
</colgroup>
<tr>
<th>Method</th>
<th>Return Type</th>
<th>Description</th>
</tr>
<tr>
<td>
get([options] [, data])<br>
get(id [,options] [, data])<br>
get(ids [, options] [, data])
</td>
<td>Object | Array | DataTable</td>
<td>
Get a single item, multiple items, or all items from the DataView.
Usage examples can be found in section <a href="#Getting_Data">Getting Data</a>, and the available <code>options</code> are described in section <a href="#Data_Selection">Data Selection</a>. If parameter <code>data</code> is provided, items will be appended to this array or table, which is required in case of Google DataTable.
</td>
</tr>
<tr>
<td>
getDataSet()
</td>
<td>DataSet</td>
<td>
Get the DataSet to which the DataView is connected.
</td>
</tr>
<tr>
<td>
getIds([options])
</td>
<td>Number[]</td>
<td>
Get ids of all items or of a filtered set of items.
Available <code>options</code> are described in section <a href="dataset.html#Data_Selection">Data Selection</a>, except that options <code>fields</code> and <code>type</code> are not applicable in case of <code>getIds</code>.
</td>
</tr>
<tr>
<td>off(event, callback)</td>
<td>none</td>
<td>
Unsubscribe from an event, remove an event listener. See section <a href="#Subscriptions">Subscriptions</a>.
</td>
</tr>
<tr>
<td>on(event, callback)</td>
<td>none</td>
<td>
Subscribe to an event, add an event listener. See section <a href="#Subscriptions">Subscriptions</a>.
</td>
</tr>
<tr>
<td>
setDataSet(data)
</td>
<td>none</td>
<td>
Replace the DataSet of the DataView. Parameter <code>data</code> can be a DataSet or a DataView.
</td>
</tr>
</table>
<h2 id="Properties">Properties</h2>
<p>DataView contains the following properties.</p>
<table>
<colgroup>
<col width="200">
</colgroup>
<tr>
<th>Property</th>
<th>Type</th>
<th>Description</th>
</tr>
<tr>
<td>length</td>
<td>Number</td>
<td>The number of items in the DataView.</td>
</tr>
</table>
<h2 id="Getting_Data">Getting Data</h2> <h2 id="Getting_Data">Getting Data</h2>
<p> <p>
@ -165,8 +267,8 @@ var items = view.get();
<p> <p>
Data of a DataView can be filtered and formatted again, in exactly the Data of a DataView can be filtered and formatted again, in exactly the
same way as in a DataSet. See sections same way as in a DataSet. See sections
<a href="dataset.html#Data_Filtering">Data Filtering</a> and
<a href="dataset.html#Data_Formatting">Data Formatting</a> for more
<a href="dataset.html#Data_Manipulation">Data Manipulation</a> and
<a href="dataset.html#Data_Selection">Data Selection</a> for more
information. information.
</p> </p>

+ 16
- 0
docs/graph2d.html View File

@ -737,6 +737,22 @@ The options colored in green can also be used as options for the groups. All opt
If not provided, the earliest date present in the events is taken as start date.</td> If not provided, the earliest date present in the events is taken as start date.</td>
</tr> </tr>
<tr>
<td>timeAxis.scale</td>
<td>string</td>
<td>none</td>
<td>Set a fixed scale for the time axis of the Timeline. Choose from <code>'millisecond'</code>, <code>'second'</code>, <code>'minute'</code>, <code>'hour'</code>, <code>'weekday'</code>, <code>'day'</code>, <code>'month'</code>, <code>'year'</code>.</td>
</tr>
<tr>
<td>timeAxis.step</td>
<td>number</td>
<td>1</td>
<td>
Set a fixed step size for the time axis. Only applicable when used together with <code>timeAxis.scale</code>.
Choose for example 1, 2, 5, or 10.</td>
</tr>
<tr> <tr>
<td>width</td> <td>width</td>
<td>String</td> <td>String</td>

+ 18
- 10
docs/network.html View File

@ -303,7 +303,8 @@ When using a DataSet, the network is automatically updating to changes in the Da
<td>level</td> <td>level</td>
<td>number</td> <td>number</td>
<td>no</td> <td>no</td>
<td>This level is used in the hierarchical layout. If this is not selected, the level does not do anything.</td>
<td>This level is used in the hierarchical layout. If this is not selected, the level does not do anything. This must be a postive number (min value: 0).
Fractions are possible but only integers are supported.</td>
</tr> </tr>
<tr> <tr>
@ -342,7 +343,7 @@ When using a DataSet, the network is automatically updating to changes in the Da
<td>no</td> <td>no</td>
<td>Horizontal position in pixels. <td>Horizontal position in pixels.
The horizontal position of the node will be fixed unless combined with the allowedToMoveX:true option. The horizontal position of the node will be fixed unless combined with the allowedToMoveX:true option.
The vertical position y may remain undefined.</td>
The vertical position y may remain undefined. This does not work with hierarchical layout.</td>
</tr> </tr>
<tr> <tr>
<td>y</td> <td>y</td>
@ -350,7 +351,7 @@ When using a DataSet, the network is automatically updating to changes in the Da
<td>no</td> <td>no</td>
<td>Vertical position in pixels. <td>Vertical position in pixels.
The vertical position of the node will be fixed unless combined with the allowedToMoveY:true option. The vertical position of the node will be fixed unless combined with the allowedToMoveY:true option.
The horizontal position x may remain undefined.</td>
The horizontal position x may remain undefined. This does not work with hierarchical layout.</td>
</tr> </tr>
</table> </table>
@ -1000,13 +1001,13 @@ All options defined per-node override these global settings.
<td>widthMin</td> <td>widthMin</td>
<td>Number</td> <td>Number</td>
<td>16</td> <td>16</td>
<td>The minimum width for a scaled image. Only applicable to shape <code>image</code>.</td>
<td>The minimum width for a scaled image. Only applicable to shape <code>image</code>. This only does something if you supply a value.</td>
</tr> </tr>
<tr> <tr>
<td>widthMax</td> <td>widthMax</td>
<td>Number</td> <td>Number</td>
<td>64</td> <td>64</td>
<td>The maximum width for a scaled image. Only applicable to shape <code>image</code>.</td>
<td>The maximum width for a scaled image. Only applicable to shape <code>image</code>. This only does something if you supply a value.</td>
</tr> </tr>
<tr> <tr>
@ -1021,14 +1022,14 @@ All options defined per-node override these global settings.
<td>Number</td> <td>Number</td>
<td>10</td> <td>10</td>
<td>The minimum radius for a scaled node. Only applicable to shapes <code>dot</code>, <td>The minimum radius for a scaled node. Only applicable to shapes <code>dot</code>,
<code>star</code>, <code>triangle</code>, <code>triangleDown</code>, and <code>square</code>.</td>
<code>star</code>, <code>triangle</code>, <code>triangleDown</code>, and <code>square</code>. This only does something if you supply a value.</td>
</tr> </tr>
<tr> <tr>
<td>radiusMax</td> <td>radiusMax</td>
<td>Number</td> <td>Number</td>
<td>30</td> <td>30</td>
<td>The maximum radius for a scaled node. Only applicable to shapes <code>dot</code>, <td>The maximum radius for a scaled node. Only applicable to shapes <code>dot</code>,
<code>star</code>, <code>triangle</code>, <code>triangleDown</code>, and <code>square</code>.</td>
<code>star</code>, <code>triangle</code>, <code>triangleDown</code>, and <code>square</code>. This only does something if you supply a value.</td>
</tr> </tr>
</table> </table>
@ -1224,13 +1225,13 @@ var options = {
<td>widthMin</td> <td>widthMin</td>
<td>Number</td> <td>Number</td>
<td>1</td> <td>1</td>
<td>The minimum thickness of the line when using per-edge defined values.</td>
<td>The minimum thickness of the line when using per-edge defined values. This does nothing if you have not defined a value.</td>
</tr> </tr>
<tr> <tr>
<td>widthMax</td> <td>widthMax</td>
<td>Number</td> <td>Number</td>
<td>15</td> <td>15</td>
<td>The maximum thickness of the line when using per-edge defined values.</td>
<td>The maximum thickness of the line when using per-edge defined values. This does nothing if you have not defined a value.</td>
</tr> </tr>
</table> </table>
@ -1905,7 +1906,8 @@ var options = {
x: 10, x: 10,
y: 10, y: 10,
zoom: 0.02 zoom: 0.02
}
},
bindToWindow: true
} }
} }
</pre> </pre>
@ -1937,6 +1939,12 @@ var options = {
<td>0.02</td> <td>0.02</td>
<td>This defines the zoomspeed when using the keyboard navigation.</td> <td>This defines the zoomspeed when using the keyboard navigation.</td>
</tr> </tr>
<tr>
<td>bindToWindow</td>
<td>Boolean</td>
<td>true</td>
<td>If this is true, global keyboard events will be used. If it is false, the keyboard events are only used when the network is active. It is activated on mouseOver automatically.</td>
</tr>
</table> </table>

+ 20
- 0
docs/timeline.html View File

@ -764,6 +764,26 @@ var options = {
<td>A template function used to generate the contents of the items. The function is called by the Timeline with an items data as argument, and must return HTML code as result. When the option template is specified, the items do not need to have a field <code>content</code>. See section <a href="#Templates">Templates</a> for a detailed explanation.</td> <td>A template function used to generate the contents of the items. The function is called by the Timeline with an items data as argument, and must return HTML code as result. When the option template is specified, the items do not need to have a field <code>content</code>. See section <a href="#Templates">Templates</a> for a detailed explanation.</td>
</tr> </tr>
<tr>
<td>timeAxis.scale</td>
<td>string</td>
<td>none</td>
<td>Set a fixed scale for the time axis of the Timeline. Choose from <code>'millisecond'</code>, <code>'second'</code>, <code>'minute'</code>, <code>'hour'</code>, <code>'weekday'</code>, <code>'day'</code>, <code>'month'</code>, <code>'year'</code>. Example usage:
<pre class="prettyprint lang-js">var options = {
timeAxis: {scale: 'minute', step: 5}
}</pre>
</td>
</tr>
<tr>
<td>timeAxis.step</td>
<td>number</td>
<td>1</td>
<td>
Set a fixed step size for the time axis. Only applicable when used together with <code>timeAxis.scale</code>.
Choose for example 1, 2, 5, or 10.</td>
</tr>
<tr> <tr>
<td>type</td> <td>type</td>
<td>String</td> <td>String</td>

+ 5
- 0
lib/DataSet.js View File

@ -53,6 +53,7 @@ function DataSet (data, options) {
this._options = options || {}; this._options = options || {};
this._data = {}; // map with data indexed by id this._data = {}; // map with data indexed by id
this.length = 0; // number of items in the DataSet
this._fieldId = this._options.fieldId || 'id'; // name of the field containing id this._fieldId = this._options.fieldId || 'id'; // name of the field containing id
this._type = {}; // internal field types (NOTE: this can differ from this._options.type) this._type = {}; // internal field types (NOTE: this can differ from this._options.type)
@ -737,6 +738,7 @@ DataSet.prototype._remove = function (id) {
if (util.isNumber(id) || util.isString(id)) { if (util.isNumber(id) || util.isString(id)) {
if (this._data[id]) { if (this._data[id]) {
delete this._data[id]; delete this._data[id];
this.length--;
return id; return id;
} }
} }
@ -744,6 +746,7 @@ DataSet.prototype._remove = function (id) {
var itemId = id[this._fieldId]; var itemId = id[this._fieldId];
if (itemId && this._data[itemId]) { if (itemId && this._data[itemId]) {
delete this._data[itemId]; delete this._data[itemId];
this.length--;
return itemId; return itemId;
} }
} }
@ -759,6 +762,7 @@ DataSet.prototype.clear = function (senderId) {
var ids = Object.keys(this._data); var ids = Object.keys(this._data);
this._data = {}; this._data = {};
this.length = 0;
this._trigger('remove', {items: ids}, senderId); this._trigger('remove', {items: ids}, senderId);
@ -884,6 +888,7 @@ DataSet.prototype._addItem = function (item) {
} }
} }
this._data[id] = d; this._data[id] = d;
this.length++;
return id; return id;
}; };

+ 5
- 0
lib/DataView.js View File

@ -14,6 +14,7 @@ var DataSet = require('./DataSet');
function DataView (data, options) { function DataView (data, options) {
this._data = null; this._data = null;
this._ids = {}; // ids of the items currently in memory (just contains a boolean true) this._ids = {}; // ids of the items currently in memory (just contains a boolean true)
this.length = 0; // number of items in the DataView
this._options = options || {}; this._options = options || {};
this._fieldId = 'id'; // name of the field containing id this._fieldId = 'id'; // name of the field containing id
this._subscribers = {}; // event subscribers this._subscribers = {}; // event subscribers
@ -50,6 +51,7 @@ DataView.prototype.setData = function (data) {
} }
} }
this._ids = {}; this._ids = {};
this.length = 0;
this._trigger('remove', {items: ids}); this._trigger('remove', {items: ids});
} }
@ -67,6 +69,7 @@ DataView.prototype.setData = function (data) {
id = ids[i]; id = ids[i];
this._ids[id] = true; this._ids[id] = true;
} }
this.length = ids.length;
this._trigger('add', {items: ids}); this._trigger('add', {items: ids});
// subscribe to new dataset // subscribe to new dataset
@ -277,6 +280,8 @@ DataView.prototype._onEvent = function (event, params, senderId) {
break; break;
} }
this.length += added.length - removed.length;
if (added.length) { if (added.length) {
this._trigger('add', {items: added}, senderId); this._trigger('add', {items: added}, senderId);
} }

+ 1
- 1
lib/network/Edge.js View File

@ -1164,7 +1164,7 @@ Edge.prototype.positionBezierNode = function() {
this.via.x = 0.5 * (this.from.x + this.to.x); this.via.x = 0.5 * (this.from.x + this.to.x);
this.via.y = 0.5 * (this.from.y + this.to.y); this.via.y = 0.5 * (this.from.y + this.to.y);
} }
else {
else if (this.via !== null) {
this.via.x = 0; this.via.x = 0;
this.via.y = 0; this.via.y = 0;
} }

+ 58
- 34
lib/network/Network.js View File

@ -45,7 +45,7 @@ function Network (container, data, options) {
this.renderRefreshRate = 60; // hz (fps) this.renderRefreshRate = 60; // hz (fps)
this.renderTimestep = 1000 / this.renderRefreshRate; // ms -- saves calculation later on this.renderTimestep = 1000 / this.renderRefreshRate; // ms -- saves calculation later on
this.renderTime = 0; // measured time it takes to render a frame this.renderTime = 0; // measured time it takes to render a frame
this.physicsTime = 0; // measured time it takes to render a frame
this.physicsTime = 0; // measured time it takes to render a frame
this.runDoubleSpeed = false; this.runDoubleSpeed = false;
this.physicsDiscreteStepsize = 0.50; // discrete stepsize of the simulation this.physicsDiscreteStepsize = 0.50; // discrete stepsize of the simulation
@ -171,7 +171,8 @@ function Network (container, data, options) {
}, },
keyboard: { keyboard: {
enabled: false, enabled: false,
speed: {x: 10, y: 10, zoom: 0.02}
speed: {x: 10, y: 10, zoom: 0.02},
bindToWindow: true
}, },
dataManipulation: { dataManipulation: {
enabled: false, enabled: false,
@ -229,6 +230,7 @@ function Network (container, data, options) {
// animation properties // animation properties
this.animationSpeed = 1/this.renderRefreshRate; this.animationSpeed = 1/this.renderRefreshRate;
this.animationEasingFunction = "easeInOutQuint"; this.animationEasingFunction = "easeInOutQuint";
this.animating = false;
this.easingTime = 0; this.easingTime = 0;
this.sourceScale = 0; this.sourceScale = 0;
this.targetScale = 0; this.targetScale = 0;
@ -410,8 +412,8 @@ Network.prototype._getRange = function() {
node = this.nodes[nodeId]; node = this.nodes[nodeId];
if (minX > (node.boundingBox.left)) {minX = node.boundingBox.left;} if (minX > (node.boundingBox.left)) {minX = node.boundingBox.left;}
if (maxX < (node.boundingBox.right)) {maxX = node.boundingBox.right;} if (maxX < (node.boundingBox.right)) {maxX = node.boundingBox.right;}
if (minY > (node.boundingBox.bottom)) {minY = node.boundingBox.bottom;}
if (maxY < (node.boundingBox.top)) {maxY = node.boundingBox.top;}
if (minY > (node.boundingBox.bottom)) {minY = node.boundingBox.top;} // top is negative, bottom is positive
if (maxY < (node.boundingBox.top)) {maxY = node.boundingBox.bottom;} // top is negative, bottom is positive
} }
} }
if (minX == 1e9 && maxX == -1e9 && minY == 1e9 && maxY == -1e9) { if (minX == 1e9 && maxX == -1e9 && minY == 1e9 && maxY == -1e9) {
@ -441,15 +443,9 @@ Network.prototype._findCenter = function(range) {
Network.prototype.zoomExtent = function(animationOptions, initialZoom, disableStart) { Network.prototype.zoomExtent = function(animationOptions, initialZoom, disableStart) {
this._redraw(true); this._redraw(true);
if (initialZoom === undefined) {
initialZoom = false;
}
if (disableStart === undefined) {
disableStart = false;
}
if (animationOptions === undefined) {
animationOptions = false;
}
if (initialZoom === undefined) {initialZoom = false;}
if (disableStart === undefined) {disableStart = false;}
if (animationOptions === undefined) {animationOptions = false;}
var range = this._getRange(); var range = this._getRange();
var zoomLevel; var zoomLevel;
@ -485,7 +481,6 @@ Network.prototype.zoomExtent = function(animationOptions, initialZoom, disableSt
var xZoomLevel = this.frame.canvas.clientWidth / xDistance; var xZoomLevel = this.frame.canvas.clientWidth / xDistance;
var yZoomLevel = this.frame.canvas.clientHeight / yDistance; var yZoomLevel = this.frame.canvas.clientHeight / yDistance;
zoomLevel = (xZoomLevel <= yZoomLevel) ? xZoomLevel : yZoomLevel; zoomLevel = (xZoomLevel <= yZoomLevel) ? xZoomLevel : yZoomLevel;
} }
@ -730,10 +725,13 @@ Network.prototype.setOptions = function (options) {
// configure the smooth curves // configure the smooth curves
this._configureSmoothCurves(); this._configureSmoothCurves();
// bind hammer
this._bindHammer();
// bind keys. If disabled, this will not do anything; // bind keys. If disabled, this will not do anything;
this._createKeyBinds(); this._createKeyBinds();
this.setSize(this.constants.width, this.constants.height); this.setSize(this.constants.width, this.constants.height);
this.moving = true; this.moving = true;
this.start(); this.start();
@ -759,6 +757,7 @@ Network.prototype._create = function () {
this.frame.className = 'vis network-frame'; this.frame.className = 'vis network-frame';
this.frame.style.position = 'relative'; this.frame.style.position = 'relative';
this.frame.style.overflow = 'hidden'; this.frame.style.overflow = 'hidden';
this.frame.tabIndex = 900;
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
@ -786,10 +785,19 @@ Network.prototype._create = function () {
this.frame.canvas.getContext("2d").setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); this.frame.canvas.getContext("2d").setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0);
} }
//////////////////////////////////////////////////////////////////
this._bindHammer();
};
/**
* This function binds hammer, it can be repeated over and over due to the uniqueness check.
* @private
*/
Network.prototype._bindHammer = function() {
var me = this; var me = this;
if (this.hammer !== undefined) {
this.hammer.dispose();
}
this.drag = {}; this.drag = {};
this.pinch = {}; this.pinch = {};
this.hammer = Hammer(this.frame.canvas, { this.hammer = Hammer(this.frame.canvas, {
@ -798,13 +806,17 @@ Network.prototype._create = function () {
this.hammer.on('tap', me._onTap.bind(me) ); this.hammer.on('tap', me._onTap.bind(me) );
this.hammer.on('doubletap', me._onDoubleTap.bind(me) ); this.hammer.on('doubletap', me._onDoubleTap.bind(me) );
this.hammer.on('hold', me._onHold.bind(me) ); this.hammer.on('hold', me._onHold.bind(me) );
this.hammer.on('pinch', me._onPinch.bind(me) );
this.hammer.on('touch', me._onTouch.bind(me) ); this.hammer.on('touch', me._onTouch.bind(me) );
this.hammer.on('dragstart', me._onDragStart.bind(me) ); this.hammer.on('dragstart', me._onDragStart.bind(me) );
this.hammer.on('drag', me._onDrag.bind(me) ); this.hammer.on('drag', me._onDrag.bind(me) );
this.hammer.on('dragend', me._onDragEnd.bind(me) ); this.hammer.on('dragend', me._onDragEnd.bind(me) );
this.hammer.on('mousewheel',me._onMouseWheel.bind(me) );
this.hammer.on('DOMMouseScroll',me._onMouseWheel.bind(me) ); // for FF
if (this.constants.zoomable == true) {
this.hammer.on('mousewheel', me._onMouseWheel.bind(me));
this.hammer.on('DOMMouseScroll', me._onMouseWheel.bind(me)); // for FF
this.hammer.on('pinch', me._onPinch.bind(me) );
}
this.hammer.on('mousemove', me._onMouseMoveTitle.bind(me) ); this.hammer.on('mousemove', me._onMouseMoveTitle.bind(me) );
this.hammerFrame = Hammer(this.frame, { this.hammerFrame = Hammer(this.frame, {
@ -814,9 +826,7 @@ Network.prototype._create = function () {
// add the frame to the container element // add the frame to the container element
this.containerElement.appendChild(this.frame); this.containerElement.appendChild(this.frame);
};
}
/** /**
* Binding the keys for keyboard navigation. These functions are defined in the NavigationMixin * Binding the keys for keyboard navigation. These functions are defined in the NavigationMixin
@ -827,7 +837,13 @@ Network.prototype._createKeyBinds = function() {
if (this.keycharm !== undefined) { if (this.keycharm !== undefined) {
this.keycharm.destroy(); this.keycharm.destroy();
} }
this.keycharm = keycharm();
if (this.constants.keyboard.bindToWindow == true) {
this.keycharm = keycharm({container: window, preventDefault: false});
}
else {
this.keycharm = keycharm({container: this.frame, preventDefault: false});
}
this.keycharm.reset(); this.keycharm.reset();
@ -1272,6 +1288,11 @@ Network.prototype._onMouseMoveTitle = function (event) {
this._checkHidePopup(pointer); this._checkHidePopup(pointer);
} }
// if we bind the keyboard to the div, we have to highlight it to use it. This highlights it on mouse over
if (this.constants.keyboard.bindToWindow == false && this.constants.keyboard.enabled == true) {
this.frame.focus();
}
// start a timeout that will check if the mouse is positioned above // start a timeout that will check if the mouse is positioned above
// an element // an element
var me = this; var me = this;
@ -2246,17 +2267,20 @@ Network.prototype._animationStep = function() {
// handle the keyboad movement // handle the keyboad movement
this._handleNavigation(); this._handleNavigation();
var startTime = Date.now();
this._physicsTick();
var physicsTime = Date.now() - startTime;
// run double speed if it is a little graph
if ((this.renderTimestep - this.renderTime > 2 * physicsTime || this.runDoubleSpeed == true) && this.moving == true) {
// check if the physics have settled
if (this.moving == true) {
var startTime = Date.now();
this._physicsTick(); this._physicsTick();
var physicsTime = Date.now() - startTime;
// this makes sure there is no jitter. The decision is taken once to run it at double speed.
if (this.renderTime != 0) {
this.runDoubleSpeed = true
// run double speed if it is a little graph
if ((this.renderTimestep - this.renderTime > 2 * physicsTime || this.runDoubleSpeed == true) && this.moving == true) {
this._physicsTick();
// this makes sure there is no jitter. The decision is taken once to run it at double speed.
if (this.renderTime != 0) {
this.runDoubleSpeed = true
}
} }
} }
@ -2277,7 +2301,7 @@ if (typeof window !== 'undefined') {
* Schedule a animation step with the refreshrate interval. * Schedule a animation step with the refreshrate interval.
*/ */
Network.prototype.start = function() { Network.prototype.start = function() {
if (this.moving == true || this.xIncrement != 0 || this.yIncrement != 0 || this.zoomIncrement != 0) {
if (this.moving == true || this.xIncrement != 0 || this.yIncrement != 0 || this.zoomIncrement != 0 || this.animating == true) {
if (!this.timer) { if (!this.timer) {
if (this.requiresTimeout == true) { if (this.requiresTimeout == true) {
this.timer = window.setTimeout(this._animationStep.bind(this), this.renderTimestep); // wait this.renderTimeStep milliseconds and perform the animation step function this.timer = window.setTimeout(this._animationStep.bind(this), this.renderTimestep); // wait this.renderTimeStep milliseconds and perform the animation step function
@ -2595,12 +2619,12 @@ Network.prototype.animateView = function (options) {
} }
} }
else { else {
this.animating = true;
this.animationSpeed = 1 / (this.renderRefreshRate * options.animation.duration * 0.001) || 1 / this.renderRefreshRate; this.animationSpeed = 1 / (this.renderRefreshRate * options.animation.duration * 0.001) || 1 / this.renderRefreshRate;
this.animationEasingFunction = options.animation.easingFunction; this.animationEasingFunction = options.animation.easingFunction;
this._classicRedraw = this._redraw; this._classicRedraw = this._redraw;
this._redraw = this._transitionRedraw; this._redraw = this._transitionRedraw;
this._redraw(); this._redraw();
this.moving = true;
this.start(); this.start();
} }
}; };
@ -2652,10 +2676,10 @@ Network.prototype._transitionRedraw = function (easingTime) {
); );
this._classicRedraw(); this._classicRedraw();
this.moving = true;
// cleanup // cleanup
if (this.easingTime >= 1.0) { if (this.easingTime >= 1.0) {
this.animating = false;
this.easingTime = 0; this.easingTime = 0;
if (this.lockedOnNodeId != null) { if (this.lockedOnNodeId != null) {
this._redraw = this._lockedRedraw; this._redraw = this._lockedRedraw;

+ 3
- 3
lib/network/Node.js View File

@ -180,16 +180,16 @@ Node.prototype.setProperties = function(properties, constants) {
} }
// copy group properties // copy group properties
if (typeof this.options.group === 'number' || (typeof this.options.group === 'string' && this.options.group != '')) {
var groupObj = this.grouplist.get(this.options.group);
if (typeof properties.group === 'number' || (typeof properties.group === 'string' && properties.group != '')) {
var groupObj = this.grouplist.get(properties.group);
util.deepExtend(this.options, groupObj); util.deepExtend(this.options, groupObj);
// the color object needs to be completely defined. Since groups can partially overwrite the colors, we parse it again, just in case. // the color object needs to be completely defined. Since groups can partially overwrite the colors, we parse it again, just in case.
this.options.color = util.parseColor(this.options.color); this.options.color = util.parseColor(this.options.color);
} }
// individual shape properties // individual shape properties
if (properties.radius !== undefined) {this.baseRadiusValue = this.options.radius;} if (properties.radius !== undefined) {this.baseRadiusValue = this.options.radius;}
if (properties.color !== undefined) {this.options.color = util.parseColor(properties.color);} if (properties.color !== undefined) {this.options.color = util.parseColor(properties.color);}
if (this.options.image !== undefined && this.options.image!= "") { if (this.options.image !== undefined && this.options.image!= "") {
if (this.imagelist) { if (this.imagelist) {
this.imageObj = this.imagelist.load(this.options.image, this.options.brokenImage); this.imageObj = this.imagelist.load(this.options.image, this.options.brokenImage);

+ 12
- 13
lib/timeline/TimeStep.js View File

@ -240,23 +240,22 @@ TimeStep.prototype.getCurrent = function() {
/** /**
* Set a custom scale. Autoscaling will be disabled. * Set a custom scale. Autoscaling will be disabled.
* For example setScale(SCALE.MINUTES, 5) will result
* For example setScale('minute', 5) will result
* in minor steps of 5 minutes, and major steps of an hour. * in minor steps of 5 minutes, and major steps of an hour.
* *
* @param {string} newScale
* A scale. Choose from 'millisecond, 'second,
* 'minute', 'hour', 'weekday, 'day, 'month, 'year'.
* @param {Number} newStep A step size, by default 1. Choose for
* example 1, 2, 5, or 10.
* @param {{scale: string, step: number}} params
* An object containing two properties:
* - A string 'scale'. Choose from 'millisecond', 'second',
* 'minute', 'hour', 'weekday, 'day, 'month, 'year'.
* - A number 'step'. A step size, by default 1.
* Choose for example 1, 2, 5, or 10.
*/ */
TimeStep.prototype.setScale = function(newScale, newStep) {
this.scale = newScale;
if (newStep > 0) {
this.step = newStep;
TimeStep.prototype.setScale = function(params) {
if (params && typeof params.scale == 'string') {
this.scale = params.scale;
this.step = params.step > 0 ? params.step : 1;
this.autoScale = false;
} }
this.autoScale = false;
}; };
/** /**

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

@ -38,7 +38,8 @@ function TimeAxis (body, options) {
// TODO: implement timeaxis orientations 'left' and 'right' // TODO: implement timeaxis orientations 'left' and 'right'
showMinorLabels: true, showMinorLabels: true,
showMajorLabels: true, showMajorLabels: true,
format: null
format: null,
timeAxis: null
}; };
this.options = util.extend({}, this.defaultOptions); this.options = util.extend({}, this.defaultOptions);
@ -68,7 +69,8 @@ TimeAxis.prototype.setOptions = function(options) {
'showMinorLabels', 'showMinorLabels',
'showMajorLabels', 'showMajorLabels',
'hiddenDates', 'hiddenDates',
'format'
'format',
'timeAxis'
], this.options, options); ], this.options, options);
// apply locale to moment.js // apply locale to moment.js
@ -190,6 +192,9 @@ TimeAxis.prototype._repaintLabels = function () {
if (this.options.format) { if (this.options.format) {
step.setFormat(this.options.format); step.setFormat(this.options.format);
} }
if (this.options.timeAxis) {
step.setScale(this.options.timeAxis);
}
this.step = step; this.step = step;
// Move all DOM elements to a "redundant" list, where they // Move all DOM elements to a "redundant" list, where they

+ 1
- 1
package.json View File

@ -1,6 +1,6 @@
{ {
"name": "vis", "name": "vis",
"version": "3.9.0",
"version": "3.9.2-SNAPSHOT",
"description": "A dynamic, browser-based visualization library.", "description": "A dynamic, browser-based visualization library.",
"homepage": "http://visjs.org/", "homepage": "http://visjs.org/",
"repository": { "repository": {

+ 9
- 0
test/DataSet.test.js View File

@ -26,6 +26,7 @@ describe('DataSet', function () {
]); ]);
var items = data.get(); var items = data.get();
assert.equal(data.length, 4);
assert.equal(items.length, 4); assert.equal(items.length, 4);
items.forEach(function (item) { items.forEach(function (item) {
assert.ok(item.start instanceof Date); assert.ok(item.start instanceof Date);
@ -76,6 +77,7 @@ describe('DataSet', function () {
{id: 3}, {id: 3},
{id: 4} {id: 4}
]); ]);
assert.equal(data.length, 3);
// add an item // add an item
data.add({id: 5, content: 'Item 5', start: now.valueOf()}); data.add({id: 5, content: 'Item 5', start: now.valueOf()});
@ -87,12 +89,17 @@ describe('DataSet', function () {
{id: 4}, {id: 4},
{id: 5} {id: 5}
]); ]);
assert.equal(data.length, 4);
// update an item // update an item
data.update({id: 5, content: 'changed!'}); // update item (extend existing fields) data.update({id: 5, content: 'changed!'}); // update item (extend existing fields)
assert.equal(data.length, 4);
data.remove(3); // remove existing item data.remove(3); // remove existing item
assert.equal(data.length, 3);
data.add({id: 3, other: 'bla'}); // add new item data.add({id: 3, other: 'bla'}); // add new item
assert.equal(data.length, 4);
data.update({id: 6, content: 'created!', start: now.valueOf()}); // this item is not yet existing, create it data.update({id: 6, content: 'created!', start: now.valueOf()}); // this item is not yet existing, create it
assert.equal(data.length, 5);
assert.deepEqual(data.get().sort(sort), [ assert.deepEqual(data.get().sort(sort), [
{id: 1, content: 'Item 1', start: now}, {id: 1, content: 'Item 1', start: now},
{id: 3, other: 'bla'}, {id: 3, other: 'bla'},
@ -100,8 +107,10 @@ describe('DataSet', function () {
{id: 5, content: 'changed!', start: now}, {id: 5, content: 'changed!', start: now},
{id: 6, content: 'created!', start: now} {id: 6, content: 'created!', start: now}
]); ]);
assert.equal(data.length, 5);
data.clear(); data.clear();
assert.equal(data.length, 0);
assert.equal(data.get().length, 0); assert.equal(data.get().length, 0);

+ 28
- 0
test/DataView.test.js View File

@ -28,6 +28,7 @@ describe('DataView', function () {
{id: 2, content: 'Item 2', group: 2}, {id: 2, content: 'Item 2', group: 2},
{id: 3, content: 'Item 3', group: 2} {id: 3, content: 'Item 3', group: 2}
]); ]);
assert.equal(group2.length, 2);
// test filtering the view contents // test filtering the view contents
assert.deepEqual(group2.get({ assert.deepEqual(group2.get({
@ -51,19 +52,46 @@ describe('DataView', function () {
groups.update({id:2, content: 'Item 2 (changed)'}); groups.update({id:2, content: 'Item 2 (changed)'});
assert.equal(groupsTriggerCount, 1); assert.equal(groupsTriggerCount, 1);
assert.equal(group2TriggerCount, 1); assert.equal(group2TriggerCount, 1);
assert.equal(group2.length, 2);
groups.update({id:5, content: 'Item 5 (changed)'}); groups.update({id:5, content: 'Item 5 (changed)'});
assert.equal(groupsTriggerCount, 2); assert.equal(groupsTriggerCount, 2);
assert.equal(group2TriggerCount, 1); assert.equal(group2TriggerCount, 1);
assert.equal(group2.length, 2);
// detach the view from groups // detach the view from groups
group2.setData(null); group2.setData(null);
assert.equal(groupsTriggerCount, 2); assert.equal(groupsTriggerCount, 2);
assert.equal(group2TriggerCount, 2); assert.equal(group2TriggerCount, 2);
assert.equal(group2.length, 0);
groups.update({id:2, content: 'Item 2 (changed again)'}); groups.update({id:2, content: 'Item 2 (changed again)'});
assert.equal(groupsTriggerCount, 3); assert.equal(groupsTriggerCount, 3);
assert.equal(group2TriggerCount, 2); assert.equal(group2TriggerCount, 2);
// test updating of .length property
group2.setData(groups);
assert.equal(group2.length, 2);
// add a new item
groups.add({id: 6, content: 'Item 6', group: 2});
assert.equal(group2.length, 3);
// change an items group to 2
groups.update({id: 4, group: 2});
assert.equal(group2.length, 4);
// change an items group to 1
groups.update({id: 4, group: 1});
assert.equal(group2.length, 3);
// remove an item
groups.remove(2);
assert.equal(group2.length, 2);
// remove all items
groups.clear();
assert.equal(group2.length, 0);
}); });
}); });

+ 4
- 0
test/timeline.html View File

@ -118,6 +118,10 @@
'hour': 'dddd D MMMM' 'hour': 'dddd D MMMM'
} }
} }
// timeAxis: {
// scale: 'hour',
// step: 2
// }
//clickToUse: true, //clickToUse: true,
//min: moment('2013-01-01'), //min: moment('2013-01-01'),
//max: moment('2013-12-31'), //max: moment('2013-12-31'),

Loading…
Cancel
Save