Browse Source

Merge remote-tracking branch 'origin/develop' into develop

v3_develop
jos 9 years ago
parent
commit
4b0433f0f5
25 changed files with 28217 additions and 27568 deletions
  1. +14
    -0
      HISTORY.md
  2. +27461
    -27289
      dist/vis.js
  3. +1
    -1
      dist/vis.map
  4. +15
    -15
      dist/vis.min.js
  5. +6
    -0
      docs/graph2d.html
  6. +23
    -4
      docs/timeline.html
  7. +64
    -0
      examples/graph2d/19_labels.html
  8. +11
    -7
      examples/network/06_groups.html
  9. +0
    -1
      examples/network/25_physics_configuration.html
  10. +17
    -4
      examples/network/26_staticSmoothCurves.html
  11. +2
    -0
      examples/network/27_world_cup_network.html
  12. +142
    -144
      examples/network/38_node_as_icon.html
  13. +74
    -0
      examples/timeline/34_add_custom_timebar.html
  14. +1
    -1
      examples/timeline/index.html
  15. +24
    -1
      lib/DOMutil.js
  16. +49
    -15
      lib/network/Edge.js
  17. +46
    -19
      lib/network/Groups.js
  18. +119
    -42
      lib/network/Network.js
  19. +13
    -13
      lib/network/Node.js
  20. +4
    -1
      lib/network/Popup.js
  21. +2
    -2
      lib/network/mixins/SelectionMixin.js
  22. +103
    -4
      lib/timeline/Core.js
  23. +16
    -3
      lib/timeline/component/CustomTime.js
  24. +9
    -1
      lib/timeline/component/LineGraph.js
  25. +1
    -1
      lib/timeline/component/graph2d_types/points.js

+ 14
- 0
HISTORY.md View File

@ -4,6 +4,20 @@ http://visjs.org
## not yet released, version 3.10.1-SNAPSHOT
### Network
- (added gradient coloring for lines, but set for release in 4.0 due to required refactoring of options)
- Fixed bug where a network that has frozen physics would resume redrawing after setData, setOptions etc.
- (add docs) Added option to bypass default groups. If more groups are specified in the nodes than there are in the groups, loop over supplied groups instead of default.
- (add docs) Added two new static smooth curves modes: curveCW and curve CCW.
- Added request redraw for certain internal processes to reduce number of draw calls.
- Added pull request for usage of Icons. Thanks @Dude9177!
- Allow hierarchical view to be set in setOptions.
### Graph2d
### Timeline
- Fixed not property initializing with a DataView for groups.

+ 27461
- 27289
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


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


+ 6
- 0
docs/graph2d.html View File

@ -175,6 +175,12 @@ var items = [
<td>no</td>
<td>The ID of the group this point belongs to.</td>
</tr>
<tr>
<td>label</td>
<td>object</td>
<td>no</td>
<td>A label object which will be displayed near to the item. A label object has one requirement - a <b> content </b> property. In addition you can set the <b> xOffset, yOffset and className </b> for further appearance customisations </td>
</tr>
</table>
<h3 id="groups">Groups</h3>

+ 23
- 4
docs/timeline.html View File

@ -854,6 +854,15 @@ var options = {
<th>Description</th>
</tr>
<tr>
<td>addCustomTime(time[, id])</td>
<td>Number | String</td>
<td>
Only applicable when the option showCustomTime is true.<br>
Add new vertical bar representing custom time that can be dragged by the user. Parameter <code>time</code> can be a Date, Number, or String. Parameter <code>id</code> can be Number or String. If <code>id</code> is provided, it will be used as ID for the new vertical bar, otherwise the ID will be auto generated.<br>
Returns ID of the newly created bar.
</td>
</tr>
<tr>
<td>clear([what])</td>
<td>none</td>
@ -903,9 +912,9 @@ timeline.clear({options: true}); // clear options only
</tr>
<tr>
<td>getCustomTime()</td>
<td>getCustomTime([id])</td>
<td>Date</td>
<td>Retrieve the custom time. Only applicable when the option <code>showCustomTime</code> is true.
<td>Retrieve the custom time. Only applicable when the option <code>showCustomTime</code> is true. If parameter <code>id</code> is provided, time of the custom time bar under that ID is returned.
</td>
</tr>
@ -958,6 +967,14 @@ timeline.clear({options: true}); // clear options only
</td>
</tr>
<tr>
<td>removeCustomTime(id)</td>
<td>none</td>
<td>
Remove vertical bars previously added to the timeline via <code>addCustomTime</code> method. Parameter <code>id</code> is the ID of the custom vertical bar returned by <code>addCustomTime</code> method.
</td>
</tr>
<tr>
<td>setCurrentTime(time)</td>
<td>none</td>
@ -967,9 +984,9 @@ timeline.clear({options: true}); // clear options only
</tr>
<tr>
<td>setCustomTime(time)</td>
<td>setCustomTime(time [, id])</td>
<td>none</td>
<td>Adjust the custom time bar. Only applicable when the option <code>showCustomTime</code> is true. <code>time</code> can be a Date object, numeric timestamp, or ISO date string.
<td>Adjust the custom time bar. Only applicable when the option <code>showCustomTime</code> is true. Parameter <code>time</code> can be a Date object, numeric timestamp, or ISO date string. Parameter <code>id</code> represents ID of the custom time bar, provided by <code>addCustomTime</code> method and can be a Number or String.
</td>
</tr>
@ -1130,6 +1147,7 @@ timeline.off('select', onSelect);
</td>
<td>
<ul>
<li><code>id</code> (Number | String): Vertical bar ID.</li>
<li><code>time</code> (Date): the current time.</li>
</ul>
</td>
@ -1142,6 +1160,7 @@ timeline.off('select', onSelect);
</td>
<td>
<ul>
<li><code>id</code> (Number | String): Vertical bar ID.</li>
<li><code>time</code> (Date): the current time.</li>
</ul>
</td>

+ 64
- 0
examples/graph2d/19_labels.html View File

@ -0,0 +1,64 @@
<!DOCTYPE HTML>
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
<title>Graph2d | Basic Example</title>
<style type="text/css">
body, html {
font-family: sans-serif;
}
.red {
fill:red;
}
</style>
<script src="../../dist/vis.js"></script>
<link href="../../dist/vis.css" rel="stylesheet" type="text/css" />
</head>
<body>
<h2>Graph2d | Label Example</h2>
<div style="width:700px; font-size:14px; text-align: justify;">
This example shows the how to add a label to each point in Graph2d. Each item can have a label object which contains the content and CSS class.In addition, xOffset and yOffset will adjust the location of the label relative to the point being labelled.
<br /><br />
</div>
<br />
<div id="visualization"></div>
<script type="text/javascript">
var container = document.getElementById('visualization');
var label1 = {
content: "offset label",
xOffset: 20,
yOffset: 20
}
var label2 = {
content: "Label2",
className: "red"
}
var items = [
{x: '2014-06-11', y: 10,label:label1},
{x: '2014-06-12', y: 25,label:label2},
{x: '2014-06-13', y: 30},
{x: '2014-06-14', y: 10},
{x: '2014-06-15', y: 15},
{x: '2014-06-16', y: 30}
];
var dataset = new vis.DataSet(items);
var options = {
start: '2014-06-10',
end: '2014-06-18',
};
var graph2d = new vis.Graph2d(container, dataset, options);
</script>
</body>
</html>

+ 11
- 7
examples/network/06_groups.html View File

@ -8,9 +8,10 @@
font: 10pt arial;
}
#mynetwork {
width: 600px;
height: 600px;
width: 1900px;
height: 900px;
border: 1px solid lightgray;
background-color:#222222;
}
</style>
@ -139,11 +140,14 @@
edges: edges
};
var options = {
stabilize: false,
stabilize: true,
nodes: {
shape: 'dot'
shape: 'dot',
radius:30,
fontColor:'#ffffff',
borderWidth:2
},
physics: {barnesHut:{springLength: 200}}
physics: {barnesHut:{springLength: 100}}
};
network = new vis.Network(container, data, options);
}
@ -154,9 +158,9 @@
<body onload="draw()">
<form onsubmit= "javascript: draw(); return false;">
Number of groups:
<input type="text" value="6" id="groupCount" style="width: 50px;">
<input type="text" value="20" id="groupCount" style="width: 50px;">
Number of nodes per group:
<input type="text" value="7" id="nodeCount" style="width: 50px;">
<input type="text" value="1" id="nodeCount" style="width: 50px;">
<input type="submit" value="Go">
</form>
<br>

