Browse Source

added multiselect, reworked options

flowchartTest
Alex de Mulder 9 years ago
parent
commit
5b7a90f4dc
13 changed files with 1660 additions and 1692 deletions
  1. +2
    -2
      dist/vis.css
  2. +1517
    -1569
      dist/vis.js
  3. +1
    -1
      dist/vis.min.css
  4. +5
    -5
      examples/network/17_network_info.html
  5. +40
    -39
      lib/network/Network.js
  6. +2
    -2
      lib/network/modules/Canvas.js
  7. +10
    -6
      lib/network/modules/CanvasRenderer.js
  8. +10
    -8
      lib/network/modules/ConfigurationSystem.js
  9. +9
    -8
      lib/network/modules/InteractionHandler.js
  10. +6
    -4
      lib/network/modules/ManipulationSystem.js
  11. +8
    -6
      lib/network/modules/SelectionHandler.js
  12. +36
    -42
      lib/network/modules/components/AllOptions.js
  13. +14
    -0
      lib/network/modules/components/Node.js

+ 2
- 2
dist/vis.css View File

@ -222,7 +222,7 @@
box-sizing: border-box;
}
.vis-item.background {
.vis-item.vis-background {
border: none;
background-color: rgba(213, 221, 246, 0.4);
box-sizing: border-box;
@ -244,7 +244,7 @@
display: inline-block;
}
.vis-item.background .vis-item-content {
.vis-item.vis-background .vis-item-content {
position: absolute;
display: inline-block;
}

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


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


+ 5
- 5
examples/network/17_network_info.html View File

@ -98,11 +98,11 @@
var x = - mynetwork.clientWidth / 2 + 50;
var y = - mynetwork.clientHeight / 2 + 50;
var step = 70;
nodes.push({id: 1000, x: x, y: y, label: 'Internet', group: 'internet', value: 1, mass:0});
nodes.push({id: 1001, x: x, y: y + step, label: 'Switch', group: 'switch', value: 1, mass:0});
nodes.push({id: 1002, x: x, y: y + 2 * step, label: 'Server', group: 'server', value: 1, mass:0});
nodes.push({id: 1003, x: x, y: y + 3 * step, label: 'Computer', group: 'desktop', value: 1, mass:0});
nodes.push({id: 1004, x: x, y: y + 4 * step, label: 'Smartphone', group: 'mobile', value: 1, mass:0});
nodes.push({id: 1000, x: x, y: y, label: 'Internet', group: 'internet', value: 1, fixed: true, physics:false});
nodes.push({id: 1001, x: x, y: y + step, label: 'Switch', group: 'switch', value: 1, fixed: true, physics:false});
nodes.push({id: 1002, x: x, y: y + 2 * step, label: 'Server', group: 'server', value: 1, fixed: true, physics:false});
nodes.push({id: 1003, x: x, y: y + 3 * step, label: 'Computer', group: 'desktop', value: 1, fixed: true, physics:false});
nodes.push({id: 1004, x: x, y: y + 4 * step, label: 'Smartphone', group: 'mobile', value: 1, fixed: true, physics:false});
// create a network
var container = document.getElementById('mynetwork');

+ 40
- 39
lib/network/Network.js View File

@ -1,15 +1,16 @@
// Load custom shapes into CanvasRenderingContext2D
require('./shapes');
var Emitter = require('emitter-component');
var Hammer = require('../module/hammer');
var util = require('../util');
var DataSet = require('../DataSet');
var DataView = require('../DataView');
var dotparser = require('./dotparser');
var gephiParser = require('./gephiParser');
var Images = require('./Images');
var Activator = require('../shared/Activator');
let Emitter = require('emitter-component');
let Hammer = require('../module/hammer');
let util = require('../util');
let DataSet = require('../DataSet');
let DataView = require('../DataView');
let dotparser = require('./dotparser');
let gephiParser = require('./gephiParser');
let Images = require('./Images');
let Activator = require('../shared/Activator');
let locales = require('./locales');
import Groups from './modules/Groups';
import NodesHandler from './modules/NodesHandler';
@ -28,6 +29,8 @@ import Validator from "./modules/Validator";
import {printStyle} from "./modules/Validator";
import {allOptions, configureOptions} from './modules/components/AllOptions.js';
/**
* @constructor Network
* Create a network visualization, displaying nodes and edges.
@ -47,8 +50,10 @@ function Network(container, data, options) {
// set constant values
this.options = {};
this.defaultOptions = {
locale: 'en',
locales: locales,
clickToUse: false
};
}
util.extend(this.options, this.defaultOptions);
// containers for nodes and edges
@ -93,6 +98,8 @@ function Network(container, data, options) {
}
};
// bind the event listeners
this.bindEventListeners();
@ -143,37 +150,50 @@ Network.prototype.setOptions = function (options) {
console.log('%cErrors have been found in the supplied options object. None of the options will be used.', printStyle);
}
//this.placeConvenienceOptions(options);
// copy the global fields over
let fields = ['locale','locales','clickToUse'];
util.selectiveDeepExtend(fields,this.options, options);
// the hierarchical system can adapt the edges and the physics to it's own options because not all combinations work with the hierarichical system.
options = this.layoutEngine.setOptions(options.layout, options);
this.canvas.setOptions(options); // options for canvas are in globals
// pass the options to the modules
this.groups.setOptions(options.groups);
this.nodesHandler.setOptions(options.nodes);
this.edgesHandler.setOptions(options.edges);
this.physics.setOptions(options.physics);
this.canvas.setOptions(options.canvas);
this.renderer.setOptions(options.rendering);
this.view.setOptions(options.view);
this.manipulation.setOptions(options.manipulation,options); // manipulation uses the locales in the globals
this.interactionHandler.setOptions(options.interaction);
this.selectionHandler.setOptions(options.selection);
this.clustering.setOptions(options.clustering);
this.manipulation.setOptions(options.manipulation);
this.renderer.setOptions(options.interaction); // options for rendering are in interaction
this.selectionHandler.setOptions(options.interaction); // options for selection are in interaction
// these two do not have options at the moment, here for completeness
//this.view.setOptions(options.view);
//this.clustering.setOptions(options.clustering);
this.configurationSystem.setOptions(options.configure);
// if the configuration system is enabled, copy all options and put them into the config system
if (this.configurationSystem.options.enabled === true) {
let networkOptions = {nodes:{},edges:{},layout:{},interaction:{},manipulation:{},physics:{},selection:{},rendering:{}};
let networkOptions = {nodes:{},edges:{},layout:{},interaction:{},manipulation:{},physics:{},global:{}};
util.deepExtend(networkOptions.nodes, this.nodesHandler.options);
util.deepExtend(networkOptions.edges, this.edgesHandler.options);
util.deepExtend(networkOptions.layout, this.layoutEngine.options);
// load the selectionHandler and rendere default options in to the interaction group
util.deepExtend(networkOptions.interaction, this.selectionHandler.options);
util.deepExtend(networkOptions.interaction, this.renderer.options);
util.deepExtend(networkOptions.interaction, this.interactionHandler.options);
util.deepExtend(networkOptions.manipulation, this.manipulation.options);
util.deepExtend(networkOptions.physics, this.physics.options);
util.deepExtend(networkOptions.selection, this.selectionHandler.options);
util.deepExtend(networkOptions.rendering, this.renderer.options);
// load globals into the global object
util.deepExtend(networkOptions.global, this.canvas.options);
util.deepExtend(networkOptions.global, this.options);
this.configurationSystem.setModuleOptions(networkOptions);
}
@ -204,25 +224,6 @@ Network.prototype.setOptions = function (options) {
}
};
//
///**
// *
// */
//Network.prototype.placeConvenienceOptions = function (options) {
// if (options.locale !== undefined) {
// if (options.manipulation === undefined) {
// options.manipulation = {enabled:false, locale:options.locale};
// }
// else if (typeof options.manipulation === 'boolean') {
// options.manipulation = {enabled: options.manipulation, locale:options.locale};
// }
// else {
// options.manipulation.locale = options.locale;
// }
// }
//}
/**
* Update the this.body.nodeIndices with the most recent node index list

+ 2
- 2
lib/network/modules/Canvas.js View File

@ -50,8 +50,8 @@ class Canvas {
setOptions(options) {
if (options !== undefined) {
if (options.width !== undefined) {this.options.width = this._prepareValue(options.width );}
if (options.height!== undefined) {this.options.height= this._prepareValue(options.height);}
let fields = ['width','height','autoResize'];
util.selectiveDeepExtend(fields,this.options, options);
}
if (this.options.autoResize === true) {

+ 10
- 6
lib/network/modules/CanvasRenderer.js View File

@ -70,7 +70,8 @@ class CanvasRenderer {
setOptions(options) {
if (options !== undefined) {
util.deepExtend(this.options, options);
let fields = ['hideEdgesOnDrag','hideNodesOnDrag'];
util.selectiveDeepExtend(fields,this.options, options);
}
}
@ -143,7 +144,7 @@ class CanvasRenderer {
this.canvas.setSize();
}
if (this.pixelRation === undefined) {
if (this.pixelRatio === undefined) {
this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio ||
ctx.mozBackingStorePixelRatio ||
ctx.msBackingStorePixelRatio ||
@ -207,6 +208,10 @@ class CanvasRenderer {
var nodeIndices = this.body.nodeIndices;
var node;
var selected = [];
var topLeft = this.canvas.DOMtoCanvas({x:0,y:0});
var bottomRight = this.canvas.DOMtoCanvas({x:this.canvas.frame.canvas.clientWidth,y:this.canvas.frame.canvas.clientHeight});;
var viewableArea = {top:topLeft.y,left:topLeft.x,bottom:bottomRight.y,right:bottomRight.x};
// draw unselected nodes;
for (let i = 0; i < nodeIndices.length; i++) {
@ -219,10 +224,9 @@ class CanvasRenderer {
if (alwaysShow === true) {
node.draw(ctx);
}
// todo: replace check
//else if (node.inArea() === true) {
node.draw(ctx);
//}
else if (node.isBoundingBoxOverlappingWith(viewableArea) === true) {
node.draw(ctx);
}
}
}

+ 10
- 8
lib/network/modules/ConfigurationSystem.js View File

@ -505,14 +505,16 @@ class ConfigurationSystem {
value = value === 'false' ? false : value;
for (let i = 0; i < path.length; i++) {
if (pointer[path[i]] === undefined) {
pointer[path[i]] = {};
}
if (i !== path.length -1) {
pointer = pointer[path[i]];
}
else {
pointer[path[i]] = value;
if (path[i] !== 'global') {
if (pointer[path[i]] === undefined) {
pointer[path[i]] = {};
}
if (i !== path.length - 1) {
pointer = pointer[path[i]];
}
else {
pointer[path[i]] = value;
}
}
}
return optionsObj;

+ 9
- 8
lib/network/modules/InteractionHandler.js View File

@ -38,15 +38,15 @@ class InteractionHandler {
this.defaultOptions = {
dragNodes:true,
dragView: true,
zoomView: true,
hoverEnabled: false,
navigationButtons: false,
tooltipDelay: 300,
keyboard: {
enabled: false,
speed: {x: 10, y: 10, zoom: 0.02},
bindToWindow: true
}
},
navigationButtons: false,
tooltipDelay: 300,
zoomView: true
}
util.extend(this.options,this.defaultOptions);
@ -63,7 +63,7 @@ class InteractionHandler {
setOptions(options) {
if (options !== undefined) {
// extend all but the values in fields
let fields = ['keyboard'];
let fields = ['hideEdgesOnDrag','hideNodesOnDrag','keyboard','multiselect','selectable','selectConnectedEdges'];
util.selectiveNotDeepExtend(fields, this.options, options);
// merge the keyboard options in.
@ -116,8 +116,9 @@ class InteractionHandler {
*/
onTap(event) {
let pointer = this.getPointer(event.center);
let multiselect = this.selectionHandler.options.multiselect && event.changedPointers[0].ctrlKey;
this.checkSelectionChanges(pointer, event);
this.checkSelectionChanges(pointer, event, multiselect);
this.selectionHandler._generateClickEvent('click', event, pointer);
}
@ -139,8 +140,9 @@ class InteractionHandler {
*/
onHold(event) {
let pointer = this.getPointer(event.center);
let multiselect = this.selectionHandler.options.multiselect;
this.checkSelectionChanges(pointer, event, true);
this.checkSelectionChanges(pointer, event, multiselect);
this.selectionHandler._generateClickEvent('click', event, pointer);
this.selectionHandler._generateClickEvent('hold', event, pointer);
@ -204,7 +206,6 @@ class InteractionHandler {
selected = true;
}
if (selected === true) { // select or unselect
this.selectionHandler._generateClickEvent('select', event, pointer);
}

+ 6
- 4
lib/network/modules/ManipulationSystem.js View File

@ -2,7 +2,6 @@
let util = require('../../util');
let Hammer = require('../../module/hammer');
let hammerUtil = require('../../hammerUtil');
let locales = require('../locales');
/**
* clears the toolbar div element of children
@ -34,8 +33,6 @@ class ManipulationSystem {
this.defaultOptions = {
enabled: false,
initiallyActive: false,
locale: 'en',
locales: locales,
addNode: true,
addEdge: true,
editNode: undefined,
@ -77,7 +74,12 @@ class ManipulationSystem {
* Set the Options
* @param options
*/
setOptions(options) {
setOptions(options, allOptions) {
if (allOptions !== undefined) {
if (allOptions.locale !== undefined) {this.options.locale = allOptions.locale};
if (allOptions.locales !== undefined) {this.options.locales = allOptions.locales};
}
if (options !== undefined) {
if (typeof options === 'boolean') {
this.options.enabled = options;

+ 8
- 6
lib/network/modules/SelectionHandler.js View File

@ -9,7 +9,8 @@ class SelectionHandler {
this.options = {};
this.defaultOptions = {
select: true,
multiselect: false,
selectable: true,
selectConnectedEdges: true
};
util.extend(this.options, this.defaultOptions);
@ -22,7 +23,8 @@ class SelectionHandler {
setOptions(options) {
if (options !== undefined) {
util.deepExtend(this.options, options);
let fields = ['multiselect','selectable','selectConnectedEdges'];
util.selectiveDeepExtend(fields,this.options, options);
}
}
@ -35,7 +37,7 @@ class SelectionHandler {
*/
selectOnPoint(pointer) {
let selected = false;
if (this.options.select === true) {
if (this.options.selectable === true) {
this.unselectAll();
let obj = this.getNodeAt(pointer) || this.getEdgeAt(pointer);;
if (obj !== undefined) {
@ -48,7 +50,7 @@ class SelectionHandler {
selectAdditionalOnPoint(pointer) {
let selectionChanged = false;
if (this.options.select === true) {
if (this.options.selectable === true) {
let obj = this.getNodeAt(pointer) || this.getEdgeAt(pointer);;
if (obj !== undefined) {
@ -517,7 +519,7 @@ class SelectionHandler {
*/
getSelectedNodes() {
let idArray = [];
if (this.options.select === true) {
if (this.options.selectable === true) {
for (let nodeId in this.selectionObj.nodes) {
if (this.selectionObj.nodes.hasOwnProperty(nodeId)) {
idArray.push(nodeId);
@ -535,7 +537,7 @@ class SelectionHandler {
*/
getSelectedEdges() {
let idArray = [];
if (this.options.select === true) {
if (this.options.selectable === true) {
for (let edgeId in this.selectionObj.edges) {
if (this.selectionObj.edges.hasOwnProperty(edgeId)) {
idArray.push(edgeId);

+ 36
- 42
lib/network/modules/components/AllOptions.js View File

@ -9,30 +9,19 @@ let string = 'string';
let boolean = 'boolean';
let number = 'number';
let array = 'array';
let object = 'object';
let object = 'object'; // should only be in a __type__ property
let dom = 'dom';
let fn = 'function';
let undef = 'undefined';
let any = 'any'
let allOptions = {
canvas: {
width: {string},
height: {string},
autoResize: {boolean},
__type__: {object}
},
rendering: {
hideEdgesOnDrag: {boolean},
hideNodesOnDrag: {boolean},
__type__: {object}
},
clustering: {},
configure: {
enabled: {boolean},
filter: {boolean,string:['nodes','edges','layout','physics','manipulation','interaction','selection','rendering'],array},
filter: {boolean,string,array},
container: {dom},
__type__: {object,boolean,string:['nodes','edges','layout','physics','manipulation','interaction','selection','rendering'],array}
__type__: {object,boolean,string,array}
},
edges: {
arrows: {
@ -102,22 +91,27 @@ let allOptions = {
},
groups: {
useDefaultGroups: {boolean},
__any__: ['__ref__','nodes'],
__any__: 'get from nodes, will be overwritten below',
__type__: {object}
},
interaction: {
dragNodes: {boolean},
dragView: {boolean},
zoomView: {boolean},
hideEdgesOnDrag: {boolean},
hideNodesOnDrag: {boolean},
hoverEnabled: {boolean},
navigationButtons: {boolean},
tooltipDelay: {number},
keyboard: {
enabled: {boolean},
speed: {x: {number}, y: {number}, zoom: {number}, __type__: {object}},
bindToWindow: {boolean},
__type__: {object,boolean}
},
multiselect: {boolean},
navigationButtons: {boolean},
selectable: {boolean},
selectConnectedEdges: {boolean},
tooltipDelay: {number},
zoomView: {boolean},
__type__: {object}
},
layout: {
@ -134,15 +128,13 @@ let allOptions = {
manipulation: {
enabled: {boolean},
initiallyActive: {boolean},
locale: {string},
locales: {object},
addNode: {boolean,fn},
addEdge: {boolean,fn},
editNode: {fn},
editEdge: {boolean,fn},
deleteNode: {boolean,fn},
deleteEdge: {boolean,fn},
controlNodeStyle: ['__ref__','nodes'],
controlNodeStyle: 'get from nodes, will be overwritten below',
__type__: {object,boolean}
},
nodes: {
@ -214,7 +206,7 @@ let allOptions = {
y: {number},
__type__: {object,boolean}
},
shape: {string:['ellipse', 'circle', 'database', 'box', 'text','image', 'circularImage','diamond', 'dot', 'star', 'triangle','triangleDown', 'square','icon']},
shape: {string:['ellipse', 'circle', 'database', 'box', 'text','image', 'circularImage','diamond', 'dot', 'star', 'triangle', 'triangleDown', 'square','icon']},
size: {number},
title: {string,undef},
value: {number,undef},
@ -261,19 +253,22 @@ let allOptions = {
timestep: {number},
__type__: {object,boolean}
},
selection: {
selectable: {boolean},
selectConnectedEdges: {boolean},
//globals :
autoResize: {boolean},
clickToUse: {boolean},
locale:{string},
locales:{
__any__: {object},
__type__: {object}
},
view: {},
height: {string},
width: {string},
__type__: {object}
};
allOptions.groups.__any__ = allOptions.nodes;
allOptions.manipulation.controlNodeStyle = allOptions.nodes;
//allOptions.locale = allOptions.manipulation.locale;
//allOptions.locales = allOptions.manipulation.locales;
let configureOptions = {
@ -399,20 +394,24 @@ let configureOptions = {
interaction: {
dragNodes: true,
dragView: true,
zoomView: true,
hideEdgesOnDrag: false,
hideNodesOnDrag: false,
hoverEnabled: false,
navigationButtons: false,
tooltipDelay: [300, 0, 1000, 25],
keyboard: {
enabled: false,
speed: {x: [10, 0, 40, 1], y: [10, 0, 40, 1], zoom: [0.02, 0, 0.1, 0.005]},
bindToWindow: true
}
},
multiselect: false,
navigationButtons: false,
selectable: true,
selectConnectedEdges: true,
tooltipDelay: [300, 0, 1000, 25],
zoomView: true
},
manipulation: {
enabled: false,
initiallyActive: false,
locale: ['en', 'nl']
initiallyActive: false
},
physics: {
barnesHut: {
@ -442,13 +441,8 @@ let configureOptions = {
solver: ['barnesHut', 'repulsion', 'hierarchicalRepulsion'],
timestep: [0.5, 0, 1, 0.05]
},
selection: {
selectable: true,
selectConnectedEdges: true
},
rendering: {
hideEdgesOnDrag: false,
hideNodesOnDrag: false
global: {
locale: ['en', 'nl']
}
};

+ 14
- 0
lib/network/modules/components/Node.js View File

@ -393,6 +393,20 @@ class Node {
);
}
/**
* Check if this object is overlapping with the provided object
* @param {Object} obj an object with parameters left, top, right, bottom
* @return {boolean} True if location is located on node
*/
isBoundingBoxOverlappingWith(obj) {
return (
this.shape.boundingBox.left < obj.right &&
this.shape.boundingBox.right > obj.left &&
this.shape.boundingBox.top < obj.bottom &&
this.shape.boundingBox.bottom > obj.top
);
}
}

Loading…
Cancel
Save