Browse Source

Fix setting of edge color options via Network.setOptions() (#3377)

* Added commenting for options, refactored choosify()

* Refactored updateLabelModule()

* added comment at node creation

* Completed fix for #3350

* parseOptions() different handling for bridged/extended; unit tests for reversal of options

* Fixes on linting and unit testing
revert-3409-performance
wimrijnders 7 years ago
committed by Yotam Berkowitz
parent
commit
dc33994eed
7 changed files with 479 additions and 123 deletions
  1. +15
    -5
      lib/network/modules/EdgesHandler.js
  2. +56
    -55
      lib/network/modules/components/Edge.js
  3. +7
    -24
      lib/network/modules/components/Node.js
  4. +54
    -0
      lib/network/modules/components/shared/ComponentUtil.js
  5. +13
    -25
      lib/network/modules/components/shared/Label.js
  6. +1
    -1
      lib/util.js
  7. +333
    -13
      test/Network.test.js

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

@ -117,7 +117,7 @@ class EdgesHandler {
value: undefined value: undefined
}; };
util.extend(this.options, this.defaultOptions);
util.deepExtend(this.options, this.defaultOptions);
this.bindEventListeners(); this.bindEventListeners();
} }
@ -140,9 +140,9 @@ class EdgesHandler {
// only forcibly remove the smooth curve if the data has been set of the edge has the smooth curves defined. // only forcibly remove the smooth curve if the data has been set of the edge has the smooth curves defined.
// this is because a change in the global would not affect these curves. // this is because a change in the global would not affect these curves.
if (edgeData !== undefined) { if (edgeData !== undefined) {
let edgeOptions = edgeData.smooth;
if (edgeOptions !== undefined) {
if (edgeOptions.enabled === true && edgeOptions.type === 'dynamic') {
let smoothOptions = edgeData.smooth;
if (smoothOptions !== undefined) {
if (smoothOptions.enabled === true && smoothOptions.type === 'dynamic') {
if (type === undefined) { if (type === undefined) {
edge.setOptions({smooth: false}); edge.setOptions({smooth: false});
} }
@ -197,7 +197,7 @@ class EdgesHandler {
this.edgeOptions = options; this.edgeOptions = options;
if (options !== undefined) { if (options !== undefined) {
// use the parser from the Edge class to fill in all shorthand notations // use the parser from the Edge class to fill in all shorthand notations
Edge.parseOptions(this.options, options);
Edge.parseOptions(this.options, options, true, this.defaultOptions, true);
// update smooth settings in all edges // update smooth settings in all edges
let dataChanged = false; let dataChanged = false;
@ -390,6 +390,16 @@ class EdgesHandler {
* @returns {Edge} * @returns {Edge}
*/ */
create(properties) { create(properties) {
// It is not at all clear why all these separate options should be passed:
//
// - this.edgeOptions is set in setOptions()
// the value of which is also added to this.options with parseOptions()
// - this.defaultOptions has been added to this.options with util.extend() in ctor
//
// So, in theory, this.options should be enough.
// The only reason I can think of for this, is that precedence is important.
// TODO: make unit tests for this, to check if edgeOptions and defaultOptions are redundant
//
return new Edge(properties, this.body, this.options, this.defaultOptions, this.edgeOptions) return new Edge(properties, this.body, this.options, this.defaultOptions, this.edgeOptions)
} }

+ 56
- 55
lib/network/modules/components/Edge.js View File

@ -1,36 +1,33 @@
var util = require('../../../util'); var util = require('../../../util');
var Label = require('./shared/Label').default; var Label = require('./shared/Label').default;
var ComponentUtil = require('./shared/ComponentUtil').default;
var CubicBezierEdge = require('./edges/CubicBezierEdge').default; var CubicBezierEdge = require('./edges/CubicBezierEdge').default;
var BezierEdgeDynamic = require('./edges/BezierEdgeDynamic').default; var BezierEdgeDynamic = require('./edges/BezierEdgeDynamic').default;
var BezierEdgeStatic = require('./edges/BezierEdgeStatic').default; var BezierEdgeStatic = require('./edges/BezierEdgeStatic').default;
var StraightEdge = require('./edges/StraightEdge').default; var StraightEdge = require('./edges/StraightEdge').default;
/** /**
* A edge connects two nodes
* @class Edge
* An edge connects two nodes and has a specific direction.
*/ */
class Edge { class Edge {
/** /**
*
* @param {Object} options Object with options. Must contain
* At least options from and to.
* Available options: from (number),
* to (number), label (string, color (string),
* width (number), style (string),
* length (number), title (string)
* @param {Object} body A Network object, used to find and edge to
* nodes.
* @param {Object} globalOptions An object with default values for
* example for the color
* @param {Object} defaultOptions
* @param {Object} edgeOptions
* @constructor Edge
* @param {Object} options values specific to this edge, must contain at least 'from' and 'to'
* @param {Object} body shared state from Network instance
* @param {Object} globalOptions options from the EdgesHandler instance
* @param {Object} defaultOptions default options from the EdgeHandler instance. Value and reference are constant
* @param {Object} edgeOptions option values specific for edges.
*/ */
constructor(options, body, globalOptions, defaultOptions, edgeOptions) { constructor(options, body, globalOptions, defaultOptions, edgeOptions) {
if (body === undefined) { if (body === undefined) {
throw "No body provided"; throw "No body provided";
} }
// Since globalOptions is constant in values as well as reference,
// Following needs to be done only once.
this.options = util.bridgeObject(globalOptions); this.options = util.bridgeObject(globalOptions);
this.globalOptions = globalOptions; this.globalOptions = globalOptions;
this.defaultOptions = defaultOptions; this.defaultOptions = defaultOptions;
@ -88,7 +85,8 @@ class Edge {
options.value = parseFloat(options.value); options.value = parseFloat(options.value);
} }
this.choosify(options);
let pile = [options, this.options, this.edgeOptions, this.defaultOptions];
this.chooser = ComponentUtil.choosify('edge', pile);
// update label Module // update label Module
this.updateLabelModule(options); this.updateLabelModule(options);
@ -109,14 +107,16 @@ class Edge {
return dataChanged; return dataChanged;
} }
/** /**
* *
* @param {Object} parentOptions * @param {Object} parentOptions
* @param {Object} newOptions * @param {Object} newOptions
* @param {boolean} [allowDeletion=false] * @param {boolean} [allowDeletion=false]
* @param {Object} [globalOptions={}] * @param {Object} [globalOptions={}]
* @param {boolean} [copyFromGlobals=false]
*/ */
static parseOptions(parentOptions, newOptions, allowDeletion = false, globalOptions = {}) {
static parseOptions(parentOptions, newOptions, allowDeletion = false, globalOptions = {}, copyFromGlobals = false) {
var fields = [ var fields = [
'arrowStrikethrough', 'arrowStrikethrough',
'id', 'id',
@ -184,24 +184,44 @@ class Edge {
// handle multiple input cases for color // handle multiple input cases for color
if (newOptions.color !== undefined && newOptions.color !== null) { if (newOptions.color !== undefined && newOptions.color !== null) {
// make a copy of the parent object in case this is referring to the global one (due to object create once, then update)
parentOptions.color = util.deepExtend({}, parentOptions.color, true);
if (util.isString(newOptions.color)) {
parentOptions.color.color = newOptions.color;
parentOptions.color.highlight = newOptions.color;
parentOptions.color.hover = newOptions.color;
parentOptions.color.inherit = false;
let fromColor = newOptions.color;
let toColor = parentOptions.color;
// If passed, fill in values from default options - required in the case of no prototype bridging
if (copyFromGlobals) {
util.deepExtend(toColor, globalOptions.color, false, allowDeletion);
} else {
// Clear local properties - need to do it like this in order to retain prototype bridges
for (var i in toColor) {
if (toColor.hasOwnProperty(i)) {
delete toColor[i];
}
}
}
if (util.isString(toColor)) {
toColor.color = toColor;
toColor.highlight = toColor;
toColor.hover = toColor;
toColor.inherit = false;
if (fromColor.opacity === undefined) {
toColor.opacity = 1.0; // set default
}
} }
else { else {
let colorsDefined = false; let colorsDefined = false;
if (newOptions.color.color !== undefined) {parentOptions.color.color = newOptions.color.color; colorsDefined = true;}
if (newOptions.color.highlight !== undefined) {parentOptions.color.highlight = newOptions.color.highlight; colorsDefined = true;}
if (newOptions.color.hover !== undefined) {parentOptions.color.hover = newOptions.color.hover; colorsDefined = true;}
if (newOptions.color.inherit !== undefined) {parentOptions.color.inherit = newOptions.color.inherit;}
if (newOptions.color.opacity !== undefined) {parentOptions.color.opacity = Math.min(1,Math.max(0,newOptions.color.opacity));}
if (newOptions.color.inherit === undefined && colorsDefined === true) {
parentOptions.color.inherit = false;
if (fromColor.color !== undefined) {toColor.color = fromColor.color; colorsDefined = true;}
if (fromColor.highlight !== undefined) {toColor.highlight = fromColor.highlight; colorsDefined = true;}
if (fromColor.hover !== undefined) {toColor.hover = fromColor.hover; colorsDefined = true;}
if (fromColor.inherit !== undefined) {toColor.inherit = fromColor.inherit;}
if (fromColor.opacity !== undefined) {toColor.opacity = Math.min(1,Math.max(0,fromColor.opacity));}
if (colorsDefined === true) {
toColor.inherit = false;
} else {
if (toColor.inherit === undefined) {
toColor.inherit = 'from'; // Set default
}
} }
} }
} }
@ -218,25 +238,6 @@ class Edge {
} }
} }
/**
*
* @param {Object} options
*/
choosify(options) {
this.chooser = true;
let pile = [options, this.options, this.edgeOptions, this.defaultOptions];
let chosen = util.topMost(pile, 'chosen');
if (typeof chosen === 'boolean') {
this.chooser = chosen;
} else if (typeof chosen === 'object') {
let chosenEdge = util.topMost(pile, ['chosen', 'edge']);
if ((typeof chosenEdge === 'boolean') || (typeof chosenEdge === 'function')) {
this.chooser = chosenEdge;
}
}
}
/** /**
* *
@ -321,12 +322,12 @@ class Edge {
* @param {Object} options * @param {Object} options
*/ */
updateLabelModule(options) { updateLabelModule(options) {
this.labelModule.setOptions(this.options, true);
let pile = [options, this.edgeOptions, this.defaultOptions];
this.labelModule.update(this.options, pile);
if (this.labelModule.baseSize !== undefined) { if (this.labelModule.baseSize !== undefined) {
this.baseFontSize = this.labelModule.baseSize; this.baseFontSize = this.labelModule.baseSize;
} }
this.labelModule.constrain(this.edgeOptions, options, this.defaultOptions);
this.labelModule.choosify(this.edgeOptions, options, this.defaultOptions);
} }
/** /**

+ 7
- 24
lib/network/modules/components/Node.js View File

@ -1,7 +1,7 @@
var util = require('../../../util'); var util = require('../../../util');
var Label = require('./shared/Label').default; var Label = require('./shared/Label').default;
var ComponentUtil = require('./shared/ComponentUtil').default;
var Box = require('./nodes/shapes/Box').default; var Box = require('./nodes/shapes/Box').default;
var Circle = require('./nodes/shapes/Circle').default; var Circle = require('./nodes/shapes/Circle').default;
var CircularImage = require('./nodes/shapes/CircularImage').default; var CircularImage = require('./nodes/shapes/CircularImage').default;
@ -146,7 +146,8 @@ class Node {
// this transforms all shorthands into fully defined options // this transforms all shorthands into fully defined options
Node.parseOptions(this.options, options, true, this.globalOptions); Node.parseOptions(this.options, options, true, this.globalOptions);
this.choosify(options);
let pile = [options, this.options, this.defaultOptions];
this.chooser = ComponentUtil.choosify('node', pile);
this._load_images(); this._load_images();
this.updateLabelModule(options); this.updateLabelModule(options);
@ -259,25 +260,6 @@ class Node {
} }
} }
/**
*
* @param {Object} options
*/
choosify(options) {
this.chooser = true;
let pile = [options, this.options, this.defaultOptions];
let chosen = util.topMost(pile, 'chosen');
if (typeof chosen === 'boolean') {
this.chooser = chosen;
} else if (typeof chosen === 'object') {
let chosenNode = util.topMost(pile, ['chosen', 'node']);
if ((typeof chosenNode === 'boolean') || (typeof chosenNode === 'function')) {
this.chooser = chosenNode;
}
}
}
/** /**
* *
@ -335,14 +317,15 @@ class Node {
if (this.options.label === undefined || this.options.label === null) { if (this.options.label === undefined || this.options.label === null) {
this.options.label = ''; this.options.label = '';
} }
this.labelModule.setOptions(this.options, true);
let pile = [options, this.nodeOptions, this.defaultOptions];
this.labelModule.update(this.options, pile);
if (this.labelModule.baseSize !== undefined) { if (this.labelModule.baseSize !== undefined) {
this.baseFontSize = this.labelModule.baseSize; this.baseFontSize = this.labelModule.baseSize;
} }
this.labelModule.constrain(this.nodeOptions, options, this.defaultOptions);
this.labelModule.choosify(this.nodeOptions, options, this.defaultOptions);
} }
/** /**
* *
* @param {string} currentShape * @param {string} currentShape

+ 54
- 0
lib/network/modules/components/shared/ComponentUtil.js View File

@ -0,0 +1,54 @@
let util = require("../../../../util");
/**
* Helper functions for components
* @class
*/
class ComponentUtil {
/**
* Determine values to use for (sub)options of 'chosen'.
*
* This option is either a boolean or an object whose values should be examined further.
* The relevant structures are:
*
* - chosen: <boolean value>
* - chosen: { subOption: <boolean or function> }
*
* Where subOption is 'node', 'edge' or 'label'.
*
* The intention of this method appears to be to set a specific priority to the options;
* Since most properties are either bridged or merged into the local options objects, there
* is not much point in handling them separately.
* TODO: examine if 'most' in previous sentence can be replaced with 'all'. In that case, we
* should be able to get rid of this method.
*
* @param {String} subOption option within object 'chosen' to consider; either 'node', 'edge' or 'label'
* @param {Object} pile array of options objects to consider
*
* @return {boolean|function} value for passed subOption of 'chosen' to use
*/
static choosify(subOption, pile) {
// allowed values for subOption
let allowed = [ 'node', 'edge', 'label'];
let value = true;
let chosen = util.topMost(pile, 'chosen');
if (typeof chosen === 'boolean') {
value = chosen;
} else if (typeof chosen === 'object') {
if (allowed.indexOf(subOption) === -1 ) {
throw new Error('choosify: subOption \'' + subOption + '\' should be one of '
+ "'" + allowed.join("', '") + "'");
}
let chosenEdge = util.topMost(pile, ['chosen', subOption]);
if ((typeof chosenEdge === 'boolean') || (typeof chosenEdge === 'function')) {
value = chosenEdge;
}
}
return value;
}
}
export default ComponentUtil;

+ 13
- 25
lib/network/modules/components/shared/Label.js View File

@ -1,4 +1,5 @@
let util = require('../../../../util'); let util = require('../../../../util');
let ComponentUtil = require('./ComponentUtil').default;
/** /**
@ -236,18 +237,14 @@ class Label {
/** /**
* Set the width and height constraints based on 'nearest' value * Set the width and height constraints based on 'nearest' value
*
* @param {Object} elementOptions
* @param {Object} options
* @param {Object} defaultOptions
* @param {Array} pile array of option objects to consider
* @private
*/ */
constrain(elementOptions, options, defaultOptions) {
constrain(pile) {
this.fontOptions.constrainWidth = false; this.fontOptions.constrainWidth = false;
this.fontOptions.maxWdt = -1; this.fontOptions.maxWdt = -1;
this.fontOptions.minWdt = -1; this.fontOptions.minWdt = -1;
let pile = [options, elementOptions, defaultOptions];
let widthConstraint = util.topMost(pile, 'widthConstraint'); let widthConstraint = util.topMost(pile, 'widthConstraint');
if (typeof widthConstraint === 'number') { if (typeof widthConstraint === 'number') {
this.fontOptions.maxWdt = Number(widthConstraint); this.fontOptions.maxWdt = Number(widthConstraint);
@ -284,29 +281,20 @@ class Label {
} }
} }
/** /**
* Set the selected functions based on 'nearest' value
* Set options and update internal state
* *
* @param {Object} elementOptions
* @param {Object} options
* @param {Object} defaultOptions
* @param {Object} options options to set
* @param {Array} pile array of option objects to consider for option 'chosen'
*/ */
choosify(elementOptions, options, defaultOptions) {
this.fontOptions.chooser = true;
let pile = [options, elementOptions, defaultOptions];
let chosen = util.topMost(pile, 'chosen');
if (typeof chosen === 'boolean') {
this.fontOptions.chooser = chosen;
} else if (typeof chosen === 'object') {
let chosenLabel = util.topMost(pile, ['chosen', 'label']);
if ((typeof chosenLabel === 'boolean') || (typeof chosenLabel === 'function')) {
this.fontOptions.chooser = chosenLabel;
}
}
update(options, pile) {
this.setOptions(options, true);
this.constrain(pile);
this.fontOptions.chooser = ComponentUtil.choosify('label', pile);
} }
/** /**
* When margins are set in an element, adjust sizes is called to remove them * When margins are set in an element, adjust sizes is called to remove them
* from the width/height constraints. This must be done prior to label sizing. * from the width/height constraints. This must be done prior to label sizing.

+ 1
- 1
lib/util.js View File

@ -305,7 +305,7 @@ exports.selectiveNotDeepExtend = function (props, a, b, allowDeletion = false) {
* @param {Object} b * @param {Object} b
* @param {Boolean} [protoExtend] --> optional parameter. If true, the prototype values will also be extended. * @param {Boolean} [protoExtend] --> optional parameter. If true, the prototype values will also be extended.
* (ie. the options objects that inherit from others will also get the inherited options) * (ie. the options objects that inherit from others will also get the inherited options)
* @param {Boolean} [allowDeletion] --> optional parameter. If true, the values of fields that are null will not deleted
* @param {Boolean} [allowDeletion] --> optional parameter. If true, the values of fields that are null will be deleted
* @returns {Object} * @returns {Object}
*/ */
exports.deepExtend = function (a, b, protoExtend, allowDeletion) { exports.deepExtend = function (a, b, protoExtend, allowDeletion) {

+ 333
- 13
test/Network.test.js View File

@ -11,6 +11,34 @@ var Validator = require("./../lib/shared/Validator").default;
// console.log(JSON.stringify(output, null, 2)); // console.log(JSON.stringify(output, null, 2));
/**
* Merge all options of object b into object b
* @param {Object} a
* @param {Object} b
* @return {Object} a
*
* Adapted merge() in dotparser.js
*/
function merge (a, b) {
if (!a) {
a = {};
}
if (b) {
for (var name in b) {
if (b.hasOwnProperty(name)) {
if (typeof b[name] === 'object') {
a[name] = merge(a[name], b[name]);
} else {
a[name] = b[name];
}
}
}
}
return a;
}
/** /**
* Load legacy-style (i.e. not module) javascript files into the given context. * Load legacy-style (i.e. not module) javascript files into the given context.
*/ */
@ -19,11 +47,11 @@ function include(list, context) {
list = [list]; list = [list];
} }
for (var n in list) {
var path = list[n];
var arr = [fs.readFileSync(path) + ''];
eval.apply(context, arr);
}
for (var n in list) {
var path = list[n];
var arr = [fs.readFileSync(path) + ''];
eval.apply(context, arr);
}
} }
@ -35,7 +63,7 @@ function include(list, context) {
* *
* For reference, this is the sample network of issue #1218 * For reference, this is the sample network of issue #1218
*/ */
function createSampleNetwork() {
function createSampleNetwork(options) {
var NumInitialNodes = 8; var NumInitialNodes = 8;
var NumInitialEdges = 6; var NumInitialEdges = 6;
@ -65,7 +93,7 @@ function createSampleNetwork() {
edges: edges edges: edges
}; };
var options = {
var defaultOptions = {
layout: { layout: {
randomSeed: 8 randomSeed: 8
}, },
@ -76,6 +104,8 @@ function createSampleNetwork() {
} }
}; };
options = merge(defaultOptions, options);
var network = new vis.Network(container, data, options); var network = new vis.Network(container, data, options);
assertNumNodes(network, NumInitialNodes); assertNumNodes(network, NumInitialNodes);
@ -94,7 +124,6 @@ function createSampleNetwork() {
* a cluster is made of two nodes, each from one of the sub-networks. * a cluster is made of two nodes, each from one of the sub-networks.
*/ */
function createCluster(network) { function createCluster(network) {
//console.log("clustering 1 and 11")
var clusterOptionsByData = { var clusterOptionsByData = {
joinCondition: function(node) { joinCondition: function(node) {
if (node.id == 1 || node.id == 11) return true; if (node.id == 1 || node.id == 11) return true;
@ -177,7 +206,7 @@ describe('Network', function () {
//console.log("Creating edge 21 pointing to 1"); //console.log("Creating edge 21 pointing to 1");
// '1' is part of the cluster so should // '1' is part of the cluster so should
// connect to cluster instead // connect to cluster instead
data.edges.update([{from: 21, to: 1}]);
data.edges.update([{from: 21, to: 1}]);
assertNumNodes(network, numNodes, numNodes - 2); // nodes unchanged assertNumNodes(network, numNodes, numNodes - 2); // nodes unchanged
numEdges += 2; // A new clustering edge is hiding a new edge numEdges += 2; // A new clustering edge is hiding a new edge
assertNumEdges(network, numEdges, numEdges - 3); assertNumEdges(network, numEdges, numEdges - 3);
@ -213,7 +242,7 @@ describe('Network', function () {
numEdges -= 3; // clustering edge c1-12 and 2 edges of 12 gone numEdges -= 3; // clustering edge c1-12 and 2 edges of 12 gone
assertNumEdges(network, numEdges, numEdges - 1); assertNumEdges(network, numEdges, numEdges - 1);
//console.log("Unclustering c1");
//console.log("Unclustering c1");
network.openCluster("c1"); network.openCluster("c1");
numNodes -= 1; // cluster node removed, one less node numNodes -= 1; // cluster node removed, one less node
assertNumNodes(network, numNodes, numNodes); // all are visible again assertNumNodes(network, numNodes, numNodes); // all are visible again
@ -243,22 +272,313 @@ describe('Network', function () {
numEdges += 1; // 1 cluster edge expected numEdges += 1; // 1 cluster edge expected
assertNumEdges(network, numEdges, numEdges - 3); // 3 edges hidden assertNumEdges(network, numEdges, numEdges - 3); // 3 edges hidden
//console.log("removing node 2, which is inside the cluster");
//console.log("removing node 2, which is inside the cluster");
data.nodes.remove(2); data.nodes.remove(2);
numNodes -= 1; // clustered node removed numNodes -= 1; // clustered node removed
assertNumNodes(network, numNodes, numNodes - 2); // view doesn't change assertNumNodes(network, numNodes, numNodes - 2); // view doesn't change
numEdges -= 2; // edges removed hidden in cluster numEdges -= 2; // edges removed hidden in cluster
assertNumEdges(network, numEdges, numEdges - 1); // view doesn't change assertNumEdges(network, numEdges, numEdges - 1); // view doesn't change
//console.log("Unclustering c1");
//console.log("Unclustering c1");
network.openCluster("c1") network.openCluster("c1")
numNodes -= 1; // cluster node gone numNodes -= 1; // cluster node gone
assertNumNodes(network, numNodes, numNodes); // all visible assertNumNodes(network, numNodes, numNodes); // all visible
numEdges -= 1; // cluster edge gone numEdges -= 1; // cluster edge gone
assertNumEdges(network, numEdges, numEdges); // all visible assertNumEdges(network, numEdges, numEdges); // all visible
//log(network);
//log(network);
});
/////////////////////////////////////////////////////
// Local helper methods for Edge and Node testing
/////////////////////////////////////////////////////
/**
* Simplify network creation for local tests
*/
function createNetwork(options) {
var [network, data, numNodes, numEdges] = createSampleNetwork(options);
return network;
}
function firstNode(network) {
for (var id in network.body.nodes) {
return network.body.nodes[id];
}
return undefined;
}
function firstEdge(network) {
for (var id in network.body.edges) {
return network.body.edges[id];
}
return undefined;
}
function checkChooserValues(item, chooser, labelChooser) {
if (chooser === 'function') {
assert.equal(typeof item.chooser, 'function');
} else {
assert.equal(item.chooser, chooser);
}
if (labelChooser === 'function') {
assert.equal(typeof item.labelModule.fontOptions.chooser, 'function');
} else {
assert.equal(item.labelModule.fontOptions.chooser, labelChooser);
}
}
describe('Node', function () {
/**
* NOTE: choosify tests of Node and Edge are parallel
* TODO: consolidate this is necessary
*/
it('properly handles choosify input', function () {
// check defaults
var options = {};
var network = createNetwork(options);
checkChooserValues(firstNode(network), true, true);
// There's no point in checking invalid values here; these are detected by the options parser
// and subsequently handled as missing input, thus assigned defaults
// check various combinations of valid input
options = {nodes: {chosen: false}};
network = createNetwork(options);
checkChooserValues(firstNode(network), false, false);
options = {nodes: {chosen: { node:true, label:false}}};
network = createNetwork(options);
checkChooserValues(firstNode(network), true, false);
options = {nodes: {chosen: {
node:true,
label: function(value, id, selected, hovering) {}
}}};
network = createNetwork(options);
checkChooserValues(firstNode(network), true, 'function');
options = {nodes: {chosen: {
node: function(value, id, selected, hovering) {},
label:false,
}}};
network = createNetwork(options);
checkChooserValues(firstNode(network), 'function', false);
});
}); // Node
describe('Edge', function () {
/**
* NOTE: choosify tests of Node and Edge are parallel
* TODO: consolidate this is necessary
*/
it('properly handles choosify input', function () {
// check defaults
var options = {};
var network = createNetwork(options);
checkChooserValues(firstEdge(network), true, true);
// There's no point in checking invalid values here; these are detected by the options parser
// and subsequently handled as missing input, thus assigned defaults
// check various combinations of valid input
options = {edges: {chosen: false}};
network = createNetwork(options);
checkChooserValues(firstEdge(network), false, false);
options = {edges: {chosen: { edge:true, label:false}}};
network = createNetwork(options);
checkChooserValues(firstEdge(network), true, false);
options = {edges: {chosen: {
edge:true,
label: function(value, id, selected, hovering) {}
}}};
network = createNetwork(options);
checkChooserValues(firstEdge(network), true, 'function');
options = {edges: {chosen: {
edge: function(value, id, selected, hovering) {},
label:false,
}}};
network = createNetwork(options);
checkChooserValues(firstEdge(network), 'function', false);
});
/**
* Support routine for next unit test
*/
function createDataforColorChange() {
var nodes = new vis.DataSet([
{id: 1, label: 'Node 1' }, // group:'Group1'},
{id: 2, label: 'Node 2', group:'Group2'},
{id: 3, label: 'Node 3'},
]);
// create an array with edges
var edges = new vis.DataSet([
{id: 1, from: 1, to: 2},
{id: 2, from: 1, to: 3, color: { inherit: 'to'}},
{id: 3, from: 3, to: 3, color: { color: '#00FF00'}},
{id: 4, from: 2, to: 3, color: { inherit: 'from'}},
]);
var data = {
nodes: nodes,
edges: edges
};
return data;
}
/**
* Unit test for fix of #3350
*
* The issue is that changing color options is not registered in the nodes.
* We test the updates the color options in the general edges options here.
*/
it('sets inherit color option for edges on call to Network.setOptions()', function () {
var container = document.getElementById('mynetwork');
var data = createDataforColorChange();
var options = {
"edges" : { "color" : { "inherit" : "to" } },
};
// Test passing options on init.
var network = new vis.Network(container, data, options);
var edges = network.body.edges;
assert.equal(edges[1].options.color.inherit, 'to'); // new default
assert.equal(edges[2].options.color.inherit, 'to'); // set in edge
assert.equal(edges[3].options.color.inherit, false); // has explicit color
assert.equal(edges[4].options.color.inherit, 'from'); // set in edge
// Sanity check: colors should still be defaults
assert.equal(edges[1].options.color.color, network.edgesHandler.options.color.color);
// Override the color value - inherit returns to default
network.setOptions({ edges:{color: {}}});
assert.equal(edges[1].options.color.inherit, 'from'); // default
assert.equal(edges[2].options.color.inherit, 'to'); // set in edge
assert.equal(edges[3].options.color.inherit, false); // has explicit color
assert.equal(edges[4].options.color.inherit, 'from'); // set in edge
// Check no options
network = new vis.Network(container, data, {});
edges = network.body.edges;
assert.equal(edges[1].options.color.inherit, 'from'); // default
assert.equal(edges[2].options.color.inherit, 'to'); // set in edge
assert.equal(edges[3].options.color.inherit, false); // has explicit color
assert.equal(edges[4].options.color.inherit, 'from'); // set in edge
// Set new value
network.setOptions(options);
assert.equal(edges[1].options.color.inherit, 'to');
assert.equal(edges[2].options.color.inherit, 'to'); // set in edge
assert.equal(edges[3].options.color.inherit, false); // has explicit color
assert.equal(edges[4].options.color.inherit, 'from'); // set in edge
/*
// Useful for debugging
console.log('===================================');
console.log(edges[1].options.color);
console.log(edges[1].options.color.__proto__);
console.log(edges[1].options);
console.log(edges[1].options.__proto__);
console.log(edges[1].edgeOptions);
*/
});
it('sets inherit color option for specific edge', function () {
var container = document.getElementById('mynetwork');
var data = createDataforColorChange();
// Check no options
var network = new vis.Network(container, data, {});
var edges = network.body.edges;
assert.equal(edges[1].options.color.inherit, 'from'); // default
assert.equal(edges[2].options.color.inherit, 'to'); // set in edge
assert.equal(edges[3].options.color.inherit, false); // has explicit color
assert.equal(edges[4].options.color.inherit, 'from'); // set in edge
// Set new value
data.edges.update({id: 1, color: { inherit: 'to'}});
assert.equal(edges[1].options.color.inherit, 'to'); // Only this changed
assert.equal(edges[2].options.color.inherit, 'to');
assert.equal(edges[3].options.color.inherit, false); // has explicit color
assert.equal(edges[4].options.color.inherit, 'from');
});
/**
* Perhaps TODO: add unit test for passing string value for color option
*/
it('sets color value for edges on call to Network.setOptions()', function () {
var container = document.getElementById('mynetwork');
var data = createDataforColorChange();
var defaultColor = '#848484'; // From defaults
var color = '#FF0000';
var options = {
"edges" : { "color" : { "color" : color } },
};
// Test passing options on init.
var network = new vis.Network(container, data, options);
var edges = network.body.edges;
assert.equal(edges[1].options.color.color, color);
assert.equal(edges[1].options.color.inherit, false); // Explicit color, so no inherit
assert.equal(edges[2].options.color.color, color);
assert.equal(edges[2].options.color.inherit, 'to'); // Local value overrides! (bug according to docs)
assert.notEqual(edges[3].options.color.color, color); // Has own value
assert.equal(edges[3].options.color.inherit, false); // Explicit color, so no inherit
assert.equal(edges[4].options.color.color, color);
// Override the color value - all should return to default
network.setOptions({ edges:{color: {}}});
assert.equal(edges[1].options.color.color, defaultColor);
assert.equal(edges[1].options.color.inherit, 'from');
assert.equal(edges[2].options.color.color, defaultColor);
assert.notEqual(edges[3].options.color.color, color); // Has own value
assert.equal(edges[4].options.color.color, defaultColor);
// Check no options
network = new vis.Network(container, data, {});
edges = network.body.edges;
// At this point, color has not changed yet
assert.equal(edges[1].options.color.color, defaultColor);
assert.equal(edges[1].options.color.highlight, defaultColor);
assert.equal(edges[1].options.color.inherit, 'from');
assert.notEqual(edges[3].options.color.color, color); // Has own value
// Set new Value
network.setOptions(options);
assert.equal(edges[1].options.color.color, color);
assert.equal(edges[1].options.color.highlight, defaultColor); // Should not be changed
assert.equal(edges[1].options.color.inherit, false); // Explicit color, so no inherit
assert.equal(edges[2].options.color.color, color);
assert.notEqual(edges[3].options.color.color, color); // Has own value
assert.equal(edges[4].options.color.color, color);
}); });
}); // Edge
describe('on node.js', function () { describe('on node.js', function () {

Loading…
Cancel
Save