+ 0
- 1
examples/network/25_physics_configuration.html View File

@ -78,7 +78,6 @@
};
var options = {
edges:{opacity:0.2},
stabilize: false,
configurePhysics:true
};

+ 17
- 4
examples/network/26_staticSmoothCurves.html View File

@ -32,19 +32,26 @@
Smooth curve type:
<select id="dropdownID">
<option value="continuous">continuous</option>
<option value="continuous" selected="selected">continuous</option>
<option value="discrete">discrete</option>
<option value="diagonalCross">diagonalCross</option>
<option value="straightCross">straightCross</option>
<option value="horizontal">horizontal</option>
<option value="vertical">vertical</option>
</select>
<option value="curvedCW">curvedCW</option>
<option value="curvedCCW">curvedCCW</option>
</select><br/>
Roundness (0..1): <input type="range" min="0" max="1" value="0.5" step="0.05" style="width:200px" id="roundnessSlider"> <input id="roundnessScreen" value="0.5"> (0.5 is max roundness for continuous, 1.0 for the others)
<div id="mynetwork"></div>
<script type="text/javascript">
var dropdown = document.getElementById("dropdownID");
dropdown.onchange = update;
var roundnessSlider = document.getElementById("roundnessSlider");
roundnessSlider.onchange = update;
var roundnessScreen = document.getElementById("roundnessScreen");
// create an array with nodes
var nodes = [
{id: 1, label: 'Node 1'},
@ -53,7 +60,7 @@ dropdown.onchange = update;
// create an array with edges
var edges = [
{from: 1, to: 2}
{from: 1, to: 2, style:"arrow"}
];
// create a network
@ -68,8 +75,14 @@ dropdown.onchange = update;
function update() {
var type = dropdown.value;
network.setOptions({smoothCurves:{type:type}});
var roundness = roundnessSlider.value;
roundnessScreen.value = roundness;
var options = {smoothCurves:{type:type, roundness:roundness}}
network.setOptions(options);
}
update();
</script>
</body>

+ 2
- 0
examples/network/27_world_cup_network.html View File

@ -39,6 +39,8 @@ Smooth curve type:
<option value="straightCross">straightCross</option>
<option value="horizontal">horizontal</option>
<option value="vertical">vertical</option>
<option value="curvedCW">curvedCW</option>
<option value="curvedCCW">curvedCCW</option>
</select><br/>
inheritColor option:
<select id="inheritColor">

+ 142
- 144
examples/network/38_node_as_icon.html View File

@ -5,15 +5,155 @@
<meta charset="UTF-8">
<title>Network | node as icon</title>
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.3.min.js"></script>
<script type="text/javascript" src="../../dist/vis.js"></script>
<link href="../../dist/vis.css" rel="stylesheet" type="text/css" />
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
<link rel="stylesheet" href="http://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
<script language="JavaScript">
function draw() {
/*
* Example for FontAwesome
*/
var optionsFA = {
height: '300px',
groups: {
usergroups: {
shape: 'icon',
iconFontFace: 'FontAwesome',
icon: '\uf0c0',
iconSize: 50,
iconColor: '#57169a'
},
users: {
shape: 'icon',
iconFontFace: 'FontAwesome',
icon: '\uf007',
iconSize: 50,
iconColor: '#aa00ff'
}
}
};
// create an array with nodes
var nodesFA = [{
id: 1,
label: 'User 1',
group: 'users'
}, {
id: 2,
label: 'User 2',
group: 'users'
}, {
id: 3,
label: 'Usergroup 1',
group: 'usergroups'
}, {
id: 4,
label: 'Usergroup 2',
group: 'usergroups'
}, {
id: 5,
label: 'Organisation 1',
shape: 'icon',
iconFontFace: 'FontAwesome',
icon: '\uf1ad',
iconSize: 50,
iconColor: '#f0a30a'
}];
// create an array with edges
var edges = [{
from: 1,
to: 3
}, {
from: 1,
to: 4
}, {
from: 2,
to: 4
}, {
from: 3,
to: 5
}, {
from: 4,
to: 5
}];
// create a network
var containerFA = document.getElementById('mynetworkFA');
var dataFA = {
nodes: nodesFA,
edges: edges
};
var networkFA = new vis.Network(containerFA, dataFA, optionsFA);
/*
* Example for Ionicons
*/
var optionsIO = {
height: '300px',
groups: {
usergroups: {
shape: 'icon',
iconFontFace: 'Ionicons',
icon: '\uf47c',
iconSize: 50,
iconColor: '#57169a'
},
users: {
shape: 'icon',
iconFontFace: 'Ionicons',
icon: '\uf47e',
iconSize: 50,
iconColor: '#aa00ff'
}
}
};
// create an array with nodes
var nodesIO = [{
id: 1,
label: 'User 1',
group: 'users'
}, {
id: 2,
label: 'User 2',
group: 'users'
}, {
id: 3,
label: 'Usergroup 1',
group: 'usergroups'
}, {
id: 4,
label: 'Usergroup 2',
group: 'usergroups'
}, {
id: 5,
label: 'Organisation 1',
shape: 'icon',
iconFontFace: 'Ionicons',
icon: '\uf276',
iconSize: 50,
iconColor: '#f0a30a'
}];
// create a network
var containerIO = document.getElementById('mynetworkIO');
var dataIO = {
nodes: nodesIO,
edges: edges
};
var networkIO = new vis.Network(containerIO, dataIO, optionsIO);
}
</script>
</head>
<body>
<body onload="draw()">
<h2>
<i class="fa fa-flag"></i> Use FontAwesome-icons for node</h2>
<div id="mynetworkFA"></div>
@ -21,148 +161,6 @@
<i class="ion ion-ionic"></i> Use Ionicons-icons for node</h2>
<div id="mynetworkIO"></div>
<script type="text/javascript">
$(function() {
/*
* Example for FontAwesome
*/
var optionsFA = {
height: '300px',
groups: {
usergroups: {
shape: 'icon',
iconFontFace: 'FontAwesome',
icon: '\uf0c0',
iconSize: 50,
iconColor: '#57169a'
},
users: {
shape: 'icon',
iconFontFace: 'FontAwesome',
icon: '\uf007',
iconSize: 50,
iconColor: '#aa00ff'
},
},
};
// create an array with nodes
var nodesFA = [{
id: 1,
label: 'User 1',
group: 'users'
}, {
id: 2,
label: 'User 2',
group: 'users'
}, {
id: 3,
label: 'Usergroup 1',
group: 'usergroups'
}, {
id: 4,
label: 'Usergroup 2',
group: 'usergroups'
}, {
id: 5,
label: 'Organisation 1',
shape: 'icon',
iconFontFace: 'FontAwesome',
icon: '\uf1ad',
iconSize: 50,
iconColor: '#f0a30a'
}];
// create an array with edges
var edges = [{
from: 1,
to: 3
}, {
from: 1,
to: 4
}, {
from: 2,
to: 4
}, {
from: 3,
to: 5
}, {
from: 4,
to: 5
}];
// create a network
var containerFA = document.getElementById('mynetworkFA');
var dataFA = {
nodes: nodesFA,
edges: edges
};
var networkFA = new vis.Network(containerFA, dataFA, optionsFA);
/*
* Example for Ionicons
*/
var optionsIO = {
height: '300px',
groups: {
usergroups: {
shape: 'icon',
iconFontFace: 'Ionicons',
icon: '\uf47c',
iconSize: 50,
iconColor: '#57169a'
},
users: {
shape: 'icon',
iconFontFace: 'Ionicons',
icon: '\uf47e',
iconSize: 50,
iconColor: '#aa00ff'
},
},
};
// create an array with nodes
var nodesIO = [{
id: 1,
label: 'User 1',
group: 'users'
}, {
id: 2,
label: 'User 2',
group: 'users'
}, {
id: 3,
label: 'Usergroup 1',
group: 'usergroups'
}, {
id: 4,
label: 'Usergroup 2',
group: 'usergroups'
}, {
id: 5,
label: 'Organisation 1',
shape: 'icon',
iconFontFace: 'Ionicons',
icon: '\uf276',
iconSize: 50,
iconColor: '#f0a30a'
}];
// create a network
var containerIO = document.getElementById('mynetworkIO');
var dataIO = {
nodes: nodesIO,
edges: edges
};
var networkIO = new vis.Network(containerIO, dataIO, optionsIO);
})
</script>
</body>
</html>

+ 74
- 0
examples/timeline/34_add_custom_timebar.html View File

@ -0,0 +1,74 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Timeline | Show current and custom time bars</title>
<style type="text/css">
body, html {
font-family: sans-serif;
font-size: 11pt;
}
</style>
<script src="../../dist/vis.js"></script>
<link href="../../dist/vis.css" rel="stylesheet" type="text/css" />
</head>
<body>
<p>
<input type="button" id="add" value="Add custom vertical bar">
<input type="text" id="barId" placeholder="custom bar ID">
</p>
<p>
<input type="button" id="remove" value="Remove custom vertical bar">
<input type="text" id="barIndex" value="1" placeholder="custom bar ID">
</p>
<p>
<code><strong>timechange</strong></code> bar index: <span id="timechangeBar"></span>. Time: <span id="timechangeEvent"></span>
</p>
<p>
<code><strong>timechanged</strong></code> bar index: <span id="timechangedBar"></span>. Time: <span id="timechangedEvent"></span>
</p><br>
<div id="visualization"></div>
<script type="text/javascript">
var container = document.getElementById('visualization');
var items = new vis.DataSet();
var customDate = new Date();
var options = {
showCurrentTime: true,
showCustomTime: true,
start: new Date(Date.now() - 1000 * 60 * 60 * 24),
end: new Date(Date.now() + 1000 * 60 * 60 * 24 * 6)
};
var timeline = new vis.Timeline(container, items, options);
// Set first time bar
customDate = new Date(customDate.getFullYear(), customDate.getMonth(), customDate.getDate() + 1);
timeline.addCustomTime(customDate, 1);
document.getElementById('add').onclick = function () {
customDate = new Date(customDate.getFullYear(), customDate.getMonth(), customDate.getDate() + 1);
var barId = document.getElementById('barId').value || undefined;
timeline.addCustomTime(customDate, barId);
document.getElementById('barId').value = '';
};
document.getElementById('remove').onclick = function () {
timeline.removeCustomTime(document.getElementById('barIndex').value);
document.getElementById('barIndex').value = '';
};
timeline.on('timechange', function (properties) {
document.getElementById('timechangeBar').innerHTML = properties.id;
document.getElementById('timechangeEvent').innerHTML = properties.time;
});
timeline.on('timechanged', function (properties) {
document.getElementById('timechangedBar').innerHTML = properties.id;
document.getElementById('timechangedEvent').innerHTML = properties.time;
});
</script>
</body>
</html>

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

@ -44,7 +44,7 @@
<p><a href="31_background_areas_with_groups.html">31_background_areas_with_groups.html</a></p>
<p><a href="32_grid_styling.html">32_grid_styling.html</a></p>
<p><a href="33_custom_snapping.html">33_custom_snapping.html</a></p>
<p><a href="34_add_custom_timebar.html">34_add_custom_timebar.html</a></p>
<p><a href="requirejs/requirejs_example.html">requirejs_example.html</a></p>
</div>

+ 24
- 1
lib/DOMutil.js View File

@ -130,9 +130,10 @@ exports.getDOMElement = function (elementType, JSONcontainer, DOMContainer, inse
* @param group
* @param JSONcontainer
* @param svgContainer
* @param labelObj
* @returns {*}
*/
exports.drawPoint = function(x, y, group, JSONcontainer, svgContainer) {
exports.drawPoint = function(x, y, group, JSONcontainer, svgContainer, labelObj) {
var point;
if (group.options.drawPoints.style == 'circle') {
point = exports.getSVGElement('circle',JSONcontainer,svgContainer);
@ -152,6 +153,28 @@ exports.drawPoint = function(x, y, group, JSONcontainer, svgContainer) {
point.setAttributeNS(null, "style", group.group.options.drawPoints.styles);
}
point.setAttributeNS(null, "class", group.className + " point");
//handle label
var label = exports.getSVGElement('text',JSONcontainer,svgContainer);
if (labelObj){
if (labelObj.xOffset) {
x = x + labelObj.xOffset;
}
if (labelObj.yOffset) {
y = y + labelObj.yOffset;
}
if (labelObj.content) {
label.textContent = labelObj.content;
}
if (labelObj.className) {
label.setAttributeNS(null, "class", labelObj.className + " label");
}
}
label.setAttributeNS(null, "x", x);
label.setAttributeNS(null, "y", y);
return point;
};

+ 49
- 15
lib/network/Edge.js View File

@ -236,6 +236,28 @@ Edge.prototype.isOverlappingWith = function(obj) {
Edge.prototype._getColor = function(ctx) {
var colorObj = this.options.color;
if (this.options.useGradients == true) {
var grd = ctx.createLinearGradient(this.from.x, this.from.y, this.to.x, this.to.y);
var fromColor, toColor;
fromColor = this.from.options.color.highlight.border;
toColor = this.to.options.color.highlight.border;
if (this.from.selected == false && this.to.selected == false) {
fromColor = util.overrideOpacity(this.from.options.color.border, this.options.opacity);
toColor = util.overrideOpacity(this.to.options.color.border, this.options.opacity);
}
else if (this.from.selected == true && this.to.selected == false) {
toColor = this.to.options.color.border;
}
else if (this.from.selected == false && this.to.selected == true) {
fromColor = this.from.options.color.border;
}
grd.addColorStop(0, fromColor);
grd.addColorStop(1, toColor);
return grd;
}
if (this.colorDirty === true) {
if (this.options.inheritColor == "to") {
colorObj = {
@ -255,12 +277,7 @@ Edge.prototype._getColor = function(ctx) {
this.colorDirty = false;
}
if (this.options.useGradients == true) {
var grd = ctx.createLinearGradient(this.from.x, this.from.y, this.to.x, this.to.y);
grd.addColorStop(0, this.from.selected ? this.from.options.color.highlight.border : this.from.options.color.border);
grd.addColorStop(1, this.to.selected ? this.to.options.color.highlight.border : this.to.options.color.border);
return grd;
}
if (this.selected == true) {return colorObj.highlight;}
else if (this.hover == true) {return colorObj.hover;}
@ -351,7 +368,6 @@ Edge.prototype._getViaCoordinates = function () {
var yVia = null;
var factor = this.options.smoothCurves.roundness;
var type = this.options.smoothCurves.type;
var dx = Math.abs(this.from.x - this.to.x);
var dy = Math.abs(this.from.y - this.to.y);
if (type == 'discrete' || type == 'diagonalCross') {
@ -444,17 +460,39 @@ Edge.prototype._getViaCoordinates = function () {
yVia = this.to.y + (1 - factor) * dy;
}
}
else if (type == 'curvedCW') {
var dx = this.to.x - this.from.x;
var dy = this.from.y - this.to.y;
var radius = Math.sqrt(dx*dx + dy*dy);
var pi = Math.PI;
var originalAngle = Math.atan2(dy,dx);
var myAngle = (originalAngle + ((factor * 0.5) + 0.5) * pi) % (2 * pi);
xVia = this.from.x + (factor*0.5 + 0.5)*radius*Math.sin(myAngle);
yVia = this.from.y + (factor*0.5 + 0.5)*radius*Math.cos(myAngle);
}
else if (type == 'curvedCCW') {
var dx = this.to.x - this.from.x;
var dy = this.from.y - this.to.y;
var radius = Math.sqrt(dx*dx + dy*dy);
var pi = Math.PI;
var originalAngle = Math.atan2(dy,dx);
var myAngle = (originalAngle + ((-factor * 0.5) + 0.5) * pi) % (2 * pi);
xVia = this.from.x + (factor*0.5 + 0.5)*radius*Math.sin(myAngle);
yVia = this.from.y + (factor*0.5 + 0.5)*radius*Math.cos(myAngle);
}
else { // continuous
if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) {
if (this.from.y > this.to.y) {
if (this.from.x < this.to.x) {
// console.log(1)
xVia = this.from.x + factor * dy;
yVia = this.from.y - factor * dy;
xVia = this.to.x < xVia ? this.to.x : xVia;
}
else if (this.from.x > this.to.x) {
// console.log(2)
xVia = this.from.x - factor * dy;
yVia = this.from.y - factor * dy;
xVia = this.to.x > xVia ? this.to.x : xVia;
@ -462,13 +500,11 @@ Edge.prototype._getViaCoordinates = function () {
}
else if (this.from.y < this.to.y) {
if (this.from.x < this.to.x) {
// console.log(3)
xVia = this.from.x + factor * dy;
yVia = this.from.y + factor * dy;
xVia = this.to.x < xVia ? this.to.x : xVia;
}
else if (this.from.x > this.to.x) {
// console.log(4, this.from.x, this.to.x)
xVia = this.from.x - factor * dy;
yVia = this.from.y + factor * dy;
xVia = this.to.x > xVia ? this.to.x : xVia;
@ -478,13 +514,11 @@ Edge.prototype._getViaCoordinates = function () {
else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) {
if (this.from.y > this.to.y) {
if (this.from.x < this.to.x) {
// console.log(5)
xVia = this.from.x + factor * dx;
yVia = this.from.y - factor * dx;
yVia = this.to.y > yVia ? this.to.y : yVia;
}
else if (this.from.x > this.to.x) {
// console.log(6)
xVia = this.from.x - factor * dx;
yVia = this.from.y - factor * dx;
yVia = this.to.y > yVia ? this.to.y : yVia;
@ -492,13 +526,11 @@ Edge.prototype._getViaCoordinates = function () {
}
else if (this.from.y < this.to.y) {
if (this.from.x < this.to.x) {
// console.log(7)
xVia = this.from.x + factor * dx;
yVia = this.from.y + factor * dx;
yVia = this.to.y < yVia ? this.to.y : yVia;
}
else if (this.from.x > this.to.x) {
// console.log(8)
xVia = this.from.x - factor * dx;
yVia = this.from.y + factor * dx;
yVia = this.to.y < yVia ? this.to.y : yVia;
@ -534,6 +566,8 @@ Edge.prototype._line = function (ctx) {
// this.via.y = via.y;
ctx.quadraticCurveTo(via.x,via.y,this.to.x, this.to.y);
ctx.stroke();
//ctx.circle(via.x,via.y,2)
//ctx.stroke();
return via;
}
}

+ 46
- 19
lib/network/Groups.js View File

@ -7,6 +7,9 @@ var util = require('../util');
function Groups() {
this.clear();
this.defaultIndex = 0;
this.groupsArray = [];
this.groupIndex = 0;
this.useDefaultGroups = true;
}
@ -14,16 +17,29 @@ function Groups() {
* default constants for group colors
*/
Groups.DEFAULT = [
{border: "#2B7CE9", background: "#97C2FC", highlight: {border: "#2B7CE9", background: "#D2E5FF"}, hover: {border: "#2B7CE9", background: "#D2E5FF"}}, // blue
{border: "#FFA500", background: "#FFFF00", highlight: {border: "#FFA500", background: "#FFFFA3"}, hover: {border: "#FFA500", background: "#FFFFA3"}}, // yellow
{border: "#FA0A10", background: "#FB7E81", highlight: {border: "#FA0A10", background: "#FFAFB1"}, hover: {border: "#FA0A10", background: "#FFAFB1"}}, // red
{border: "#41A906", background: "#7BE141", highlight: {border: "#41A906", background: "#A1EC76"}, hover: {border: "#41A906", background: "#A1EC76"}}, // green
{border: "#E129F0", background: "#EB7DF4", highlight: {border: "#E129F0", background: "#F0B3F5"}, hover: {border: "#E129F0", background: "#F0B3F5"}}, // magenta
{border: "#7C29F0", background: "#AD85E4", highlight: {border: "#7C29F0", background: "#D3BDF0"}, hover: {border: "#7C29F0", background: "#D3BDF0"}}, // purple
{border: "#C37F00", background: "#FFA807", highlight: {border: "#C37F00", background: "#FFCA66"}, hover: {border: "#C37F00", background: "#FFCA66"}}, // orange
{border: "#4220FB", background: "#6E6EFD", highlight: {border: "#4220FB", background: "#9B9BFD"}, hover: {border: "#4220FB", background: "#9B9BFD"}}, // darkblue
{border: "#FD5A77", background: "#FFC0CB", highlight: {border: "#FD5A77", background: "#FFD1D9"}, hover: {border: "#FD5A77", background: "#FFD1D9"}}, // pink
{border: "#4AD63A", background: "#C2FABC", highlight: {border: "#4AD63A", background: "#E6FFE3"}, hover: {border: "#4AD63A", background: "#E6FFE3"}} // mint
{border: "#2B7CE9", background: "#97C2FC", highlight: {border: "#2B7CE9", background: "#D2E5FF"}, hover: {border: "#2B7CE9", background: "#D2E5FF"}}, // 0: blue
{border: "#FFA500", background: "#FFFF00", highlight: {border: "#FFA500", background: "#FFFFA3"}, hover: {border: "#FFA500", background: "#FFFFA3"}}, // 1: yellow
{border: "#FA0A10", background: "#FB7E81", highlight: {border: "#FA0A10", background: "#FFAFB1"}, hover: {border: "#FA0A10", background: "#FFAFB1"}}, // 2: red
{border: "#41A906", background: "#7BE141", highlight: {border: "#41A906", background: "#A1EC76"}, hover: {border: "#41A906", background: "#A1EC76"}}, // 3: green
{border: "#E129F0", background: "#EB7DF4", highlight: {border: "#E129F0", background: "#F0B3F5"}, hover: {border: "#E129F0", background: "#F0B3F5"}}, // 4: magenta
{border: "#7C29F0", background: "#AD85E4", highlight: {border: "#7C29F0", background: "#D3BDF0"}, hover: {border: "#7C29F0", background: "#D3BDF0"}}, // 5: purple
{border: "#C37F00", background: "#FFA807", highlight: {border: "#C37F00", background: "#FFCA66"}, hover: {border: "#C37F00", background: "#FFCA66"}}, // 6: orange
{border: "#4220FB", background: "#6E6EFD", highlight: {border: "#4220FB", background: "#9B9BFD"}, hover: {border: "#4220FB", background: "#9B9BFD"}}, // 7: darkblue
{border: "#FD5A77", background: "#FFC0CB", highlight: {border: "#FD5A77", background: "#FFD1D9"}, hover: {border: "#FD5A77", background: "#FFD1D9"}}, // 8: pink
{border: "#4AD63A", background: "#C2FABC", highlight: {border: "#4AD63A", background: "#E6FFE3"}, hover: {border: "#4AD63A", background: "#E6FFE3"}}, // 9: mint
{border: "#990000", background: "#EE0000", highlight: {border: "#BB0000", background: "#FF3333"}, hover: {border: "#BB0000", background: "#FF3333"}}, // 10:bright red
{border: "#FF6000", background: "#FF6000", highlight: {border: "#FF6000", background: "#FF6000"}, hover: {border: "#FF6000", background: "#FF6000"}}, // 12: real orange
{border: "#97C2FC", background: "#2B7CE9", highlight: {border: "#D2E5FF", background: "#2B7CE9"}, hover: {border: "#D2E5FF", background: "#2B7CE9"}}, // 13: blue
{border: "#399605", background: "#255C03", highlight: {border: "#399605", background: "#255C03"}, hover: {border: "#399605", background: "#255C03"}}, // 14: green
{border: "#B70054", background: "#FF007E", highlight: {border: "#B70054", background: "#FF007E"}, hover: {border: "#B70054", background: "#FF007E"}}, // 15: magenta
{border: "#AD85E4", background: "#7C29F0", highlight: {border: "#D3BDF0", background: "#7C29F0"}, hover: {border: "#D3BDF0", background: "#7C29F0"}}, // 16: purple
{border: "#4557FA", background: "#000EA1", highlight: {border: "#6E6EFD", background: "#000EA1"}, hover: {border: "#6E6EFD", background: "#000EA1"}}, // 17: darkblue
{border: "#FFC0CB", background: "#FD5A77", highlight: {border: "#FFD1D9", background: "#FD5A77"}, hover: {border: "#FFD1D9", background: "#FD5A77"}}, // 18: pink
{border: "#C2FABC", background: "#74D66A", highlight: {border: "#E6FFE3", background: "#74D66A"}, hover: {border: "#E6FFE3", background: "#74D66A"}}, // 19: mint
{border: "#EE0000", background: "#990000", highlight: {border: "#FF3333", background: "#BB0000"}, hover: {border: "#FF3333", background: "#BB0000"}}, // 20:bright red
];
@ -54,12 +70,22 @@ Groups.prototype.clear = function () {
Groups.prototype.get = function (groupname) {
var group = this.groups[groupname];
if (group == undefined) {
// create new group
var index = this.defaultIndex % Groups.DEFAULT.length;
this.defaultIndex++;
group = {};
group.color = Groups.DEFAULT[index];
this.groups[groupname] = group;
if (this.useDefaultGroups === false && this.groupsArray.length > 0) {
// create new group
var index = this.groupIndex % this.groupsArray.length;
this.groupIndex++;
group = {};
group.color = this.groups[this.groupsArray[index]];
this.groups[groupname] = group;
}
else {
// create new group
var index = this.defaultIndex % Groups.DEFAULT.length;
this.defaultIndex++;
group = {};
group.color = Groups.DEFAULT[index];
this.groups[groupname] = group;
}
}
return group;
@ -67,13 +93,14 @@ Groups.prototype.get = function (groupname) {
/**
* Add a custom group style
* @param {String} groupname
* @param {String} groupName
* @param {Object} style An object containing borderColor,
* backgroundColor, etc.
* @return {Object} group The created group object
*/
Groups.prototype.add = function (groupname, style) {
this.groups[groupname] = style;
Groups.prototype.add = function (groupName, style) {
this.groups[groupName] = style;
this.groupsArray.push(groupName);
return style;
};

+ 119
- 42
lib/network/Network.js View File

@ -130,7 +130,7 @@ function Network (container, data, options) {
altLength: undefined
},
inheritColor: "from", // to, from, false, true (== from)
useGradients: false
useGradients: false // release in 4.0
},
configurePhysics:false,
physics: {
@ -236,7 +236,8 @@ function Network (container, data, options) {
hideNodesOnDrag: false,
width : '100%',
height : '100%',
selectable: true
selectable: true,
useDefaultGroups: true
};
this.constants = util.extend({}, this.defaultOptions);
this.pixelRatio = 1;
@ -258,13 +259,14 @@ function Network (container, data, options) {
this.lockedOnNodeId = null;
this.lockedOnNodeOffset = null;
this.touchTime = 0;
this.redrawRequested = false;
// Node variables
var network = this;
this.groups = new Groups(); // object with groups
this.images = new Images(); // object with images
this.images.setOnloadCallback(function (status) {
network._redraw();
network._requestRedraw();
});
// keyboard navigation variables
@ -676,6 +678,7 @@ Network.prototype.setOptions = function (options) {
util.selectiveNotDeepExtend(['color'],this.constants.nodes, options.nodes);
util.selectiveNotDeepExtend(['color','length'],this.constants.edges, options.edges);
this.groups.useDefaultGroups = this.constants.useDefaultGroups;
if (options.physics) {
util.mergeOptions(this.constants.physics, options.physics,'barnesHut');
util.mergeOptions(this.constants.physics, options.physics,'repulsion');
@ -806,6 +809,10 @@ Network.prototype.setOptions = function (options) {
this._markAllEdgesAsDirty();
this.setSize(this.constants.width, this.constants.height);
this.moving = true;
if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) {
this._resetLevels();
this._setupHierarchicalLayout();
}
this.start();
}
};
@ -1355,10 +1362,20 @@ Network.prototype._onMouseWheel = function(event) {
Network.prototype._onMouseMoveTitle = function (event) {
var gesture = hammerUtil.fakeGesture(this, event);
var pointer = this._getPointer(gesture.center);
var popupVisible = false;
// check if the previously selected node is still selected
if (this.popupObj) {
this._checkHidePopup(pointer);
if (this.popup !== undefined) {
if (this.popup.hidden === false) {
this._checkHidePopup(pointer);
}
// if the popup was not hidden above
if (this.popup.hidden === false) {
popupVisible = true;
this.popup.setPosition(pointer.x + 3,pointer.y - 5)
this.popup.show();
}
}
// if we bind the keyboard to the div, we have to highlight it to use it. This highlights it on mouse over
@ -1366,20 +1383,20 @@ Network.prototype._onMouseMoveTitle = function (event) {
this.frame.focus();
}
// start a timeout that will check if the mouse is positioned above
// an element
var me = this;
var checkShow = function() {
me._checkShowPopup(pointer);
};
if (this.popupTimer) {
clearInterval(this.popupTimer); // stop any running calculationTimer
}
if (!this.drag.dragging) {
this.popupTimer = setTimeout(checkShow, this.constants.tooltip.delay);
// start a timeout that will check if the mouse is positioned above an element
if (popupVisible === false) {
var me = this;
var checkShow = function () {
me._checkShowPopup(pointer);
};
if (this.popupTimer) {
clearInterval(this.popupTimer); // stop any running calculationTimer
}
if (!this.drag.dragging) {
this.popupTimer = setTimeout(checkShow, this.constants.tooltip.delay);
}
}
/**
* Adding hover highlights
*/
@ -1431,8 +1448,9 @@ Network.prototype._checkShowPopup = function (pointer) {
};
var id;
var lastPopupNode = this.popupObj;
var previousPopupObjId = this.popupObj === undefined ? "" : this.popupObj.id;
var nodeUnderCursor = false;
var popupType = "node";
if (this.popupObj == undefined) {
// search the nodes for overlap, select the top one in case of multiple nodes
@ -1474,23 +1492,26 @@ Network.prototype._checkShowPopup = function (pointer) {
if (overlappingEdges.length > 0) {
this.popupObj = this.edges[overlappingEdges[overlappingEdges.length - 1]];
popupType = "edge";
}
}
if (this.popupObj) {
// show popup message window
if (this.popupObj != lastPopupNode) {
var me = this;
if (!me.popup) {
me.popup = new Popup(me.frame, me.constants.tooltip);
if (this.popupObj.id != previousPopupObjId) {
if (this.popup === undefined) {
this.popup = new Popup(this.frame, this.constants.tooltip);
}
this.popup.popupTargetType = popupType;
this.popup.popupTargetId = this.popupObj.id;
// adjust a small offset such that the mouse cursor is located in the
// bottom left location of the popup, and you can easily move over the
// popup area
me.popup.setPosition(pointer.x - 3, pointer.y - 3);
me.popup.setText(me.popupObj.getTitle());
me.popup.show();
this.popup.setPosition(pointer.x + 3, pointer.y - 5);
this.popup.setText(this.popupObj.getTitle());
this.popup.show();
}
}
else {
@ -1502,18 +1523,38 @@ Network.prototype._checkShowPopup = function (pointer) {
/**
* Check if the popup must be hided, which is the case when the mouse is no
* Check if the popup must be hidden, which is the case when the mouse is no
* longer hovering on the object
* @param {{x:Number, y:Number}} pointer
* @private
*/
Network.prototype._checkHidePopup = function (pointer) {
if (!this.popupObj || !this._getNodeAt(pointer) ) {
this.popupObj = undefined;
if (this.popup) {
this.popup.hide();
var pointerObj = {
left: this._XconvertDOMtoCanvas(pointer.x),
top: this._YconvertDOMtoCanvas(pointer.y),
right: this._XconvertDOMtoCanvas(pointer.x),
bottom: this._YconvertDOMtoCanvas(pointer.y)
};
var stillOnObj = false;
if (this.popup.popupTargetType == 'node') {
stillOnObj = this.nodes[this.popup.popupTargetId].isOverlappingWith(pointerObj);
if (stillOnObj === true) {
var overNode = this._getNodeAt(pointer);
stillOnObj = overNode.id == this.popup.popupTargetId;
}
}
else {
if (this._getNodeAt(pointer) === null) {
stillOnObj = this.edges[this.popup.popupTargetId].isOverlappingWith(pointerObj);
}
}
if (stillOnObj === false) {
this.popupObj = undefined;
this.popup.hide();
}
};
@ -1937,7 +1978,23 @@ Network.prototype.redraw = function() {
* @param hidden | used to get the first estimate of the node sizes. only the nodes are drawn after which they are quickly drawn over.
* @private
*/
Network.prototype._redraw = function(hidden) {
Network.prototype._requestRedraw = function(hidden) {
if (this.redrawRequested !== true) {
this.redrawRequested = true;
if (this.requiresTimeout === true) {
window.setTimeout(this._redraw.bind(this, hidden),0);
}
else {
window.requestAnimationFrame(this._redraw.bind(this, hidden, true));
}
}
};
Network.prototype._redraw = function(hidden, requested) {
if (hidden === undefined) {
hidden = false;
}
this.redrawRequested = false;
var ctx = this.frame.canvas.getContext('2d');
ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0);
@ -1961,7 +2018,7 @@ Network.prototype._redraw = function(hidden) {
"y": this._YconvertDOMtoCanvas(this.frame.canvas.clientHeight)
};
if (!(hidden == true)) {
if (hidden === false) {
this._doInAllSectors("_drawAllSectorNodes", ctx);
if (this.drag.dragging == false || this.drag.dragging === undefined || this.constants.hideEdgesOnDrag == false) {
this._doInAllSectors("_drawEdges", ctx);
@ -1972,7 +2029,7 @@ Network.prototype._redraw = function(hidden) {
this._doInAllSectors("_drawNodes",ctx,false);
}
if (!(hidden == true)) {
if (hidden === false) {
if (this.controlNodesActive == true) {
this._doInAllSectors("_drawControlNodes", ctx);
}
@ -1984,10 +2041,10 @@ Network.prototype._redraw = function(hidden) {
// restore original scaling and translation
ctx.restore();
if (hidden == true) {
if (hidden === true) {
ctx.clearRect(0, 0, w, h);
}
};
}
/**
* Set the translation of the network
@ -2193,10 +2250,6 @@ Network.prototype._stabilize = function() {
var count = 0;
while (this.moving && count < this.constants.stabilizationIterations) {
this._physicsTick();
// TODO: cleanup
//if (count % 100 == 0) {
// console.log("stabilizationIterations",count);
//}
count++;
}
@ -2378,6 +2431,11 @@ Network.prototype._animationStep = function() {
// reset the timer so a new scheduled animation step can be set
this.timer = undefined;
if (this.requiresTimeout == true) {
// this schedules a new animation step
this.start();
}
// handle the keyboad movement
this._handleNavigation();
@ -2402,8 +2460,10 @@ Network.prototype._animationStep = function() {
this._redraw();
this.renderTime = Date.now() - renderStartTime;
// this schedules a new animation step
this.start();
if (this.requiresTimeout == false) {
// this schedules a new animation step
this.start();
}
};
if (typeof window !== 'undefined') {
@ -2429,7 +2489,7 @@ Network.prototype.start = function() {
}
}
else {
this._redraw();
this._requestRedraw();
// this check is to ensure that the network does not emit these events if it was already stabilized and setOptions is called (setting moving to true and calling start())
if (this.stabilizationIterations > 1) {
// trigger the "stabilized" event.
@ -2880,4 +2940,21 @@ Network.prototype.getConnectedNodes = function(nodeId) {
return nodeList;
}
Network.prototype.getEdgesFromNode = function(nodeId) {
var edgesList = [];
if (this.nodes[nodeId] !== undefined) {
var node = this.nodes[nodeId];
for (var i = 0; i < node.edges.length; i++) {
edgesList.push(node.edges[i].id);
}
}
return edgesList;
}
Network.prototype.generateColorObject = function(color) {
return util.parseColor(color);
}
module.exports = Network;

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

@ -78,7 +78,7 @@ function Node(properties, imagelist, grouplist, networkConstants) {
this.clusterSizeWidthFactor = networkConstants.clustering.nodeScaling.width;
this.clusterSizeHeightFactor = networkConstants.clustering.nodeScaling.height;
this.clusterSizeRadiusFactor = networkConstants.clustering.nodeScaling.radius;
this.maxNodeSizeIncrements = networkConstants.clustering.maxNodeSizeIncrements;
this.maxNodeSizeIncrements = networkConstants.clustering.maxNodeSizeIncrements;
this.growthIndicator = 0;
// variables to tell the node about the network.
@ -1015,31 +1015,30 @@ Node.prototype._drawText = function (ctx) {
Node.prototype._resizeIcon = function (ctx) {
if (!this.width) {
var margin = 5;
var textSize =
var iconSize =
{
width: 1,
height: Number(this.options.iconSize) + 4
width: Number(this.options.iconSize),
height: Number(this.options.iconSize)
};
this.width = textSize.width + 2 * margin;
this.height = textSize.height + 2 * margin;
this.width = iconSize.width + 2 * margin;
this.height = iconSize.height + 2 * margin;
// scaling used for clustering
this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor;
this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor;
this.options.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor;
this.growthIndicator = this.width - (textSize.width + 2 * margin);
this.growthIndicator = this.width - (iconSize.width + 2 * margin);
}
};
Node.prototype._drawIcon = function (ctx) {
this._resizeIcon(ctx);
this.options.iconSize = this.options.iconSize || 50;
this.options.iconSize = this.options.iconSize || 50;
this.left = this.x - this.width / 2;
this.top = this.y - this.height / 2;
this._icon(ctx, this.options.icon, this.x, this.y);
this._icon(ctx);
this.boundingBox.top = this.y - this.options.iconSize/2;
@ -1048,7 +1047,8 @@ Node.prototype._drawIcon = function (ctx) {
this.boundingBox.bottom = this.y + this.options.iconSize/2;
if (this.label) {
this._label(ctx, this.label, this.x, this.y + this.height / 2, 'top', true);
var iconTextSpacing = 5;
this._label(ctx, this.label, this.x, this.y + this.height / 2 + iconTextSpacing, 'top', true);
this.boundingBox.left = Math.min(this.boundingBox.left, this.labelDimensions.left);
this.boundingBox.right = Math.max(this.boundingBox.right, this.labelDimensions.left + this.labelDimensions.width);
@ -1056,10 +1056,10 @@ Node.prototype._drawIcon = function (ctx) {
}
};
Node.prototype._icon = function (ctx, icon, x, y) {
Node.prototype._icon = function (ctx) {
var relativeIconSize = Number(this.options.iconSize) * this.networkScale;
if (icon && relativeIconSize > this.options.fontDrawThreshold - 1) {
if (this.options.icon && relativeIconSize > this.options.fontDrawThreshold - 1) {
var iconSize = Number(this.options.iconSize);
@ -1069,7 +1069,7 @@ Node.prototype._icon = function (ctx, icon, x, y) {
ctx.fillStyle = this.options.iconColor || "black";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(icon, x, y);
ctx.fillText(this.options.icon, this.x, this.y);
}
};

+ 4
- 1
lib/network/Popup.js View File

@ -40,8 +40,9 @@ function Popup(container, x, y, text, style) {
this.x = 0;
this.y = 0;
this.padding = 5;
this.hidden = false;
if (x !== undefined && y !== undefined ) {
if (x !== undefined && y !== undefined) {
this.setPosition(x, y);
}
if (text !== undefined) {
@ -116,6 +117,7 @@ Popup.prototype.show = function (show) {
this.frame.style.left = left + "px";
this.frame.style.top = top + "px";
this.frame.style.visibility = "visible";
this.hidden = false;
}
else {
this.hide();
@ -126,6 +128,7 @@ Popup.prototype.show = function (show) {
* Hide the popup window
*/
Popup.prototype.hide = function () {
this.hidden = true;
this.frame.style.visibility = "hidden";
};

+ 2
- 2
lib/network/mixins/SelectionMixin.js View File

@ -512,7 +512,7 @@ exports._handleTap = function(pointer) {
canvas: {x: this._XconvertDOMtoCanvas(pointer.x), y: this._YconvertDOMtoCanvas(pointer.y)}
}
this.emit("click", properties);
this._redraw();
this._requestRedraw();
};
@ -556,7 +556,7 @@ exports._handleOnHold = function(pointer) {
this._selectObject(edge,true);
}
}
this._redraw();
this._requestRedraw();
};

+ 103
- 4
lib/timeline/Core.js View File

@ -7,6 +7,7 @@ var Range = require('./Range');
var ItemSet = require('./component/ItemSet');
var Activator = require('../shared/Activator');
var DateUtil = require('./DateUtil');
var CustomTime = require('./component/CustomTime');
/**
* Create a timeline visualization
@ -281,25 +282,123 @@ Core.prototype.destroy = function () {
/**
* Set a custom time bar
* @param {Date} time
* @param {int} id
*/
Core.prototype.setCustomTime = function (time) {
Core.prototype.setCustomTime = function (time, id) {
if (!this.customTime) {
throw new Error('Cannot get custom time: Custom time bar is not enabled');
}
this.customTime.setCustomTime(time);
var barId = id || 0;
this.components.forEach(function (element, index, components) {
if (element instanceof CustomTime && element.options.id === barId) {
element.setCustomTime(time);
}
});
};
/**
* Retrieve the current custom time.
* @return {Date} customTime
* @param {int} id
*/
Core.prototype.getCustomTime = function() {
Core.prototype.getCustomTime = function(id) {
if (!this.customTime) {
throw new Error('Cannot get custom time: Custom time bar is not enabled');
}
return this.customTime.getCustomTime();
var barId = id || 0,
customTime = this.customTime.getCustomTime();
this.components.forEach(function (element, index, components) {
if (element instanceof CustomTime && element.options.id === barId) {
customTime = element.getCustomTime();
}
});
return customTime;
};
/**
* Add custom vertical bar
* @param {Date | String | Number} time A Date, unix timestamp, or
* ISO date string. Time point where the new bar should be placed
* @param {Number | String} ID of the new bar
* @return {Number | String} ID of the new bar
*/
Core.prototype.addCustomTime = function (time, id) {
if (!this.currentTime) {
throw new Error('Option showCurrentTime must be true');
}
if (time === undefined) {
throw new Error('Time parameter for the custom bar must be provided');
}
var ts = util.convert(time, 'Date').valueOf(),
numIds, customTime, customBarId;
// All bar IDs are kept in 1 array, mixed types
// Bar with ID 0 is the default bar.
if (!this.customBarIds || this.customBarIds.constructor !== Array) {
this.customBarIds = [0];
}
// If the ID is not provided, generate one, otherwise just use it
if (id === undefined) {
numIds = this.customBarIds.filter(function (element) {
return util.isNumber(element);
});
customBarId = numIds.length > 0 ? Math.max.apply(null, numIds) + 1 : 1;
} else {
// Check for duplicates
this.customBarIds.forEach(function (element) {
if (element === id) {
throw new Error('Custom time ID already exists');
}
});
customBarId = id;
}
this.customBarIds.push(customBarId);
customTime = new CustomTime(this.body, {
showCustomTime : true,
time : ts,
id : customBarId
});
this.components.push(customTime);
this.redraw();
return customBarId;
};
/**
* Remove previously added custom bar
* @param {int} id ID of the custom bar to be removed
* @return {boolean} True if the bar exists and is removed, false otherwise
*/
Core.prototype.removeCustomTime = function (id) {
var me = this;
this.components.forEach(function (bar, index, components) {
if (bar instanceof CustomTime && bar.options.id === id) {
// Only the lines added by the user will be removed
if (bar.options.id !== 0) {
me.customBarIds.splice(me.customBarIds.indexOf(id), 1);
components.splice(index, 1);
bar.destroy();
}
}
});
};

+ 16
- 3
lib/timeline/component/CustomTime.js View File

@ -20,11 +20,17 @@ function CustomTime (body, options) {
this.defaultOptions = {
showCustomTime: false,
locales: locales,
locale: 'en'
locale: 'en',
id: 0
};
this.options = util.extend({}, this.defaultOptions);
this.customTime = new Date();
if (options && options.time) {
this.customTime = options.time;
} else {
this.customTime = new Date();
}
this.eventParams = {}; // stores state parameters while dragging the bar
// create the DOM
@ -43,7 +49,12 @@ CustomTime.prototype = new Component();
CustomTime.prototype.setOptions = function(options) {
if (options) {
// copy all options that we know
util.selectiveExtend(['showCustomTime', 'locale', 'locales'], this.options, options);
util.selectiveExtend(['showCustomTime', 'locale', 'locales', 'id'], this.options, options);
// Triggered by addCustomTimeBar, redraw to add new bar
if (this.options.id) {
this.redraw();
}
}
};
@ -169,6 +180,7 @@ CustomTime.prototype._onDrag = function (event) {
// fire a timechange event
this.body.emitter.emit('timechange', {
id: this.options.id,
time: new Date(this.customTime.valueOf())
});
@ -186,6 +198,7 @@ CustomTime.prototype._onDragEnd = function (event) {
// fire a timechanged event
this.body.emitter.emit('timechanged', {
id: this.options.id,
time: new Date(this.customTime.valueOf())
});

+ 9
- 1
lib/timeline/component/LineGraph.js View File

@ -981,9 +981,17 @@ LineGraph.prototype._convertYcoordinates = function (datapoints, group) {
}
for (var i = 0; i < datapoints.length; i++) {
var labelValue;
//if (datapoints[i].label) {
// labelValue = datapoints[i].label;
//}
//else {
// labelValue = null;
//}
labelValue = datapoints[i].label ? datapoints[i].label : null;
xValue = toScreen(datapoints[i].x) + this.props.width;
yValue = Math.round(axis.convertValue(datapoints[i].y));
extractedData.push({x: xValue, y: yValue});
extractedData.push({x: xValue, y: yValue, label:labelValue});
}
group.setZeroPosition(Math.min(svgHeight, axis.convertValue(0)));

+ 1
- 1
lib/timeline/component/graph2d_types/points.js View File

@ -35,7 +35,7 @@ Points.prototype.draw = function(dataset, group, framework, offset) {
Points.draw = function (dataset, group, framework, offset) {
if (offset === undefined) {offset = 0;}
for (var i = 0; i < dataset.length; i++) {
DOMutil.drawPoint(dataset[i].x + offset, dataset[i].y, group, framework.svgElements, framework.svg);
DOMutil.drawPoint(dataset[i].x + offset, dataset[i].y, group, framework.svgElements, framework.svg, dataset[i].label);
}
};

Loading…
Cancel
Save