Browse Source

unified options with timeline, updated docs and configurator

flowchartTest
Alex de Mulder 9 years ago
parent
commit
4d57a6df7c
22 changed files with 1780 additions and 1595 deletions
  1. +1505
    -1420
      dist/vis.js
  2. +10
    -1
      docs/network/canvas.html
  3. +5
    -2
      docs/network/interaction.html
  4. +2
    -2
      docs/network/manipulation.html
  5. +2
    -2
      docs/network/selection.html
  6. +18
    -17
      docs/network/view.html
  7. +2
    -1
      examples/graph2d/05_bothAxis.html
  8. +2
    -1
      examples/graph2d/10_barsSideBySide.html
  9. +3
    -2
      examples/graph2d/11_barsSideBySideGroups.html
  10. +1
    -2
      examples/network/01_basic_usage.html
  11. +1
    -1
      lib/network/Network.js
  12. +25
    -3
      lib/network/modules/Canvas.js
  13. +6
    -13
      lib/network/modules/ConfigurationSystem.js
  14. +19
    -17
      lib/network/modules/InteractionHandler.js
  15. +28
    -38
      lib/network/modules/ManipulationSystem.js
  16. +2
    -1
      lib/network/modules/SelectionHandler.js
  17. +1
    -1
      lib/network/modules/View.js
  18. +8
    -19
      lib/network/modules/components/AllOptions.js
  19. +4
    -0
      lib/timeline/component/GraphGroup.js
  20. +21
    -25
      lib/timeline/component/LineGraph.js
  21. +27
    -27
      lib/timeline/component/graph2d_types/bar.js
  22. +88
    -0
      lib/timeline/component/graph2d_types/line.js

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


+ 10
- 1
docs/network/canvas.html View File

@ -92,7 +92,8 @@
var options = {
canvas:{
width:'100%', // other format: '400px'
height:'100%'
height:'100%',
autoResize: true
}
}
@ -118,6 +119,14 @@ network.setOptions(options);
<td class="mid"><code>'100%'</code></td>
<td>the height of the canvas. Can be in percentages or pixels (ie. <code>'400px'</code>).</td>
</tr>
<tr>
<td>autoResize</td>
<td class="mid">Boolean</td>
<td class="mid"><code>true</code></td>
<td>If true, the Network will automatically detect when its container is resized, and redraw itself
accordingly. If false, the Network can be forced to repaint after its container has been resized
using the function redraw() and setSize().</td>
</tr>
</table>
<h3>Methods</h3>

+ 5
- 2
docs/network/interaction.html View File

