Browse Source

tweaks. NagivationMixin has now been ported along with keyboard bindings using keycharm.

flowchartTest
Alex de Mulder 10 years ago
parent
commit
208da974db
12 changed files with 816 additions and 3332 deletions
  1. +559
    -2402
      dist/vis.js
  2. +7
    -24
      lib/network/Network.js
  3. +0
    -158
      lib/network/mixins/MixinLoader.js
  4. +0
    -174
      lib/network/mixins/NavigationMixin.js
  5. +0
    -553
      lib/network/mixins/SectorsMixin.js
  6. +5
    -1
      lib/network/modules/Canvas.js
  7. +1
    -1
      lib/network/modules/CanvasRenderer.js
  8. +33
    -7
      lib/network/modules/InteractionHandler.js
  9. +2
    -2
      lib/network/modules/PhysicsEngine.js
  10. +5
    -5
      lib/network/modules/View.js
  11. +203
    -0
      lib/network/modules/components/NavigationHandler.js
  12. +1
    -5
      lib/timeline/component/Group.js

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


+ 7
- 24
lib/network/Network.js View File

@ -133,9 +133,6 @@ function Network (container, data, options) {
inheritColor: "from", // to, from, false, true (== from)
useGradients: false // release in 4.0
},
navigation: {
enabled: false
},
dataManipulation: {
enabled: false,
initiallyVisible: false
@ -152,6 +149,7 @@ function Network (container, data, options) {
dragView: true,
zoomView: true,
hoverEnabled: false,
showNavigationIcons: false,
tooltip: {
delay: 300,
fontColor: 'black',
@ -538,39 +536,24 @@ Network.prototype.setOptions = function (options) {
}
if ('clickToUse' in options) {
if (options.clickToUse) {
if (!this.activator) {
if (options.clickToUse === true) {
if (this.activator === undefined) {
this.activator = new Activator(this.frame);
this.activator.on('change', this._createKeyBinds.bind(this));
}
}
else {
if (this.activator) {
if (this.activator !== undefined) {
this.activator.destroy();
delete this.activator;
}
this.body.emitter.emit("activate");
}
}
if (options.labels) {
throw new Error('Option "labels" is deprecated. Use options "locale" and "locales" instead.');
else {
this.body.emitter.emit("activate");
}
// (Re)loading the mixins that can be enabled or disabled in the options.
// load the force calculation functions, grouped under the physics system.
// load the navigation system.
//this._loadNavigationControls();
//// load the data manipulation system
//this._loadManipulationSystem();
//// configure the smooth curves
//this._configureSmoothCurves();
// bind hammer
//this.canvas._bindHammer();
// bind keys. If disabled, this will not do anything;
//this._createKeyBinds();
this._markAllEdgesAsDirty();
this.canvas.setSize();
if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) {

+ 0
- 158
lib/network/mixins/MixinLoader.js View File

@ -1,158 +0,0 @@
var SelectionMixin = require('./SelectionMixin');
var ManipulationMixin = require('./ManipulationMixin');
var NavigationMixin = require('./NavigationMixin');
var HierarchicalLayoutMixin = require('./HierarchicalLayoutMixin');
/**
* Load a mixin into the network object
*
* @param {Object} sourceVariable | this object has to contain functions.
* @private
*/
exports._loadMixin = function (sourceVariable) {
for (var mixinFunction in sourceVariable) {
if (sourceVariable.hasOwnProperty(mixinFunction)) {
this[mixinFunction] = sourceVariable[mixinFunction];
}
}
};
/**
* removes a mixin from the network object.
*
* @param {Object} sourceVariable | this object has to contain functions.
* @private
*/
exports._clearMixin = function (sourceVariable) {
for (var mixinFunction in sourceVariable) {
if (sourceVariable.hasOwnProperty(mixinFunction)) {
this[mixinFunction] = undefined;
}
}
};
/**
* Mixin the physics system and initialize the parameters required.
*
* @private
*/
exports._loadPhysicsSystem = function () {
this._loadMixin(PhysicsMixin);
this._loadSelectedForceSolver();
if (this.constants.configurePhysics == true) {
this._loadPhysicsConfiguration();
}
else {
this._cleanupPhysicsConfiguration();
}
};
/**
* Mixin the selection system and initialize the parameters required
*
* @private
*/
exports._loadSelectionSystem = function () {
this.selectionObj = {nodes: {}, edges: {}};
this._loadMixin(SelectionMixin);
};
/**
* Mixin the navigationUI (User Interface) system and initialize the parameters required
*
* @private
*/
exports._loadManipulationSystem = function () {
// reset global variables -- these are used by the selection of nodes and edges.
this.blockConnectingEdgeSelection = false;
this.forceAppendSelection = false;
if (this.constants.dataManipulation.enabled == true) {
// load the manipulator HTML elements. All styling done in css.
if (this.manipulationDiv === undefined) {
this.manipulationDiv = document.createElement('div');
this.manipulationDiv.className = 'network-manipulationDiv';
if (this.editMode == true) {
this.manipulationDiv.style.display = "block";
}
else {
this.manipulationDiv.style.display = "none";
}
this.frame.appendChild(this.manipulationDiv);
}
if (this.editModeDiv === undefined) {
this.editModeDiv = document.createElement('div');
this.editModeDiv.className = 'network-manipulation-editMode';
if (this.editMode == true) {
this.editModeDiv.style.display = "none";
}
else {
this.editModeDiv.style.display = "block";
}
this.frame.appendChild(this.editModeDiv);
}
if (this.closeDiv === undefined) {
this.closeDiv = document.createElement('div');
this.closeDiv.className = 'network-manipulation-closeDiv';
this.closeDiv.style.display = this.manipulationDiv.style.display;
this.frame.appendChild(this.closeDiv);
}
// load the manipulation functions
this._loadMixin(ManipulationMixin);
// create the manipulator toolbar
this._createManipulatorBar();
}
else {
if (this.manipulationDiv !== undefined) {
// removes all the bindings and overloads
this._createManipulatorBar();
// remove the manipulation divs
this.frame.removeChild(this.manipulationDiv);
this.frame.removeChild(this.editModeDiv);
this.frame.removeChild(this.closeDiv);
this.manipulationDiv = undefined;
this.editModeDiv = undefined;
this.closeDiv = undefined;
// remove the mixin functions
this._clearMixin(ManipulationMixin);
}
}
};
/**
* Mixin the navigation (User Interface) system and initialize the parameters required
*
* @private
*/
exports._loadNavigationControls = function () {
this._loadMixin(NavigationMixin);
// the clean function removes the button divs, this is done to remove the bindings.
this._cleanNavigation();
if (this.constants.navigation.enabled == true) {
this._loadNavigationElements();
}
};
/**
* Mixin the hierarchical layout system.
*
* @private
*/
exports._loadHierarchySystem = function () {
this._loadMixin(HierarchicalLayoutMixin);
};

+ 0
- 174
lib/network/mixins/NavigationMixin.js View File

@ -1,174 +0,0 @@
var util = require('../../util');
var Hammer = require('../../module/hammer');
var hammerUtil = require('../../hammerUtil');
exports._cleanNavigation = function() {
// clean hammer bindings
if (this.navigationHammers.length != 0) {
for (var i = 0; i < this.navigationHammers.length; i++) {
this.navigationHammers[i].destroy();
}
this.navigationHammers = [];
}
this._navigationReleaseOverload = function () {};
// clean up previous navigation items
if (this.navigationDOM && this.navigationDOM['wrapper'] && this.navigationDOM['wrapper'].parentNode) {
this.navigationDOM['wrapper'].parentNode.removeChild(this.navigationDOM['wrapper']);
}
};
/**
* Creation of the navigation controls nodes. They are drawn over the rest of the nodes and are not affected by scale and translation
* they have a triggerFunction which is called on click. If the position of the navigation controls is dependent
* on this.frame.canvas.clientWidth or this.frame.canvas.clientHeight, we flag horizontalAlignLeft and verticalAlignTop false.
* This means that the location will be corrected by the _relocateNavigation function on a size change of the canvas.
*
* @private
*/
exports._loadNavigationElements = function() {
this._cleanNavigation();
this.navigationDOM = {};
var navigationDivs = ['up','down','left','right','zoomIn','zoomOut','zoomExtends'];
var navigationDivActions = ['_moveUp','_moveDown','_moveLeft','_moveRight','_zoomIn','_zoomOut','_zoomExtent'];
this.navigationDOM['wrapper'] = document.createElement('div');
this.frame.appendChild(this.navigationDOM['wrapper']);
for (var i = 0; i < navigationDivs.length; i++) {
this.navigationDOM[navigationDivs[i]] = document.createElement('div');
this.navigationDOM[navigationDivs[i]].className = 'network-navigation ' + navigationDivs[i];
this.navigationDOM['wrapper'].appendChild(this.navigationDOM[navigationDivs[i]]);
var hammer = new Hammer(this.navigationDOM[navigationDivs[i]], {prevent_default: true});
hammerUtil.onTouch(hammer, this[navigationDivActions[i]].bind(this));
this.navigationHammers.push(hammer);
}
this._navigationReleaseOverload = this._stopMovement;
};
/**
* this stops all movement induced by the navigation buttons
*
* @private
*/
exports._zoomExtent = function(event) {
this.zoomExtent({duration:700});
event.stopPropagation();
};
/**
* this stops all movement induced by the navigation buttons
*
* @private
*/
exports._stopMovement = function() {
this._xStopMoving();
this._yStopMoving();
this._stopZoom();
};
/**
* move the screen up
* By using the increments, instead of adding a fixed number to the translation, we keep fluent and
* instant movement. The onKeypress event triggers immediately, then pauses, then triggers frequently
* To avoid this behaviour, we do the translation in the start loop.
*
* @private
*/
exports._moveUp = function(event) {
this.yIncrement = this.constants.keyboard.speed.y;
this.start(); // if there is no node movement, the calculation wont be done
event.preventDefault();
};
/**
* move the screen down
* @private
*/
exports._moveDown = function(event) {
this.yIncrement = -this.constants.keyboard.speed.y;
this.start(); // if there is no node movement, the calculation wont be done
event.preventDefault();
};
/**
* move the screen left
* @private
*/
exports._moveLeft = function(event) {
this.xIncrement = this.constants.keyboard.speed.x;
this.start(); // if there is no node movement, the calculation wont be done
event.preventDefault();
};
/**
* move the screen right
* @private
*/
exports._moveRight = function(event) {
this.xIncrement = -this.constants.keyboard.speed.y;
this.start(); // if there is no node movement, the calculation wont be done
event.preventDefault();
};
/**
* Zoom in, using the same method as the movement.
* @private
*/
exports._zoomIn = function(event) {
this.zoomIncrement = this.constants.keyboard.speed.zoom;
this.start(); // if there is no node movement, the calculation wont be done
event.preventDefault();
};
/**
* Zoom out
* @private
*/
exports._zoomOut = function(event) {
this.zoomIncrement = -this.constants.keyboard.speed.zoom;
this.start(); // if there is no node movement, the calculation wont be done
event.preventDefault();
};
/**
* Stop zooming and unhighlight the zoom controls
* @private
*/
exports._stopZoom = function(event) {
this.zoomIncrement = 0;
event && event.preventDefault();
};
/**
* Stop moving in the Y direction and unHighlight the up and down
* @private
*/
exports._yStopMoving = function(event) {
this.yIncrement = 0;
event && event.preventDefault();
};
/**
* Stop moving in the X direction and unHighlight left and right.
* @private
*/
exports._xStopMoving = function(event) {
this.xIncrement = 0;
event && event.preventDefault();
};

+ 0
- 553
lib/network/mixins/SectorsMixin.js View File

@ -1,553 +0,0 @@
var util = require('../../util');
var Node = require('../Node');
/**
* Creation of the SectorMixin var.
*
* This contains all the functions the Network object can use to employ the sector system.
* The sector system is always used by Network, though the benefits only apply to the use of clustering.
* If clustering is not used, there is no overhead except for a duplicate object with references to nodes and edges.
*/
/**
* This function is only called by the setData function of the Network object.
* This loads the global references into the active sector. This initializes the sector.
*
* @private
*/
exports._putDataInSector = function() {
this.body.sectors["active"][this._sector()].nodes = this.body.nodes;
this.body.sectors["active"][this._sector()].edges = this.body.edges;
this.body.sectors["active"][this._sector()].nodeIndices = this.body.nodeIndices;
};
/**
* /**
* This function sets the global references to nodes, edges and nodeIndices back to
* those of the supplied (active) sector. If a type is defined, do the specific type
*
* @param {String} sectorId
* @param {String} [sectorType] | "active" or "frozen"
* @private
*/
exports._switchToSector = function(sectorId, sectorType) {
if (sectorType === undefined || sectorType == "active") {
this._switchToActiveSector(sectorId);
}
else {
this._switchToFrozenSector(sectorId);
}
};
/**
* This function sets the global references to nodes, edges and nodeIndices back to
* those of the supplied active sector.
*
* @param sectorId
* @private
*/
exports._switchToActiveSector = function(sectorId) {
this.body.nodeIndices = this.body.sectors["active"][sectorId]["nodeIndices"];
this.body.nodes = this.body.sectors["active"][sectorId]["nodes"];
this.body.edges = this.body.sectors["active"][sectorId]["edges"];
};
/**
* This function sets the global references to nodes, edges and nodeIndices back to
* those of the supplied active sector.
*
* @private
*/
exports._switchToSupportSector = function() {
this.body.nodeIndices = this.body.sectors["support"]["nodeIndices"];
this.body.nodes = this.body.sectors["support"]["nodes"];
this.body.edges = this.body.sectors["support"]["edges"];
};
/**
* This function sets the global references to nodes, edges and nodeIndices back to
* those of the supplied frozen sector.
*
* @param sectorId
* @private
*/
exports._switchToFrozenSector = function(sectorId) {
this.body.nodeIndices = this.body.sectors["frozen"][sectorId]["nodeIndices"];
this.body.nodes = this.body.sectors["frozen"][sectorId]["nodes"];
this.body.edges = this.body.sectors["frozen"][sectorId]["edges"];
};
/**
* This function sets the global references to nodes, edges and nodeIndices back to
* those of the currently active sector.
*
* @private
*/
exports._loadLatestSector = function() {
this._switchToSector(this._sector());
};
/**
* This function returns the currently active sector Id
*
* @returns {String}
* @private
*/
exports._sector = function() {
return this.activeSector[this.activeSector.length-1];
};
/**
* This function returns the previously active sector Id
*
* @returns {String}
* @private
*/
exports._previousSector = function() {
if (this.activeSector.length > 1) {
return this.activeSector[this.activeSector.length-2];
}
else {
throw new TypeError('there are not enough sectors in the this.activeSector array.');
}
};
/**
* We add the active sector at the end of the this.activeSector array
* This ensures it is the currently active sector returned by _sector() and it reaches the top
* of the activeSector stack. When we reverse our steps we move from the end to the beginning of this stack.
*
* @param newId
* @private
*/
exports._setActiveSector = function(newId) {
this.activeSector.push(newId);
};
/**
* We remove the currently active sector id from the active sector stack. This happens when
* we reactivate the previously active sector
*
* @private
*/
exports._forgetLastSector = function() {
this.activeSector.pop();
};
/**
* This function creates a new active sector with the supplied newId. This newId
* is the expanding node id.
*
* @param {String} newId | Id of the new active sector
* @private
*/
exports._createNewSector = function(newId) {
// create the new sector
this.body.sectors["active"][newId] = {"nodes":{},
"edges":{},
"nodeIndices":[],
"formationScale": this.scale,
"drawingNode": undefined};
// create the new sector render node. This gives visual feedback that you are in a new sector.
this.body.sectors["active"][newId]['drawingNode'] = new Node(
{id:newId,
color: {
background: "#eaefef",
border: "495c5e"
}
},{},{},this.constants);
this.body.sectors["active"][newId]['drawingNode'].clusterSize = 2;
};
/**
* This function removes the currently active sector. This is called when we create a new
* active sector.
*
* @param {String} sectorId | Id of the active sector that will be removed
* @private
*/
exports._deleteActiveSector = function(sectorId) {
delete this.body.sectors["active"][sectorId];
};
/**
* This function removes the currently active sector. This is called when we reactivate
* the previously active sector.
*
* @param {String} sectorId | Id of the active sector that will be removed
* @private
*/
exports._deleteFrozenSector = function(sectorId) {
delete this.body.sectors["frozen"][sectorId];
};
/**
* Freezing an active sector means moving it from the "active" object to the "frozen" object.
* We copy the references, then delete the active entree.
*
* @param sectorId
* @private
*/
exports._freezeSector = function(sectorId) {
// we move the set references from the active to the frozen stack.
this.body.sectors["frozen"][sectorId] = this.body.sectors["active"][sectorId];
// we have moved the sector data into the frozen set, we now remove it from the active set
this._deleteActiveSector(sectorId);
};
/**
* This is the reverse operation of _freezeSector. Activating means moving the sector from the "frozen"
* object to the "active" object.
*
* @param sectorId
* @private
*/
exports._activateSector = function(sectorId) {
// we move the set references from the frozen to the active stack.
this.body.sectors["active"][sectorId] = this.body.sectors["frozen"][sectorId];
// we have moved the sector data into the active set, we now remove it from the frozen stack
this._deleteFrozenSector(sectorId);
};
/**
* This function merges the data from the currently active sector with a frozen sector. This is used
* in the process of reverting back to the previously active sector.
* The data that is placed in the frozen (the previously active) sector is the node that has been removed from it
* upon the creation of a new active sector.
*
* @param sectorId
* @private
*/
exports._mergeThisWithFrozen = function(sectorId) {
// copy all nodes
for (var nodeId in this.body.nodes) {
if (this.body.nodes.hasOwnProperty(nodeId)) {
this.body.sectors["frozen"][sectorId]["nodes"][nodeId] = this.body.nodes[nodeId];
}
}
// copy all edges (if not fully clustered, else there are no edges)
for (var edgeId in this.body.edges) {
if (this.body.edges.hasOwnProperty(edgeId)) {
this.body.sectors["frozen"][sectorId]["edges"][edgeId] = this.body.edges[edgeId];
}
}
// merge the nodeIndices
for (var i = 0; i < this.body.nodeIndices.length; i++) {
this.body.sectors["frozen"][sectorId]["nodeIndices"].push(this.body.nodeIndices[i]);
}
};
/**
* This clusters the sector to one cluster. It was a single cluster before this process started so
* we revert to that state. The clusterToFit function with a maximum size of 1 node does this.
*
* @private
*/
exports._collapseThisToSingleCluster = function() {
this.clusterToFit(1,false);
};
/**
* We create a new active sector from the node that we want to open.
*
* @param node
* @private
*/
exports._addSector = function(node) {
// this is the currently active sector
var sector = this._sector();
// // this should allow me to select nodes from a frozen set.
// if (this.body.sectors['active'][sector]["nodes"].hasOwnProperty(node.id)) {
// console.log("the node is part of the active sector");
// }
// else {
// console.log("I dont know what the fuck happened!!");
// }
// when we switch to a new sector, we remove the node that will be expanded from the current nodes list.
delete this.body.nodes[node.id];
var unqiueIdentifier = util.randomUUID();
// we fully freeze the currently active sector
this._freezeSector(sector);
// we create a new active sector. This sector has the Id of the node to ensure uniqueness
this._createNewSector(unqiueIdentifier);
// we add the active sector to the sectors array to be able to revert these steps later on
this._setActiveSector(unqiueIdentifier);
// we redirect the global references to the new sector's references. this._sector() now returns unqiueIdentifier
this._switchToSector(this._sector());
// finally we add the node we removed from our previous active sector to the new active sector
this.body.nodes[node.id] = node;
};
/**
* We close the sector that is currently open and revert back to the one before.
* If the active sector is the "default" sector, nothing happens.
*
* @private
*/
exports._collapseSector = function() {
// the currently active sector
var sector = this._sector();
// we cannot collapse the default sector
if (sector != "default") {
if ((this.body.nodeIndices.length == 1) ||
(this.body.sectors["active"][sector]["drawingNode"].width*this.scale < this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientWidth) ||
(this.body.sectors["active"][sector]["drawingNode"].height*this.scale < this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientHeight)) {
var previousSector = this._previousSector();
// we collapse the sector back to a single cluster
this._collapseThisToSingleCluster();
// we move the remaining nodes, edges and nodeIndices to the previous sector.
// This previous sector is the one we will reactivate
this._mergeThisWithFrozen(previousSector);
// the previously active (frozen) sector now has all the data from the currently active sector.
// we can now delete the active sector.
this._deleteActiveSector(sector);
// we activate the previously active (and currently frozen) sector.
this._activateSector(previousSector);
// we load the references from the newly active sector into the global references
this._switchToSector(previousSector);
// we forget the previously active sector because we reverted to the one before
this._forgetLastSector();
// finally, we update the node index list.
this._updateNodeIndexList();
// we refresh the list with calulation nodes and calculation node indices.
this._updateCalculationNodes();
}
}
};
/**
* This runs a function in all active sectors. This is used in _redraw() and the _initializeForceCalculation().
*
* @param {String} runFunction | This is the NAME of a function we want to call in all active sectors
* | we dont pass the function itself because then the "this" is the window object
* | instead of the Network object
* @param {*} [argument] | Optional: arguments to pass to the runFunction
* @private
*/
exports._doInAllActiveSectors = function(runFunction,argument) {
var returnValues = [];
if (argument === undefined) {
for (var sector in this.body.sectors["active"]) {
if (this.body.sectors["active"].hasOwnProperty(sector)) {
// switch the global references to those of this sector
this._switchToActiveSector(sector);
returnValues.push( this[runFunction]() );
}
}
}
else {
for (var sector in this.body.sectors["active"]) {
if (this.body.sectors["active"].hasOwnProperty(sector)) {
// switch the global references to those of this sector
this._switchToActiveSector(sector);
var args = Array.prototype.splice.call(arguments, 1);
if (args.length > 1) {
returnValues.push( this[runFunction](args[0],args[1]) );
}
else {
returnValues.push( this[runFunction](argument) );
}
}
}
}
// we revert the global references back to our active sector
this._loadLatestSector();
return returnValues;
};
/**
* This runs a function in all active sectors. This is used in _redraw() and the _initializeForceCalculation().
*
* @param {String} runFunction | This is the NAME of a function we want to call in all active sectors
* | we dont pass the function itself because then the "this" is the window object
* | instead of the Network object
* @param {*} [argument] | Optional: arguments to pass to the runFunction
* @private
*/
exports._doInSupportSector = function(runFunction,argument) {
var returnValues = false;
if (argument === undefined) {
this._switchToSupportSector();
returnValues = this[runFunction]();
}
else {
this._switchToSupportSector();
var args = Array.prototype.splice.call(arguments, 1);
if (args.length > 1) {
returnValues = this[runFunction](args[0],args[1]);
}
else {
returnValues = this[runFunction](argument);
}
}
// we revert the global references back to our active sector
this._loadLatestSector();
return returnValues;
};
/**
* This runs a function in all frozen sectors. This is used in the _redraw().
*
* @param {String} runFunction | This is the NAME of a function we want to call in all active sectors
* | we don't pass the function itself because then the "this" is the window object
* | instead of the Network object
* @param {*} [argument] | Optional: arguments to pass to the runFunction
* @private
*/
exports._doInAllFrozenSectors = function(runFunction,argument) {
if (argument === undefined) {
for (var sector in this.body.sectors["frozen"]) {
if (this.body.sectors["frozen"].hasOwnProperty(sector)) {
// switch the global references to those of this sector
this._switchToFrozenSector(sector);
this[runFunction]();
}
}
}
else {
for (var sector in this.body.sectors["frozen"]) {
if (this.body.sectors["frozen"].hasOwnProperty(sector)) {
// switch the global references to those of this sector
this._switchToFrozenSector(sector);
var args = Array.prototype.splice.call(arguments, 1);
if (args.length > 1) {
this[runFunction](args[0],args[1]);
}
else {
this[runFunction](argument);
}
}
}
}
this._loadLatestSector();
};
/**
* This runs a function in all sectors. This is used in the _redraw().
*
* @param {String} runFunction | This is the NAME of a function we want to call in all active sectors
* | we don't pass the function itself because then the "this" is the window object
* | instead of the Network object
* @param {*} [argument] | Optional: arguments to pass to the runFunction
* @private
*/
exports._doInAllSectors = function(runFunction,argument) {
var args = Array.prototype.splice.call(arguments, 1);
if (argument === undefined) {
this._doInAllActiveSectors(runFunction);
this._doInAllFrozenSectors(runFunction);
}
else {
if (args.length > 1) {
this._doInAllActiveSectors(runFunction,args[0],args[1]);
this._doInAllFrozenSectors(runFunction,args[0],args[1]);
}
else {
this._doInAllActiveSectors(runFunction,argument);
this._doInAllFrozenSectors(runFunction,argument);
}
}
};
/**
* This clears the nodeIndices list. We cannot use this.body.nodeIndices = [] because we would break the link with the
* active sector. Thus we clear the nodeIndices in the active sector, then reconnect the this.body.nodeIndices to it.
*
* @private
*/
exports._clearNodeIndexList = function() {
var sector = this._sector();
this.body.sectors["active"][sector]["nodeIndices"] = [];
this.body.nodeIndices = this.body.sectors["active"][sector]["nodeIndices"];
};
/**
* Draw the encompassing sector node
*
* @param ctx
* @param sectorType
* @private
*/
exports._drawSectorNodes = function(ctx,sectorType) {
var minY = 1e9, maxY = -1e9, minX = 1e9, maxX = -1e9, node;
for (var sector in this.body.sectors[sectorType]) {
if (this.body.sectors[sectorType].hasOwnProperty(sector)) {
if (this.body.sectors[sectorType][sector]["drawingNode"] !== undefined) {
this._switchToSector(sector,sectorType);
minY = 1e9; maxY = -1e9; minX = 1e9; maxX = -1e9;
for (var nodeId in this.body.nodes) {
if (this.body.nodes.hasOwnProperty(nodeId)) {
node = this.body.nodes[nodeId];
node.resize(ctx);
if (minX > node.x - 0.5 * node.width) {minX = node.x - 0.5 * node.width;}
if (maxX < node.x + 0.5 * node.width) {maxX = node.x + 0.5 * node.width;}
if (minY > node.y - 0.5 * node.height) {minY = node.y - 0.5 * node.height;}
if (maxY < node.y + 0.5 * node.height) {maxY = node.y + 0.5 * node.height;}
}
}
node = this.body.sectors[sectorType][sector]["drawingNode"];
node.x = 0.5 * (maxX + minX);
node.y = 0.5 * (maxY + minY);
node.width = 2 * (node.x - minX);
node.height = 2 * (node.y - minY);
node.options.radius = Math.sqrt(Math.pow(0.5*node.width,2) + Math.pow(0.5*node.height,2));
node.setScale(this.scale);
node._drawCircle(ctx);
}
}
}
};
exports._drawAllSectorNodes = function(ctx) {
this._drawSectorNodes(ctx,"frozen");
this._drawSectorNodes(ctx,"active");
this._loadLatestSector();
};

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

@ -89,7 +89,11 @@ class Canvas {
}
this.drag = {};
this.pinch = {};
// init hammer
this.hammer = new Hammer(this.frame.canvas);
this.hammer.get('pinch').set({enable: true});
this.hammer.on('tap', this.body.eventListeners.onTap );
this.hammer.on('doubletap', this.body.eventListeners.onDoubleTap );
this.hammer.on('press', this.body.eventListeners.onHold );
@ -97,7 +101,7 @@ class Canvas {
this.hammer.on('panstart', this.body.eventListeners.onDragStart );
this.hammer.on('panmove', this.body.eventListeners.onDrag );
this.hammer.on('panend', this.body.eventListeners.onDragEnd );
this.hammer.on('pinch', function() {console.log("pinching!");});//this.body.eventListeners.onPinch );
this.hammer.on('pinch', this.body.eventListeners.onPinch );
// TODO: neatly cleanup these handlers when re-creating the Canvas, IF these are done with hammer, event.stopPropagation will not work?
this.frame.canvas.addEventListener('mousewheel', this.body.eventListeners.onMouseWheel);

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

@ -111,7 +111,7 @@ class CanvasRenderer {
}
_redraw(hidden = false) {
this.body.emitter.emit("_beforeRender");
this.body.emitter.emit("initRedraw");
this.redrawRequested = false;
var ctx = this.canvas.frame.canvas.getContext('2d');

+ 33
- 7
lib/network/modules/InteractionHandler.js View File

@ -6,11 +6,14 @@
var util = require('../../util');
import { NavigationHandler } from "./components/NavigationHandler"
class InteractionHandler {
constructor(body, canvas, selectionHandler) {
this.body = body;
this.canvas = canvas;
this.selectionHandler = selectionHandler;
this.navigationHandler = new NavigationHandler(body,canvas);
// bind the events from hammer to functions in this object
this.body.eventListeners.onTap = this.onTap.bind(this);
@ -37,16 +40,38 @@ class InteractionHandler {
dragNodes:true,
dragView: true,
zoomView: true,
selectEnabled: true,
hoverEnabled: false
hoverEnabled: false,
showNavigationIcons: true,
tooltip: {
delay: 300,
fontColor: 'black',
fontSize: 14, // px
fontFace: 'verdana',
color: {
border: '#666',
background: '#FFFFC6'
}
},
keyboard: {
enabled: true,
speed: {x: 10, y: 10, zoom: 0.02},
bindToWindow: true
}
}
util.extend(this.options,this.defaultOptions);
}
setOptions(options) {
if (options !== undefined) {
util.deepExtend(this.options, options);
// extend all but the values in fields
var fields = ['keyboard'];
util.selectiveNotDeepExtend(fields,this.options, options);
// merge the keyboard options in.
util.mergeOptions(this.options, options,'keyboard');
}
this.navigationHandler.setOptions(this.options);
}
@ -131,7 +156,9 @@ class InteractionHandler {
*
* @private
*/
onRelease() {}
onRelease(event) {
this.body.emitter.emit("release",event)
}
/**
@ -274,16 +301,15 @@ class InteractionHandler {
* @private
*/
onPinch(event) {
console.log("on pinch")
var pointer = this.getPointer(event.center);
this.drag.pinched = true;
if (this.pinch[scale] === undefined) {
if (this.pinch['scale'] === undefined) {
this.pinch.scale = 1;
}
// TODO: enabled moving while pinching?
var scale = this.pinch.scale * event.gesture.scale;
var scale = this.pinch.scale * event.scale;
this.zoom(scale, pointer)
}

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

@ -119,7 +119,7 @@ class PhysicsEngine {
stopSimulation() {
this.stabilized = true;
if (this.viewFunction !== undefined) {
this.body.emitter.off("_beforeRender", this.viewFunction);
this.body.emitter.off("initRedraw", this.viewFunction);
this.viewFunction = undefined;
this.body.emitter.emit("_stopRendering");
}
@ -128,7 +128,7 @@ class PhysicsEngine {
runSimulation() {
if (this.viewFunction === undefined) {
this.viewFunction = this.simulationStep.bind(this);
this.body.emitter.on("_beforeRender", this.viewFunction);
this.body.emitter.on("initRedraw", this.viewFunction);
this.body.emitter.emit("_startRendering");
}
}

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

@ -252,7 +252,7 @@ class View {
if (options.animation.duration == 0) {
if (this.lockedOnNodeId != undefined) {
this.viewFunction = this._lockedRedraw.bind(this);
this.body.emitter.on("_beforeRender", this.viewFunction);
this.body.emitter.on("initRedraw", this.viewFunction);
}
else {
this.body.view.scale = this.targetScale;
@ -266,7 +266,7 @@ class View {
this.viewFunction = this._transitionRedraw.bind(this);
this.body.emitter.on("_beforeRender", this.viewFunction);
this.body.emitter.on("initRedraw", this.viewFunction);
this.body.emitter.emit("_startRendering");
}
}
@ -293,7 +293,7 @@ class View {
releaseNode() {
if (this.lockedOnNodeId !== undefined && this.viewFunction !== undefined) {
this.body.emitter.off("_beforeRender", this.viewFunction);
this.body.emitter.off("initRedraw", this.viewFunction);
this.lockedOnNodeId = undefined;
this.lockedOnNodeOffset = undefined;
}
@ -318,11 +318,11 @@ class View {
// cleanup
if (this.easingTime >= 1.0) {
this.body.emitter.off("_beforeRender", this.viewFunction);
this.body.emitter.off("initRedraw", this.viewFunction);
this.easingTime = 0;
if (this.lockedOnNodeId != undefined) {
this.viewFunction = this._lockedRedraw.bind(this);
this.body.emitter.on("_beforeRender", this.viewFunction);
this.body.emitter.on("initRedraw", this.viewFunction);
}
this.body.emitter.emit("animationFinished");
}

+ 203
- 0
lib/network/modules/components/NavigationHandler.js View File

@ -0,0 +1,203 @@
var util = require('../../../util');
var Hammer = require('../../../module/hammer');
var hammerUtil = require('../../../hammerUtil');
var keycharm = require('keycharm');
class NavigationHandler {
constructor(body, canvas) {
this.body = body;
this.canvas = canvas;
this.iconsCreated = false;
this.navigationHammers = [];
this.boundFunctions = {};
this.touchTime = 0;
this.activated = false;
this.body.emitter.on("release", this._stopMovement.bind(this));
this.body.emitter.on("activate", () => {this.activated = true; this.configureKeyboardBindings();});
this.body.emitter.on("deactivate", () => {this.activated = false; this.configureKeyboardBindings();});
this.options = {}
}
setOptions(options) {
if (options !== undefined) {
this.options = options;
this.create();
}
}
create() {
if (this.options.showNavigationIcons === true) {
if (this.iconsCreated === false) {
this.loadNavigationElements();
}
}
else if (this.iconsCreated === true) {
this.cleanNavigation();
}
this.configureKeyboardBindings();
}
cleanNavigation() {
// clean hammer bindings
if (this.navigationHammers.length != 0) {
for (var i = 0; i < this.navigationHammers.length; i++) {
this.navigationHammers[i].destroy();
}
this.navigationHammers = [];
}
this._navigationReleaseOverload = function() {};
// clean up previous navigation items
if (this.navigationDOM && this.navigationDOM['wrapper'] && this.navigationDOM['wrapper'].parentNode) {
this.navigationDOM['wrapper'].parentNode.removeChild(this.navigationDOM['wrapper']);
}
this.iconsCreated = false;
}
/**
* Creation of the navigation controls nodes. They are drawn over the rest of the nodes and are not affected by scale and translation
* they have a triggerFunction which is called on click. If the position of the navigation controls is dependent
* on this.frame.canvas.clientWidth or this.frame.canvas.clientHeight, we flag horizontalAlignLeft and verticalAlignTop false.
* This means that the location will be corrected by the _relocateNavigation function on a size change of the canvas.
*
* @private
*/
loadNavigationElements() {
this.cleanNavigation();
this.navigationDOM = {};
var navigationDivs = ['up','down','left','right','zoomIn','zoomOut','zoomExtends'];
var navigationDivActions = ['_moveUp','_moveDown','_moveLeft','_moveRight','_zoomIn','_zoomOut','_zoomExtent'];
this.navigationDOM['wrapper'] = document.createElement('div');
this.canvas.frame.appendChild(this.navigationDOM['wrapper']);
for (var i = 0; i < navigationDivs.length; i++) {
this.navigationDOM[navigationDivs[i]] = document.createElement('div');
this.navigationDOM[navigationDivs[i]].className = 'network-navigation ' + navigationDivs[i];
this.navigationDOM['wrapper'].appendChild(this.navigationDOM[navigationDivs[i]]);
var hammer = new Hammer(this.navigationDOM[navigationDivs[i]]);
if (navigationDivActions[i] == "_zoomExtent") {
hammerUtil.onTouch(hammer, this._zoomExtent.bind(this));
}
else {
hammerUtil.onTouch(hammer, this.bindToRedraw.bind(this,navigationDivActions[i]));
}
this.navigationHammers.push(hammer);
}
this.iconsCreated = true;
}
bindToRedraw(action) {
if (this.boundFunctions[action] === undefined) {
this.boundFunctions[action] = this[action].bind(this);
this.body.emitter.on("initRedraw", this.boundFunctions[action]);
this.body.emitter.emit("_startRendering");
}
}
unbindFromRedraw(action) {
if (this.boundFunctions[action] !== undefined) {
this.body.emitter.off("initRedraw", this.boundFunctions[action]);
this.body.emitter.emit("_stopRendering");
delete this.boundFunctions[action];
}
}
/**
* this stops all movement induced by the navigation buttons
*
* @private
*/
_zoomExtent() {
if (new Date().valueOf() - this.touchTime > 700) { // TODO: fix ugly hack to avoid hammer's double fireing of event (because we use release?)
this.body.emitter.emit("zoomExtent", {duration: 700});
this.touchTime = new Date().valueOf();
}
}
/**
* this stops all movement induced by the navigation buttons
*
* @private
*/
_stopMovement() {
for (let boundAction in this.boundFunctions) {
if (this.boundFunctions.hasOwnProperty(boundAction)) {
this.body.emitter.off("initRedraw", this.boundFunctions[boundAction]);
this.body.emitter.emit("_stopRendering");
}
}
this.boundFunctions = {};
}
_moveUp() {this.body.view.translation.y -= this.options.keyboard.speed.y;}
_moveDown() {this.body.view.translation.y += this.options.keyboard.speed.y;}
_moveLeft() {this.body.view.translation.x -= this.options.keyboard.speed.x;}
_moveRight(){this.body.view.translation.x += this.options.keyboard.speed.x;}
_zoomIn() {this.body.view.scale += this.options.keyboard.speed.zoom;}
_zoomOut() {this.body.view.scale -= this.options.keyboard.speed.zoom;}
/**
* bind all keys using keycharm.
*/
configureKeyboardBindings() {
if (this.keycharm !== undefined) {
this.keycharm.destroy();
}
if (this.options.keyboard.enabled === true) {
if (this.options.keyboard.bindToWindow === true) {
this.keycharm = keycharm({container: window, preventDefault: false});
}
else {
this.keycharm = keycharm({container: this.canvas.frame, preventDefault: false});
}
this.keycharm.reset();
if (this.activated === true) {
this.keycharm.bind("up", this.bindToRedraw.bind(this, "_moveUp"), "keydown");
this.keycharm.bind("down", this.bindToRedraw.bind(this, "_moveDown"), "keydown");
this.keycharm.bind("left", this.bindToRedraw.bind(this, "_moveLeft"), "keydown");
this.keycharm.bind("right", this.bindToRedraw.bind(this, "_moveRight"), "keydown");
this.keycharm.bind("=", this.bindToRedraw.bind(this, "_zoomIn"), "keydown");
this.keycharm.bind("num+", this.bindToRedraw.bind(this, "_zoomIn"), "keydown");
this.keycharm.bind("num-", this.bindToRedraw.bind(this, "_zoomOut"), "keydown");
this.keycharm.bind("-", this.bindToRedraw.bind(this, "_zoomOut"), "keydown");
this.keycharm.bind("[", this.bindToRedraw.bind(this, "_zoomOut"), "keydown");
this.keycharm.bind("]", this.bindToRedraw.bind(this, "_zoomIn"), "keydown");
this.keycharm.bind("pageup", this.bindToRedraw.bind(this, "_zoomIn"), "keydown");
this.keycharm.bind("pagedown", this.bindToRedraw.bind(this, "_zoomOut"), "keydown");
this.keycharm.bind("up", this.unbindFromRedraw.bind(this, "_moveUp"), "keyup");
this.keycharm.bind("down", this.unbindFromRedraw.bind(this, "_moveDown"), "keyup");
this.keycharm.bind("left", this.unbindFromRedraw.bind(this, "_moveLeft"), "keyup");
this.keycharm.bind("right", this.unbindFromRedraw.bind(this, "_moveRight"), "keyup");
this.keycharm.bind("=", this.unbindFromRedraw.bind(this, "_zoomIn"), "keyup");
this.keycharm.bind("num+", this.unbindFromRedraw.bind(this, "_zoomIn"), "keyup");
this.keycharm.bind("num-", this.unbindFromRedraw.bind(this, "_zoomOut"), "keyup");
this.keycharm.bind("-", this.unbindFromRedraw.bind(this, "_zoomOut"), "keyup");
this.keycharm.bind("[", this.unbindFromRedraw.bind(this, "_zoomOut"), "keyup");
this.keycharm.bind("]", this.unbindFromRedraw.bind(this, "_zoomIn"), "keyup");
this.keycharm.bind("pageup", this.unbindFromRedraw.bind(this, "_zoomIn"), "keyup");
this.keycharm.bind("pagedown", this.unbindFromRedraw.bind(this, "_zoomOut"), "keyup");
}
}
}
}
export {NavigationHandler};

+ 1
- 5
lib/timeline/component/Group.js View File

@ -214,7 +214,7 @@ Group.prototype._calculateHeight = function (margin) {
//this.visibleSubgroups = 0;
this.resetSubgroups();
var me = this;
if (visibleItems.length) {
if (visibleItems.length > 0) {
var min = visibleItems[0].top;
var max = visibleItems[0].top + visibleItems[0].height;
util.forEach(visibleItems, function (item) {
@ -223,10 +223,6 @@ Group.prototype._calculateHeight = function (margin) {
if (item.data.subgroup !== undefined) {
me.subgroups[item.data.subgroup].height = Math.max(me.subgroups[item.data.subgroup].height,item.height);
me.subgroups[item.data.subgroup].visible = true;
//if (visibleSubgroups.indexOf(item.data.subgroup) == -1){
// visibleSubgroups.push(item.data.subgroup);
// me.visibleSubgroups += 1;
//}
}
});
if (min > margin.axis) {

Loading…
Cancel
Save