@ -120,7 +120,7 @@ network.setOptions(options);
<tr><td>zoomView</td> <td class="mid">Boolean</td> <td class="mid"><code>true</code></td> <td>When true, the user can zoom in.</td></tr>
<tr><td>hoverEnabled</td> <td class="mid">Boolean</td> <td class="mid"><code>false</code></td> <td>When true, the nodes use their hover colors when the mouse moves over them.</td></tr>
<tr><td>navigationButtons</td> <td class="mid">Boolean</td> <td class="mid"><code>false</code></td> <td>When true, navigation buttons are drawn on the network canvas. These are HTML buttons and can be completely customized using CSS.</td></tr>
<tr><td>tooltipDelay</td> <td class="mid">Object</td> <td class="mid"><code>Object</code></td> <td>When nodes or edges have a defined <code>'title'</code> field, this can be shown as a pop-up tooltip. The tooltip itself is an HTML element that can be fully styled using CSS. The delay is the amount of time in milliseconds it takes before the tooltip is shown.</td></tr>
<tr><td>tooltipDelay</td> <td class="mid">Number</td> <td class="mid"><code>Object</code></td> <td>When nodes or edges have a defined <code>'title'</code> field, this can be shown as a pop-up tooltip. The tooltip itself is an HTML element that can be fully styled using CSS. The delay is the amount of time in milliseconds it takes before the tooltip is shown.</td></tr>
<tr class='toggle collapsible' onclick="toggleTable('optionTable','keyboard', this);"><td><span parent="keyboard" class="right-caret"></span> keyboard</td> <td class="mid">Object or Boolean</td> <td class="mid"><code>Object</code></td> <td>When true, the keyboard shortcuts are enabled with the default settings. For further customization, you can supply an object.</td></tr>
<tr parent="keyboard" class="hidden"><td class="indent">keyboard.enabled</td> <td class="mid">Boolean</td> <td class="mid"><code>false</code></td> <td>Toggle the usage of the keyboard shortcuts. If this option is not defined, it is set to true if any of the properties in this object are defined.</td></tr>
<tr parent="keyboard" class="hidden"><td class="indent">keyboard.speed.x</td> <td class="mid">Number</td> <td class="mid"><code>1</code></td> <td>The speed at which the view moves in the x direction on pressing a key or pressing a navigation button.</td></tr>
@ -141,6 +141,7 @@ network.setOptions(options);
{
nodes: [Array of selected nodeIds],
edges: [Array of selected edgeIds],
event: [Object] original click event,
pointer: {
DOM: {x:pointer_x, y:pointer_y},
canvas: {x:canvas_x, y:canvas_y}
@ -149,7 +150,7 @@ network.setOptions(options);
</pre>
</td><td>Fired when the user clicks the mouse or taps on a touchscreen device.</td></tr>
<tr><td>doubleClick</td> <td class="mid">same as <code>click</code>.</td><td>Fired when the user double clicks the mouse or double taps on a touchscreen device. Since a double click is in fact 2 clicks, 2 click events are fired, followed by a double click event. If you do not want to use the click events if a double click event is fired, just check the time between click events before processing them.</td></tr>
<tr><td>rightClick</td> <td class="mid">same as <code>click</code>.</td><td>Fired when the user click on the canvas with the right mouse button. The right mouse button does not select by default. You can use <a href="./selection.html">getNodeAt</a> to select the node if you want.</td></tr>
<tr><td>oncontext</td> <td class="mid">same as <code>click</code>.</td><td>Fired when the user click on the canvas with the right mouse button. The right mouse button does not select by default. You can use <a href="./selection.html">getNodeAt</a> to select the node if you want.</td></tr>
<tr><td>hold</td> <td class="mid">same as <code>click</code>.</td><td>Fired when the user clicks and holds the mouse or taps and holds on a touchscreen device. A click event is also fired in this case.</td></tr>
<tr><td>release</td> <td class="mid">same as <code>click</code>.</td><td>Fired after drawing on the canvas has been completed. Can be used to draw on top of the network.</td></tr>
<tr><td>select</td> <td class="mid">same as <code>click</code>.</td><td>Fired when the selection has changed by user action. This means a node or edge has been selected, added to the selection or deselected. <b>All select events are only triggerd on click and hold</b>.</td></tr>
@ -159,6 +160,7 @@ network.setOptions(options);
{
nodes: [Array of selected nodeIds],
edges: [Array of selected edgeIds],
event: [Object] original click event,
pointer: {
DOM: {x:pointer_x, y:pointer_y},
canvas: {x:canvas_x, y:canvas_y}
@ -172,6 +174,7 @@ network.setOptions(options);
</pre></td><td>Fired when a node (or nodes) has (or have) been deselected by the user. The previous selection is the list of nodes and edges that were selected before the last user event.</td></tr>
<tr><td>deselectEdge</td><td class="mid">same as <code>deselectNode</code>.</td><td>Fired when a edge (or edges) has (or have) been deselected by the user. The previous selection is the list of nodes and edges that were selected before the last user event.</td></tr>
<tr><td>dragStart</td> <td class="mid">same as <code>click</code>.</td><td>Fired when starting a drag.</td></tr>
<tr><td>dragging</td> <td class="mid">same as <code>click</code>.</td><td>Fired when dragging node(s) or the view.</td></tr>
<tr><td>dragEnd</td> <td class="mid">same as <code>click</code>.</td><td>Fired when the drag has finished.</td></tr>
<tr><td>zoom</td> <td class="mid"><code>{direction:'+'/'-'}</code></td><td>Fired when the user zooms in or out. The properties tell you which direction the zoom is in.</td></tr>
<tr><td>showPopup</td> <td class="mid"><code>id of item corresponding to popup</code></td><td>Fired when the popup is shown.</td></tr>

+ 2
- 2
docs/network/manipulation.html View File

@ -83,12 +83,12 @@
<pre class="prettyprint lang-js options top hidden" id="fullOptions">
// these are all options in full.
var options = {
manupulation: {
manipulation: {
enabled: false,
initiallyActive: false,
locale: 'en',
locales: locales,
functionality:{
functionality: {
addNode: true,
addEdge: true,
editNode: true,

+ 2
- 2
docs/network/selection.html View File

@ -83,7 +83,7 @@
<pre class="prettyprint lang-js options hidden" id="fullOptions">
var options = {
selection:{
select: true,
selectable: true,
selectConnectedEdges: false
}
}
@ -93,7 +93,7 @@ network.setOptions(options);
<p>All of the individual options are explained here:</p>
<table class="moduleTable">
<tr class="header"><td>name</td><td>type</td><td>default</td><td>description</td></tr>
<tr><td>select</td> <td class="mid">Boolean</td><td class="mid"><code>true</code></td><td>When true, the nodes and edges can be selected by the user.</td></tr>
<tr><td>selectable</td> <td class="mid">Boolean</td><td class="mid"><code>true</code></td><td>When true, the nodes and edges can be selected by the user.</td></tr>
<tr><td>selectConnectedEdges</td> <td class="mid">Boolean</td><td class="mid"><code>true</code></td><td>When true, on selecting a node, its connecting edges are highlighted.</td></tr>
</table>

+ 18
- 17
docs/network/view.html View File

@ -92,6 +92,23 @@
The other options are explained in the <code>moveTo()</code> description above.
All options are optional for zoomExtent.
</td></tr>
<tr><td>focus(<br>
&nbsp;&nbsp;&nbsp;<code>String nodeId</code>,<br>
&nbsp;&nbsp;&nbsp;<code>[Object options]</code><br>)</td><td class="mid">none</td>
<td>You can focus on a node with this function. What that means is the view will lock onto that node, if it is moving, the view will also move accordingly. If the view is dragged by the user, the focus is broken.
You can supply options to customize the effect:
<pre class="code">
{
scale: Number,
offset: {x:Number, y:Number}
locked: boolean
animation: { // -------------------> can be a boolean too!
duration: Number
easingFunction: String
}
}
</pre>
All options except for locked are explained in the <code>moveTo()</code> description below. Locked denotes whether or not the view remains locked to the node once the zoom-in animation is finished. Default value is true. The options object is optional in the focus method.</td></tr>
<tr><td>moveTo(<code>Object options</code>)</td> <td class="mid">none</td><td>You can animate or move the camera using the moveTo method. Options are:
<pre class="code">
{
@ -113,23 +130,7 @@
</td></tr>
<tr><td>focusOnNode(<br>
&nbsp;&nbsp;&nbsp;<code>String nodeId</code>,<br>
&nbsp;&nbsp;&nbsp;<code>[Object options]</code><br>)</td><td class="mid">none</td>
<td>You can focus on a node with this function. What that means is the view will lock onto that node, if it is moving, the view will also move accordingly. If the view is dragged by the user, the focus is broken.
You can supply options to customize the effect:
<pre class="code">
{
scale: Number,
offset: {x:Number, y:Number}
locked: boolean
animation: { // -------------------> can be a boolean too!
duration: Number
easingFunction: String
}
}
</pre>
All options except for locked are explained in the <code>moveTo()</code> description above. Locked denotes whether or not the view remains locked to the node once the zoom-in animation is finished. Default value is true. The options object is optional in focusOnNode.</td></tr>
<tr><td>releaseNode()</td><td class="mid">none</td><td>Programatically release the focussed node.</td></tr>

+ 2
- 1
examples/graph2d/05_bothAxis.html View File

@ -2,6 +2,7 @@
<html>
<head>
<title>Graph2d | Both Axis Example</title>
<link href="../../dist/vis.css" rel="stylesheet" type="text/css" />
<style type="text/css">
body, html {
font-family: sans-serif;
@ -32,7 +33,7 @@
}
</style>
<!--IMPORTANT: import the vis css after the custom styles to avoid overriding styles-->
<link href="../../dist/vis.css" rel="stylesheet" type="text/css" />
<script src="../../dist/vis.js"></script>
<script>(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)})(window,document,'script','//www.google-analytics.com/analytics.js','ga');ga('create', 'UA-61231638-1', 'auto');ga('send', 'pageview');</script></head>

+ 2
- 1
examples/graph2d/10_barsSideBySide.html View File

@ -49,7 +49,8 @@
var dataset = new vis.DataSet(items);
var options = {
style:'bar',
barChart: {width:50, align:'center'}, // align: left, center, right
stack:false,
barChart: {width:50, align:'center', sideBySide:false}, // align: left, center, right
drawPoints: false,
dataAxis: {
icons:true

+ 3
- 2
examples/graph2d/11_barsSideBySideGroups.html View File

@ -49,7 +49,7 @@
{x: '2014-06-12', y: 15, group:1},
{x: '2014-06-13', y: 34, group:1},
{x: '2014-06-14', y: 24, group:1},
{x: '2014-06-15', y: 5, group:1},
{x: '2014-06-15', y: 5, group:1},
{x: '2014-06-16', y: 12, group:1},
{x: '2014-06-11', y: 22, group:2},
{x: '2014-06-12', y: 14, group:2},
@ -62,7 +62,8 @@
var dataset = new vis.DataSet(items);
var options = {
style:'bar',
barChart: {width:50, align:'center', handleOverlap:'sideBySide'}, // align: left, center, right
stack:true,
barChart: {width:50, align:'center', sideBySide:true}, // align: left, center, right
drawPoints: false,
dataAxis: {
icons:true

+ 1
- 2
examples/network/01_basic_usage.html View File

@ -44,9 +44,8 @@
edges: edges
};
var options = {
configure:true,
configure:'nodes',
// physics:{barnesHut:{gravitationslPull:34}, solver:'banana'},
groups:{'bla':{color:{backsground:'red'}, borderWidth:5}}
}
var network = new vis.Network(container, data, options);
// network.setOptions({nodes:{color:'red'}})

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

@ -428,7 +428,7 @@ Network.prototype.getScale = function() {this.view.getScale.apply(thi
Network.prototype.getPosition = function() {this.view.getPosition.apply(this.view,arguments);};
Network.prototype.fit = function() {this.view.fit.apply(this.view,arguments);};
Network.prototype.moveTo = function() {this.view.moveTo.apply(this.view,arguments);};
Network.prototype.focusOnNode = function() {this.view.focusOnNode.apply(this.view,arguments);};
Network.prototype.focus = function() {this.view.focus.apply(this.view,arguments);};
Network.prototype.releaseNode = function() {this.view.releaseNode.apply(this.view,arguments);};
module.exports = Network;

+ 25
- 3
lib/network/modules/Canvas.js View File

@ -14,11 +14,13 @@ class Canvas {
constructor(body) {
this.body = body;
this.pixelRatio = 1;
this.resizeTimer = undefined;
this.options = {};
this.defaultOptions = {
width:'100%',
height:'100%'
height:'100%',
autoResize: true
}
util.extend(this.options, this.defaultOptions);
@ -38,10 +40,10 @@ class Canvas {
this.body.emitter.on("destroy", () => {
this.hammerFrame.destroy();
this.hammer.destroy();
this._cleanUp();
});
// automatically adapt to a changing size of the browser.
window.onresize = () => {this.setSize(); this.body.emitter.emit("_redraw");};
}
setOptions(options) {
@ -49,6 +51,26 @@ class Canvas {
if (options.width !== undefined) {this.options.width = this._prepareValue(options.width );}
if (options.height!== undefined) {this.options.height= this._prepareValue(options.height);}
}
if (this.options.autoResize === true) {
// automatically adapt to a changing size of the browser.
this._cleanUp();
this.resizeTimer = setInterval(() => {this.setSize(); this.body.emitter.emit("_requestRedraw");}, 1000);
util.addEventListener(window,'resize',this._onResize);
}
}
_cleanUp() {
// automatically adapt to a changing size of the browser.
if (this.resizeTimer !== undefined) {
clearInterval(this.resizeTimer);
}
util.removeEventListener(window,'resize',this._onResize);
}
_onResize() {
this.setSize();
this.body.emitter.emit("_redraw");
}
_prepareValue(value) {

+ 6
- 13
lib/network/modules/ConfigurationSystem.js View File

@ -153,15 +153,7 @@ class ConfigurationSystem {
manipulation: {
enabled: false,
initiallyActive: false,
locale: ['en', 'nl'],
functionality: {
addNode: true,
addEdge: true,
editNode: true,
editEdge: true,
deleteNode: true,
deleteEdge: true
}
locale: ['en', 'nl']
},
physics: {
barnesHut: {
@ -192,7 +184,7 @@ class ConfigurationSystem {
timestep: [0.5, 0, 1, 0.05]
},
selection: {
select: true,
selectable: true,
selectConnectedEdges: true
},
rendering: {
@ -503,7 +495,7 @@ class ConfigurationSystem {
input.value = range.value;
var me = this;
range.onchange = function () {input.value = this.value; me._update(this.value, path);};
range.onchange = function () {input.value = this.value; me._update(Number(this.value), path);};
range.oninput = function () {input.value = this.value; };
let label = this._makeLabel(path[path.length-1], path);
@ -677,7 +669,7 @@ class ConfigurationSystem {
}
else if (typeof arr[0] === 'number') {
this._makeRange(arr, value, path);
if (arr[0] !== value) {this.changedOptions.push({path:path, value:value});}
if (arr[0] !== value) {this.changedOptions.push({path:path, value:Number(value)});}
}
}
@ -694,7 +686,7 @@ class ConfigurationSystem {
this.network.setOptions(options);
}
_constructOptions(value,path, optionsObj = {}) {
_constructOptions(value, path, optionsObj = {}) {
let pointer = optionsObj;
// when dropdown boxes can be string or boolean, we typecast it into correct types
@ -712,6 +704,7 @@ class ConfigurationSystem {
pointer[path[i]] = value;
}
}
console.log(optionsObj)
return optionsObj;
}

+ 19
- 17
lib/network/modules/InteractionHandler.js View File

@ -117,9 +117,8 @@ class InteractionHandler {
onTap(event) {
let pointer = this.getPointer(event.center);
this.checkSelectionChanges(pointer);
this.selectionHandler._generateClickEvent('click',pointer);
this.checkSelectionChanges(pointer, event);
this.selectionHandler._generateClickEvent('click', event, pointer);
}
@ -129,7 +128,7 @@ class InteractionHandler {
*/
onDoubleTap(event) {
let pointer = this.getPointer(event.center);
this.selectionHandler._generateClickEvent('doubleClick',pointer);
this.selectionHandler._generateClickEvent('doubleClick', event, pointer);
}
@ -141,10 +140,10 @@ class InteractionHandler {
onHold(event) {
let pointer = this.getPointer(event.center);
this.checkSelectionChanges(pointer, true);
this.checkSelectionChanges(pointer, event, true);
this.selectionHandler._generateClickEvent('click',pointer);
this.selectionHandler._generateClickEvent('hold',pointer);
this.selectionHandler._generateClickEvent('click', event, pointer);
this.selectionHandler._generateClickEvent('hold', event, pointer);
}
@ -156,7 +155,7 @@ class InteractionHandler {
onRelease(event) {
if (new Date().valueOf() - this.touchTime > 10) {
let pointer = this.getPointer(event.center);
this.selectionHandler._generateClickEvent('release',pointer);
this.selectionHandler._generateClickEvent('release', event, pointer);
// to avoid double fireing of this event because we have two hammer instances. (on canvas and on frame)
this.touchTime = new Date().valueOf();
}
@ -164,7 +163,7 @@ class InteractionHandler {
onContext(event) {
let pointer = this.getPointer({x:event.pageX, y:event.pageY});
this.selectionHandler._generateClickEvent('rightClick',pointer);
this.selectionHandler._generateClickEvent('oncontext', event, pointer);
}
@ -173,7 +172,7 @@ class InteractionHandler {
* @param pointer
* @param add
*/
checkSelectionChanges(pointer, add = false) {
checkSelectionChanges(pointer, event, add = false) {
let previouslySelectedEdgeCount = this.selectionHandler._getSelectedEdgeCount();
let previouslySelectedNodeCount = this.selectionHandler._getSelectedNodeCount();
let previousSelection = this.selectionHandler.getSelection();
@ -188,26 +187,26 @@ class InteractionHandler {
let selectedNodes = this.selectionHandler._getSelectedNodeCount();
if (selectedNodes - previouslySelectedNodeCount > 0) { // node was selected
this.selectionHandler._generateClickEvent('selectNode',pointer);
this.selectionHandler._generateClickEvent('selectNode', event, pointer);
selected = true;
}
else if (selectedNodes - previouslySelectedNodeCount < 0) { // node was deselected
this.selectionHandler._generateClickEvent('deselectNode',pointer, previousSelection);
this.selectionHandler._generateClickEvent('deselectNode', event, pointer, previousSelection);
selected = true;
}
if (selectedEdges - previouslySelectedEdgeCount > 0) { // node was selected
this.selectionHandler._generateClickEvent('selectEdge',pointer);
this.selectionHandler._generateClickEvent('selectEdge', event, pointer);
selected = true;
}
else if (selectedEdges - previouslySelectedEdgeCount < 0) { // node was deselected
this.selectionHandler._generateClickEvent('deselectEdge',pointer, previousSelection);
this.selectionHandler._generateClickEvent('deselectEdge', event, pointer, previousSelection);
selected = true;
}
if (selected === true) { // select or unselect
this.selectionHandler._generateClickEvent('select',pointer);
this.selectionHandler._generateClickEvent('select', event, pointer);
}
}
@ -232,7 +231,7 @@ class InteractionHandler {
this.drag.translation = util.extend({},this.body.view.translation); // copy the object
this.drag.nodeId = undefined;
this.selectionHandler._generateClickEvent('dragStart',this.drag.pointer);
this.selectionHandler._generateClickEvent('dragStart', event, this.drag.pointer);
if (node !== undefined && this.options.dragNodes === true) {
this.drag.nodeId = node.id;
@ -281,6 +280,9 @@ class InteractionHandler {
this.body.emitter.emit('unlockNode');
let pointer = this.getPointer(event.center);
this.selectionHandler._generateClickEvent('dragging', event, pointer);
let selection = this.drag.selection;
if (selection && selection.length && this.options.dragNodes === true) {
// calculate delta's and new location
@ -339,7 +341,7 @@ class InteractionHandler {
else {
this.body.emitter.emit('_requestRedraw');
}
this.selectionHandler._generateClickEvent('dragEnd',this.getPointer(event.center));
this.selectionHandler._generateClickEvent('dragEnd', event, this.getPointer(event.center));
}

+ 28
- 38
lib/network/modules/ManipulationSystem.js View File

@ -36,22 +36,12 @@ class ManipulationSystem {
initiallyActive: false,
locale: 'en',
locales: locales,
functionality:{
addNode: true,
addEdge: true,
editNode: true,
editEdge: true,
deleteNode: true,
deleteEdge: true
},
handlerFunctions: {
addNode: undefined,
addEdge: undefined,
editNode: undefined,
editEdge: undefined,
deleteNode: undefined,
deleteEdge: undefined
},
addNode: true,
addEdge: true,
editNode: undefined,
editEdge: true,
deleteNode: true,
deleteEdge: true,
controlNodeStyle:{
shape:'dot',
size:6,
@ -164,11 +154,11 @@ class ManipulationSystem {
let needSeperator = false;
if (this.options.functionality.addNode === true) {
if (this.options.addNode !== false) {
this._createAddNodeButton(locale);
needSeperator = true;
}
if (this.options.functionality.addEdge === true) {
if (this.options.addEdge !== false) {
if (needSeperator === true) {
this._createSeperator(1);
} else {
@ -177,7 +167,7 @@ class ManipulationSystem {
this._createAddEdgeButton(locale);
}
if (selectedNodeCount === 1 && typeof this.options.handlerFunctions.editNode === 'function' && this.options.functionality.editNode === true) {
if (selectedNodeCount === 1 && typeof this.options.editNode === 'function') {
if (needSeperator === true) {
this._createSeperator(2);
} else {
@ -185,7 +175,7 @@ class ManipulationSystem {
}
this._createEditNodeButton(locale);
}
else if (selectedEdgeCount === 1 && selectedNodeCount === 0 && this.options.functionality.editEdge === true) {
else if (selectedEdgeCount === 1 && selectedNodeCount === 0 && this.options.editEdge !== false) {
if (needSeperator === true) {
this._createSeperator(3);
} else {
@ -196,13 +186,13 @@ class ManipulationSystem {
// remove buttons
if (selectedTotalCount !== 0) {
if (selectedNodeCount === 1 && this.options.functionality.deleteNode === true) {
if (selectedNodeCount === 1 && this.options.deleteNode !== false) {
if (needSeperator === true) {
this._createSeperator(4);
}
this._createDeleteButton(locale);
}
else if (selectedNodeCount === 0 && this.options.functionality.deleteEdge === true) {
else if (selectedNodeCount === 0 && this.options.deleteEdge !== false) {
if (needSeperator === true) {
this._createSeperator(4);
}
@ -267,15 +257,15 @@ class ManipulationSystem {
this._clean();
this.inMode = 'editNode';
if (typeof this.options.handlerFunctions.editNode === 'function') {
if (typeof this.options.editNode === 'function') {
let node = this.selectionHandler._getSelectedNode();
if (node.isCluster !== true) {
let data = util.deepExtend({}, node.options, true);
data.x = node.x;
data.y = node.y;
if (this.options.handlerFunctions.editNode.length === 2) {
this.options.handlerFunctions.editNode(data, (finalizedData) => {
if (this.options.editNode.length === 2) {
this.options.editNode(data, (finalizedData) => {
if (finalizedData !== null && finalizedData !== undefined && this.inMode === 'delete') { // if for whatever reason the mode has changes (due to dataset change) disregard the callback) {
this.body.data.nodes.update(finalizedData);
this.showManipulatorToolbar();
@ -425,13 +415,13 @@ class ManipulationSystem {
}
}
if (typeof this.options.handlerFunctions.deleteNode === 'function') {
deleteFunction = this.options.handlerFunctions.deleteNode;
if (typeof this.options.deleteNode === 'function') {
deleteFunction = this.options.deleteNode;
}
}
else if (selectedEdges.length > 0) {
if (typeof this.options.handlerFunctions.deleteEdge === 'function') {
deleteFunction = this.options.handlerFunctions.deleteEdge;
if (typeof this.options.deleteEdge === 'function') {
deleteFunction = this.options.deleteEdge;
}
}
@ -1039,9 +1029,9 @@ class ManipulationSystem {
label: 'new'
};
if (typeof this.options.handlerFunctions.addNode === 'function') {
if (this.options.handlerFunctions.addNode.length === 2) {
this.options.handlerFunctions.addNode(defaultData, (finalizedData) => {
if (typeof this.options.addNode === 'function') {
if (this.options.addNode.length === 2) {
this.options.addNode(defaultData, (finalizedData) => {
if (finalizedData !== null && finalizedData !== undefined && this.inMode === 'addNode') { // if for whatever reason the mode has changes (due to dataset change) disregard the callback
this.body.data.nodes.add(finalizedData);
this.showManipulatorToolbar();
@ -1067,9 +1057,9 @@ class ManipulationSystem {
*/
_performCreateEdge(sourceNodeId, targetNodeId) {
let defaultData = {from: sourceNodeId, to: targetNodeId};
if (this.options.handlerFunctions.addEdge) {
if (this.options.handlerFunctions.addEdge.length === 2) {
this.options.handlerFunctions.addEdge(defaultData, (finalizedData) => {
if (typeof this.options.addEdge === 'function') {
if (this.options.addEdge.length === 2) {
this.options.addEdge(defaultData, (finalizedData) => {
if (finalizedData !== null && finalizedData !== undefined && this.inMode === 'addEdge') { // if for whatever reason the mode has changes (due to dataset change) disregard the callback
this.body.data.edges.add(finalizedData);
this.selectionHandler.unselectAll();
@ -1095,9 +1085,9 @@ class ManipulationSystem {
*/
_performEditEdge(sourceNodeId, targetNodeId) {
let defaultData = {id: this.edgeBeingEditedId, from: sourceNodeId, to: targetNodeId};
if (this.options.handlerFunctions.editEdge) {
if (this.options.handlerFunctions.editEdge.length === 2) {
this.options.handlerFunctions.editEdge(defaultData, (finalizedData) => {
if (typeof this.options.editEdge === 'function') {
if (this.options.editEdge.length === 2) {
this.options.editEdge(defaultData, (finalizedData) => {
if (finalizedData === null || finalizedData === undefined || this.inMode !== 'editEdge') { // if for whatever reason the mode has changes (due to dataset change) disregard the callback) {
this.body.edges[defaultData.id].updateEdgeType();
this.body.emitter.emit('_redraw');

+ 2
- 1
lib/network/modules/SelectionHandler.js View File

@ -66,12 +66,13 @@ class SelectionHandler {
return selectionChanged;
}
_generateClickEvent(eventType, pointer, oldSelection) {
_generateClickEvent(eventType, event, pointer, oldSelection) {
let properties = this.getSelection();
properties['pointer'] = {
DOM: {x: pointer.x, y: pointer.y},
canvas: this.canvas.DOMtoCanvas(pointer)
}
properties['event'] = event;
if (oldSelection !== undefined) {
properties['previousSelection'] = oldSelection;

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

@ -156,7 +156,7 @@ class View {
* @param {Number} nodeId
* @param {Number} [options]
*/
focusOnNode(nodeId, options = {}) {
focus(nodeId, options = {}) {
if (this.body.nodes[nodeId] !== undefined) {
var nodePosition = {x: this.body.nodes[nodeId].x, y: this.body.nodes[nodeId].y};
options.position = nodePosition;

+ 8
- 19
lib/network/modules/components/AllOptions.js View File

@ -19,6 +19,7 @@ let allOptions = {
canvas: {
width: {string},
height: {string},
autoResize: {boolean},
__type__: {object}
},
rendering: {
@ -138,24 +139,12 @@ let allOptions = {
initiallyActive: {boolean},
locale: {string},
locales: {object},
functionality: {
addNode: {boolean},
addEdge: {boolean},
editNode: {boolean},
editEdge: {boolean},
deleteNode: {boolean},
deleteEdge: {boolean},
__type__: {object}
},
handlerFunctions: {
addNode: {fn,undef},
addEdge: {fn,undef},
editNode: {fn,undef},
editEdge: {fn,undef},
deleteNode: {fn,undef},
deleteEdge: {fn,undef},
__type__: {object}
},
addNode: {boolean,fn},
addEdge: {boolean,fn},
editNode: {fn},
editEdge: {boolean,fn},
deleteNode: {boolean,fn},
deleteEdge: {boolean,fn},
controlNodeStyle: ['__ref__','nodes'],
__type__: {object,boolean}
},
@ -276,7 +265,7 @@ let allOptions = {
__type__: {object,boolean}
},
selection: {
select: {boolean},
selectable: {boolean},
selectConnectedEdges: {boolean},
__type__: {object}
},

+ 4
- 0
lib/timeline/component/GraphGroup.js View File

@ -191,6 +191,10 @@ GraphGroup.prototype.getYRange = function(groupData) {
return this.type.getYRange(groupData);
}
GraphGroup.prototype.getData = function(groupData) {
return this.type.getData(groupData);
}
GraphGroup.prototype.draw = function(dataset, group, framework) {
this.type.draw(dataset, group, framework);
}

+ 21
- 25
lib/timeline/component/LineGraph.js View File

@ -6,7 +6,8 @@ var Component = require('./Component');
var DataAxis = require('./DataAxis');
var GraphGroup = require('./GraphGroup');
var Legend = require('./Legend');
var BarGraphFunctions = require('./graph2d_types/bar');
var BarFunctions = require('./graph2d_types/bar');
var LineFunctions = require('./graph2d_types/line');
var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items
@ -26,6 +27,7 @@ function LineGraph(body, options) {
defaultGroup: 'default',
sort: true,
sampling: true,
stack:false,
graphHeight: '400px',
shaded: {
enabled: false,
@ -182,7 +184,7 @@ LineGraph.prototype._create = function(){
*/
LineGraph.prototype.setOptions = function(options) {
if (options) {
var fields = ['sampling','defaultGroup','height','graphHeight','yAxisOrientation','style','barChart','dataAxis','sort','groups'];
var fields = ['sampling','defaultGroup','stack','height','graphHeight','yAxisOrientation','style','barChart','dataAxis','sort','groups'];
if (options.graphHeight === undefined && options.height !== undefined && this.body.domProps.centerContainer.height !== undefined) {
this.updateSVGheight = true;
this.updateSVGheightOnResize = true;
@ -566,7 +568,6 @@ LineGraph.prototype.redraw = function(forceGraphUpdate) {
// update the height of the graph on each redraw of the graph.
if (this.updateSVGheight == true) {
console.log(this.options.graphHeight, this.body.domProps.centerContainer.height)
if (this.options.graphHeight != this.body.domProps.centerContainer.height + 'px') {
this.options.graphHeight = this.body.domProps.centerContainer.height + 'px';
this.svg.style.height = this.body.domProps.centerContainer.height + 'px';
@ -656,7 +657,7 @@ LineGraph.prototype._updateGraph = function () {
}
else {
if (this.COUNTER > MAX_CYCLES) {
console.log("WARNING: there may be an infinite loop in the _updateGraph emitter cycle.")
console.log("WARNING: there may be an infinite loop in the _updateGraph emitter cycle.");
}
this.COUNTER = 0;
this.abortedGraphUpdate = false;
@ -674,7 +675,7 @@ LineGraph.prototype._updateGraph = function () {
group.draw(processedGroupData[groupIds[i]], group, this.framework);
}
}
BarGraphFunctions.draw(groupIds, processedGroupData, this.framework);
BarFunctions.draw(groupIds, processedGroupData, this.framework);
}
}
}
@ -782,8 +783,8 @@ LineGraph.prototype._applySampling = function (groupIds, groupsData) {
*/
LineGraph.prototype._getYRanges = function (groupIds, groupsData, groupRanges) {
var groupData, group, i;
var barCombinedDataLeft = [];
var barCombinedDataRight = [];
var combinedDataLeft = [];
var combinedDataRight = [];
var options;
if (groupIds.length > 0) {
for (i = 0; i < groupIds.length; i++) {
@ -792,9 +793,9 @@ LineGraph.prototype._getYRanges = function (groupIds, groupsData, groupRanges) {
if (groupData.length > 0) {
group = this.groups[groupIds[i]];
// if bar graphs are stacked, their range need to be handled differently and accumulated over all groups.
if (options.barChart.handleOverlap == 'stack' && options.style == 'bar') {
if (options.yAxisOrientation == 'left') {barCombinedDataLeft = barCombinedDataLeft.concat(group.getYRange(groupData)) ;}
else {barCombinedDataRight = barCombinedDataRight.concat(group.getYRange(groupData));}
if (options.stack === true) {
if (options.yAxisOrientation == 'left') {combinedDataLeft = combinedDataLeft .concat(group.getData(groupData));}
else {combinedDataRight = combinedDataRight.concat(group.getData(groupData));}
}
else {
groupRanges[groupIds[i]] = group.getYRange(groupData,groupIds[i]);
@ -803,8 +804,12 @@ LineGraph.prototype._getYRanges = function (groupIds, groupsData, groupRanges) {
}
// if bar graphs are stacked, their range need to be handled differently and accumulated over all groups.
BarGraphFunctions.getStackedBarYRange(barCombinedDataLeft , groupRanges, groupIds, '__barchartLeft' , 'left' );
BarGraphFunctions.getStackedBarYRange(barCombinedDataRight, groupRanges, groupIds, '__barchartRight', 'right');
BarFunctions.getStackedYRange(combinedDataLeft , groupRanges, groupIds, '__barStackLeft' , 'left' );
BarFunctions.getStackedYRange(combinedDataRight, groupRanges, groupIds, '__barStackRight', 'right');
// if line graphs are stacked, their range need to be handled differently and accumulated over all groups.
LineFunctions.getStackedYRange(combinedDataLeft , groupRanges, groupIds, '__lineStackLeft' , 'left' );
LineFunctions.getStackedYRange(combinedDataRight, groupRanges, groupIds, '__lineStackRight', 'right');
}
};
@ -892,11 +897,9 @@ LineGraph.prototype._updateYAxis = function (groupIds, groupRanges) {
}
// clean the accumulated lists
if (groupIds.indexOf('__barchartLeft') != -1) {
groupIds.splice(groupIds.indexOf('__barchartLeft'),1);
}
if (groupIds.indexOf('__barchartRight') != -1) {
groupIds.splice(groupIds.indexOf('__barchartRight'),1);
var tempGroups = ['__barStackLeft','__barStackRight','__lineStackLeft','__lineStackRight'];
for (var i = 0; i < tempGroups.length; i++) {
if (groupIds.indexOf(tempGroups[i]) != -1) {groupIds.splice(groupIds.indexOf(tempGroups[i]),1);}
}
return resized;
@ -974,14 +977,7 @@ 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;
var 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, label:labelValue});

+ 27
- 27
lib/timeline/component/graph2d_types/bar.js View File

@ -7,29 +7,29 @@ function Bargraph(groupId, options) {
}
Bargraph.prototype.getYRange = function(groupData) {
if (this.options.barChart.handleOverlap != 'stack') {
var yMin = groupData[0].y;
var yMax = groupData[0].y;
for (var j = 0; j < groupData.length; j++) {
yMin = yMin > groupData[j].y ? groupData[j].y : yMin;
yMax = yMax < groupData[j].y ? groupData[j].y : yMax;
}
return {min: yMin, max: yMax, yAxisOrientation: this.options.yAxisOrientation};
}
else {
var barCombinedData = [];
for (var j = 0; j < groupData.length; j++) {
barCombinedData.push({
x: groupData[j].x,
y: groupData[j].y,
groupId: this.groupId
});
}
return barCombinedData;
var yMin = groupData[0].y;
var yMax = groupData[0].y;
for (var j = 0; j < groupData.length; j++) {
yMin = yMin > groupData[j].y ? groupData[j].y : yMin;
yMax = yMax < groupData[j].y ? groupData[j].y : yMax;
}
return {min: yMin, max: yMax, yAxisOrientation: this.options.yAxisOrientation};
};
Bargraph.prototype.getData = function(groupData) {
var combinedData = [];
for (var j = 0; j < groupData.length; j++) {
combinedData.push({
x: groupData[j].x,
y: groupData[j].y,
groupId: this.groupId
});
}
return combinedData;
}
/**
* draw a bar graph
@ -99,7 +99,7 @@ Bargraph.draw = function (groupIds, processedGroupData, framework) {
drawData = Bargraph._getSafeDrawData(coreDistance, group, minWidth);
intersections[key].resolved += 1;
if (group.options.barChart.handleOverlap === 'stack') {
if (group.options.stack === true) {
if (combinedData[i].y < group.zeroPosition) {
heightOffset = intersections[key].accumulatedNegative;
intersections[key].accumulatedNegative += group.zeroPosition - combinedData[i].y;
@ -109,7 +109,7 @@ Bargraph.draw = function (groupIds, processedGroupData, framework) {
intersections[key].accumulatedPositive += group.zeroPosition - combinedData[i].y;
}
}
else if (group.options.barChart.handleOverlap === 'sideBySide') {
else if (group.options.barChart.sideBySide === true) {
drawData.width = drawData.width / intersections[key].amount;
drawData.offset += (intersections[key].resolved) * drawData.width - (0.5*drawData.width * (intersections[key].amount+1));
if (group.options.barChart.align === 'left') {drawData.offset -= 0.5*drawData.width;}
@ -189,10 +189,10 @@ Bargraph._getSafeDrawData = function (coreDistance, group, minWidth) {
return {width: width, offset: offset};
};
Bargraph.getStackedBarYRange = function(barCombinedData, groupRanges, groupIds, groupLabel, orientation) {
if (barCombinedData.length > 0) {
Bargraph.getStackedYRange = function(combinedData, groupRanges, groupIds, groupLabel, orientation) {
if (combinedData.length > 0) {
// sort by time and by group
barCombinedData.sort(function (a, b) {
combinedData.sort(function (a, b) {
if (a.x === b.x) {
return a.groupId < b.groupId ? -1 : 1;
}
@ -202,14 +202,14 @@ Bargraph.getStackedBarYRange = function(barCombinedData, groupRanges, groupIds,
});
var intersections = {};
Bargraph._getDataIntersections(intersections, barCombinedData);
groupRanges[groupLabel] = Bargraph._getStackedBarYRange(intersections, barCombinedData);
Bargraph._getDataIntersections(intersections, combinedData);
groupRanges[groupLabel] = Bargraph._getStackedYRange(intersections, combinedData);
groupRanges[groupLabel].yAxisOrientation = orientation;
groupIds.push(groupLabel);
}
}
Bargraph._getStackedBarYRange = function (intersections, combinedData) {
Bargraph._getStackedYRange = function (intersections, combinedData) {
var key;
var yMin = combinedData[0].y;
var yMax = combinedData[0].y;

+ 88
- 0
lib/timeline/component/graph2d_types/line.js View File

@ -6,6 +6,18 @@ function Line(groupId, options) {
this.options = options;
}
Line.prototype.getData = function(groupData) {
var combinedData = [];
for (var j = 0; j < groupData.length; j++) {
combinedData.push({
x: groupData[j].x,
y: groupData[j].y,
groupId: this.groupId
});
}
return combinedData;
}
Line.prototype.getYRange = function(groupData) {
var yMin = groupData[0].y;
var yMax = groupData[0].y;
@ -16,6 +28,82 @@ Line.prototype.getYRange = function(groupData) {
return {min: yMin, max: yMax, yAxisOrientation: this.options.yAxisOrientation};
};
Line.getStackedYRange = function(combinedData, groupRanges, groupIds, groupLabel, orientation) {
if (combinedData.length > 0) {
// sort by time and by group
combinedData.sort(function (a, b) {
if (a.x === b.x) {
return a.groupId < b.groupId ? -1 : 1;
}
else {
return a.x - b.x;
}
});
var intersections = {};
Line._getDataIntersections(intersections, combinedData);
groupRanges[groupLabel] = Line._getStackedYRange(intersections, combinedData);
groupRanges[groupLabel].yAxisOrientation = orientation;
groupIds.push(groupLabel);
}
}
Line._getStackedYRange = function (intersections, combinedData) {
var key;
var yMin = combinedData[0].y;
var yMax = combinedData[0].y;
for (var i = 0; i < combinedData.length; i++) {
key = combinedData[i].x;
if (intersections[key] === undefined) {
yMin = yMin > combinedData[i].y ? combinedData[i].y : yMin;
yMax = yMax < combinedData[i].y ? combinedData[i].y : yMax;
}
else {
if (combinedData[i].y < 0) {
intersections[key].accumulatedNegative += combinedData[i].y;
}
else {
intersections[key].accumulatedPositive += combinedData[i].y;
}
}
}
for (var xpos in intersections) {
if (intersections.hasOwnProperty(xpos)) {
yMin = yMin > intersections[xpos].accumulatedNegative ? intersections[xpos].accumulatedNegative : yMin;
yMin = yMin > intersections[xpos].accumulatedPositive ? intersections[xpos].accumulatedPositive : yMin;
yMax = yMax < intersections[xpos].accumulatedNegative ? intersections[xpos].accumulatedNegative : yMax;
yMax = yMax < intersections[xpos].accumulatedPositive ? intersections[xpos].accumulatedPositive : yMax;
}
}
return {min: yMin, max: yMax};
};
/**
* Fill the intersections object with counters of how many datapoints share the same x coordinates
* @param intersections
* @param combinedData
* @private
*/
Line._getDataIntersections = function (intersections, combinedData) {
// get intersections
var coreDistance;
for (var i = 0; i < combinedData.length; i++) {
if (i + 1 < combinedData.length) {
coreDistance = Math.abs(combinedData[i + 1].x - combinedData[i].x);
}
if (i > 0) {
coreDistance = Math.min(coreDistance, Math.abs(combinedData[i - 1].x - combinedData[i].x));
}
if (coreDistance === 0) {
if (intersections[combinedData[i].x] === undefined) {
intersections[combinedData[i].x] = {amount: 0, resolved: 0, accumulatedPositive: 0, accumulatedNegative: 0};
}
intersections[combinedData[i].x].amount += 1;
}
}
};
/**
* draw a line graph

Loading…
Cancel
Save