utils in another file

This commit is contained in:
Klemek
2019-07-15 10:19:44 +02:00
parent 18cd4e8855
commit 9dbf33f64a
7 changed files with 174 additions and 91 deletions
+1 -29
View File
@@ -1,38 +1,10 @@
const placing = require('./placing');
const rendering = require('./rendering');
/**
* Merge resources by reading object keys and keeping reference value only if it's type is different from the source
* @param ref - reference object/value
* @param src - source object/value
* @returns {*}
*/
const merge = (ref, src) => {
if (typeof ref !== typeof src) {
return ref;
} else if (ref.length && !src.length) {
return ref;
} else if (ref.length && src.length) {
return src;
} else if (typeof ref === 'object') {
const out = {};
Object.keys(ref).forEach((key) => out[key] = merge(ref[key], src[key]));
return out;
} else {
return src;
}
};
const DEFAULT_OPTIONS = {
'placing': placing().defaultOptions,
'rendering': rendering().defaultOptions
};
const self = {
options: DEFAULT_OPTIONS,
compute: (data) => {
const options = merge(DEFAULT_OPTIONS, data['options']);
const options = data['options'] || {};
let nodes = {};
const nodeList = (data['nodes'] || []).filter(n => typeof n.name === 'string');
+6 -6
View File
@@ -1,5 +1,4 @@
const ezclone = (a) => JSON.parse(JSON.stringify(a));
const newmap = (w, h, fill) => new Array(w).fill(0).map(() => new Array(h).fill(fill));
const utils = require('./utils');
/**
* @typedef Node1
@@ -21,10 +20,11 @@ const DEFAULT_OPTIONS = {
'diagonals': true,
};
module.exports = (options = DEFAULT_OPTIONS) => {
module.exports = (options) => {
options = utils.merge(DEFAULT_OPTIONS, options);
const self = {
defaultOptions: DEFAULT_OPTIONS,
/**
* Get the current bounds of the graph of nodes
@@ -55,7 +55,7 @@ module.exports = (options = DEFAULT_OPTIONS) => {
*/
getNewPos: (nodes) => {
const b = self.getBounds(nodes);
const map = newmap(b.w, b.h, false);
const map = utils.newMap(b.w, b.h, false);
const list = Object.values(nodes).filter(n => n.x !== undefined);
list.forEach(n => {
map[n.x - b.x][n.y - b.y] = true;
@@ -250,7 +250,7 @@ module.exports = (options = DEFAULT_OPTIONS) => {
const free = [];
const tryPos = (key, x, y) => {
const nodes2 = ezclone(nodes);
const nodes2 = utils.ezClone(nodes);
nodes2[key].x = x;
nodes2[key].y = y;
return self.applyLinks(nodes2, links, depth + 1);
+5 -2
View File
@@ -1,4 +1,5 @@
const convert = require('xml-js');
const utils = require('./utils');
let list = {};
try {
@@ -39,9 +40,11 @@ const DEFAULT_OPTIONS = {
const DEFAULT_SCALE = 0.4;
const LINK_MARGIN = (1 - DEFAULT_SCALE) / 2;
module.exports = (options = DEFAULT_OPTIONS) => {
module.exports = (options) => {
options = utils.merge(DEFAULT_OPTIONS, options);
const self = {
defaultOptions: DEFAULT_OPTIONS,
/**
* Find icon data from given name
+41
View File
@@ -0,0 +1,41 @@
const self = {
/**
* Merge resources by reading object keys and keeping reference value only if it's type is different from the source
* @param ref - reference object/value
* @param src - source object/value
* @returns {*}
*/
merge: (ref, src) => {
if (typeof ref !== typeof src) {
return ref;
} else if (ref.length && !src.length) {
return ref;
} else if (ref.length && src.length) {
return src;
} else if (typeof ref === 'object') {
const out = {};
Object.keys(ref).forEach((key) => out[key] = self.merge(ref[key], src[key]));
return out;
} else {
return src;
}
},
/**
* Clone any JS variable or object
* @param {*} arg
* @returns {any}
*/
ezClone: (arg) => JSON.parse(JSON.stringify(arg)),
/**
* Create a new map of the defined bounds and filling
* @param {number} w
* @param {number} h
* @param {*} fill
* @returns {any[][]}
*/
newMap: (w, h, fill) => new Array(w).fill(0).map(() => new Array(h).fill(fill))
};
module.exports = self;
+49 -49
View File
@@ -3,35 +3,35 @@ const placing = require('../src/placing');
describe('getBounds', () => {
test('no nodes', () => {
const res = placing({}).getBounds({});
const res = placing().getBounds({});
expect(res).toEqual({x: 0, y: 0, w: 0, h: 0});
});
test('no placed nodes', () => {
const res = placing({}).getBounds({
const res = placing().getBounds({
'a': {}, 'b': {}
});
expect(res).toEqual({x: 0, y: 0, w: 0, h: 0});
});
test('first node', () => {
const res = placing({}).getBounds({
const res = placing().getBounds({
'a': {x: 0, y: 0}, 'b': {}
});
expect(res).toEqual({x: 0, y: 0, w: 1, h: 1});
});
test('one node not 0,0', () => {
const res = placing({}).getBounds({
const res = placing().getBounds({
'a': {x: 5, y: 6}, 'b': {}
});
expect(res).toEqual({x: 5, y: 6, w: 1, h: 1});
});
test('2 nodes', () => {
const res = placing({}).getBounds({
const res = placing().getBounds({
'a': {x: 0, y: 0}, 'b': {x: 1, y: 1}, 'c': {}
});
expect(res).toEqual({x: 0, y: 0, w: 2, h: 2});
});
test('2 nodes special', () => {
const res = placing({}).getBounds({
const res = placing().getBounds({
'a': {x: 1, y: 2}, 'b': {x: -5, y: 6}, 'c': {}
});
expect(res).toEqual({x: -5, y: 2, w: 7, h: 5});
@@ -40,29 +40,29 @@ describe('getBounds', () => {
describe('getNewPos', () => {
test('no nodes', () => {
const res = placing({}).getNewPos({});
const res = placing().getNewPos({});
expect(res).toEqual({x: 0, y: 0});
});
test('no placed nodes', () => {
const res = placing({}).getNewPos({
const res = placing().getNewPos({
'a': {}, 'b': {}
});
expect(res).toEqual({x: 0, y: 0});
});
test('one node', () => {
const res = placing({}).getNewPos({
const res = placing().getNewPos({
'a': {x: 0, y: 0}, 'b': {}
});
expect(res).toEqual({x: 1, y: 0});
});
test('one node not 0,0', () => {
const res = placing({}).getNewPos({
const res = placing().getNewPos({
'a': {x: 5, y: 6}, 'b': {}
});
expect(res).toEqual({x: 6, y: 6});
});
test('2 nodes', () => {
const res = placing({}).getNewPos({
const res = placing().getNewPos({
'a': {x: 0, y: 0}, 'b': {x: 1, y: 1}
});
expect(res).toEqual({x: 1, y: 0});
@@ -71,43 +71,43 @@ describe('getNewPos', () => {
describe('nodeBetween', () => {
test('only 2 nodes', () => {
const res = placing({}).nodeBetween({
const res = placing().nodeBetween({
'a': {name: 'a', x: 0, y: 0}, 'b': {name: 'b', x: 1, y: 0}
}, 'a', 'b');
expect(res).toBe(false);
});
test('other node not between', () => {
const res = placing({}).nodeBetween({
const res = placing().nodeBetween({
'a': {name: 'a', x: 0, y: 0}, 'b': {name: 'b', x: 0, y: 1}, 'c': {name: 'c', x: 1, y: 0}
}, 'a', 'b');
expect(res).toBe(false);
});
test('between aligned h', () => {
const res = placing({}).nodeBetween({
const res = placing().nodeBetween({
'a': {name: 'a', x: 0, y: 0}, 'b': {name: 'b', x: 2, y: 0}, 'c': {name: 'c', x: 1, y: 0}
}, 'a', 'b');
expect(res).toBe(true);
});
test('between aligned v', () => {
const res = placing({}).nodeBetween({
const res = placing().nodeBetween({
'a': {name: 'a', x: 0, y: 0}, 'b': {name: 'b', x: 0, y: 2}, 'c': {name: 'c', x: 0, y: 1}
}, 'a', 'b');
expect(res).toBe(true);
});
test('between diagonal', () => {
const res = placing({}).nodeBetween({
const res = placing().nodeBetween({
'a': {name: 'a', x: 0, y: 0}, 'b': {name: 'b', x: 2, y: 2}, 'c': {name: 'c', x: 1, y: 1}
}, 'a', 'b');
expect(res).toBe(true);
});
test('between diagonal 2', () => {
const res = placing({}).nodeBetween({
const res = placing().nodeBetween({
'a': {name: 'a', x: 0, y: 0}, 'b': {name: 'b', x: 2, y: 1}, 'c': {name: 'c', x: 1, y: 1}
}, 'a', 'b');
expect(res).toBe(true);
});
test('between diagonal 3', () => {
const res = placing({}).nodeBetween({
const res = placing().nodeBetween({
'1': {'name': '1', 'x': 1, 'y': 0},
'2': {'name': '2', 'x': 2, 'y': 0},
'3': {'name': '3', 'x': 1, 'y': 1},
@@ -121,7 +121,7 @@ describe('nodeBetween', () => {
describe('getPosition', () => {
test('free node', () => {
const res = placing({'max-link-length': 2}).getPosition({
const res = placing().getPosition({
'a': {
const: {
afterX: [],
@@ -135,7 +135,7 @@ describe('getPosition', () => {
expect(res).toEqual({x: null, y: null, free: true});
});
test('constrained to another not placed', () => {
const res = placing({'max-link-length': 2}).getPosition({
const res = placing().getPosition({
'a': {
const: {
afterX: [],
@@ -149,7 +149,7 @@ describe('getPosition', () => {
expect(res).toEqual({x: null, y: null, free: true});
});
test('constrained to another left', () => {
const res = placing({'max-link-length': 2}).getPosition({
const res = placing().getPosition({
'a': {
const: {
afterX: [],
@@ -163,7 +163,7 @@ describe('getPosition', () => {
expect(res).toEqual({x: 1, y: 0, free: false});
});
test('constrained to another right', () => {
const res = placing({'max-link-length': 2}).getPosition({
const res = placing().getPosition({
'a': {
const: {
afterX: ['b'],
@@ -177,7 +177,7 @@ describe('getPosition', () => {
expect(res).toEqual({x: -1, y: 0, free: false});
});
test('constrained to another up', () => {
const res = placing({'max-link-length': 2}).getPosition({
const res = placing().getPosition({
'a': {
const: {
afterX: [],
@@ -191,7 +191,7 @@ describe('getPosition', () => {
expect(res).toEqual({x: 0, y: 1, free: false});
});
test('constrained to another down', () => {
const res = placing({'max-link-length': 2}).getPosition({
const res = placing().getPosition({
'a': {
const: {
afterX: [],
@@ -205,7 +205,7 @@ describe('getPosition', () => {
expect(res).toEqual({x: 0, y: -1, free: false});
});
test('double constrained diagonal', () => {
const res = placing({'max-link-length': 2}).getPosition({
const res = placing().getPosition({
'a': {
const: {
afterX: [],
@@ -220,7 +220,7 @@ describe('getPosition', () => {
expect(res).toEqual({x: 1, y: 0, free: false});
});
test('double constrained no diagonal', () => {
const res = placing({'max-link-length': 2}).getPosition({
const res = placing().getPosition({
'a': {
const: {
afterX: [],
@@ -235,7 +235,7 @@ describe('getPosition', () => {
expect(res).toEqual({x: 2, y: 0, free: false});
});
test('double constrained impossible', () => {
const res = placing({'max-link-length': 2}).getPosition({
const res = placing().getPosition({
'a': {
const: {
afterX: [],
@@ -253,11 +253,11 @@ describe('getPosition', () => {
describe('isValid', () => {
test('no nodes', () => {
const res = placing({diagonals: true}).isValid({}, []);
const res = placing().isValid({}, []);
expect(res).toBe(true);
});
test('no placed nodes', () => {
const res = placing({diagonals: true}).isValid({
const res = placing().isValid({
'a': {}, 'b': {}
}, [
{from: 'a', to: 'b'}
@@ -265,7 +265,7 @@ describe('isValid', () => {
expect(res).toBe(true);
});
test('one nodes', () => {
const res = placing({diagonals: true}).isValid({
const res = placing().isValid({
'a': {name: 'a', x: 0, y: 0}, 'b': {}
}, [
{from: 'a', to: 'b'}
@@ -273,13 +273,13 @@ describe('isValid', () => {
expect(res).toBe(true);
});
test('overlapping nodes', () => {
const res = placing({diagonals: true}).isValid({
const res = placing().isValid({
'a': {name: 'a', x: 0, y: 0}, 'b': {name: 'b', x: 0, y: 0}
}, []);
expect(res).toBe(false);
});
test('in between node', () => {
const res = placing({diagonals: true}).isValid({
const res = placing().isValid({
'a': {name: 'a', x: 0, y: 0}, 'b': {name: 'b', x: 2, y: 0}, 'c': {name: 'c', x: 1, y: 0}
}, [
{from: 'a', to: 'b'}
@@ -295,7 +295,7 @@ describe('isValid', () => {
expect(res).toBe(false);
});
test('invalid right link', () => {
const res = placing({diagonals: true}).isValid({
const res = placing().isValid({
'a': {name: 'a', x: 0, y: 0}, 'b': {name: 'b', x: -2, y: 2}
}, [
{from: 'a', to: 'b', direction: 'right'}
@@ -303,7 +303,7 @@ describe('isValid', () => {
expect(res).toBe(false);
});
test('invalid left link', () => {
const res = placing({diagonals: true}).isValid({
const res = placing().isValid({
'a': {name: 'a', x: 0, y: 0}, 'b': {name: 'b', x: 2, y: 2}
}, [
{from: 'a', to: 'b', direction: 'left'}
@@ -311,7 +311,7 @@ describe('isValid', () => {
expect(res).toBe(false);
});
test('invalid up link', () => {
const res = placing({diagonals: true}).isValid({
const res = placing().isValid({
'a': {name: 'a', x: 0, y: 0}, 'b': {name: 'b', x: 2, y: 2}
}, [
{from: 'a', to: 'b', direction: 'up'}
@@ -319,7 +319,7 @@ describe('isValid', () => {
expect(res).toBe(false);
});
test('invalid down link', () => {
const res = placing({diagonals: true}).isValid({
const res = placing().isValid({
'a': {name: 'a', x: 0, y: 0}, 'b': {name: 'b', x: 2, y: -2}
}, [
{from: 'a', to: 'b', direction: 'down'}
@@ -327,7 +327,7 @@ describe('isValid', () => {
expect(res).toBe(false);
});
test('valid right link', () => {
const res = placing({diagonals: true}).isValid({
const res = placing().isValid({
'a': {name: 'a', x: 0, y: 0}, 'b': {name: 'b', x: 2, y: 2}
}, [
{from: 'a', to: 'b', direction: 'right'}
@@ -335,7 +335,7 @@ describe('isValid', () => {
expect(res).toBe(true);
});
test('valid left link', () => {
const res = placing({diagonals: true}).isValid({
const res = placing().isValid({
'a': {name: 'a', x: 0, y: 0}, 'b': {name: 'b', x: -2, y: 2}
}, [
{from: 'a', to: 'b', direction: 'left'}
@@ -343,7 +343,7 @@ describe('isValid', () => {
expect(res).toBe(true);
});
test('valid up link', () => {
const res = placing({diagonals: true}).isValid({
const res = placing().isValid({
'a': {name: 'a', x: 0, y: 0}, 'b': {name: 'b', x: 2, y: -2}
}, [
{from: 'a', to: 'b', direction: 'up'}
@@ -351,7 +351,7 @@ describe('isValid', () => {
expect(res).toBe(true);
});
test('valid down link', () => {
const res = placing({diagonals: true}).isValid({
const res = placing().isValid({
'a': {name: 'a', x: 0, y: 0}, 'b': {name: 'b', x: 2, y: 2}
}, [
{from: 'a', to: 'b', direction: 'down'}
@@ -371,7 +371,7 @@ describe('isValid', () => {
describe('correctPlacing', () => {
test('no nodes', () => {
const nodes = {};
placing({}).correctPlacing(nodes);
placing().correctPlacing(nodes);
expect(nodes).toEqual({});
});
test('several nodes', () => {
@@ -380,7 +380,7 @@ describe('correctPlacing', () => {
'b': {x: -2, y: 5},
'c': {x: -4, y: 3}
};
placing({}).correctPlacing(nodes);
placing().correctPlacing(nodes);
expect(nodes).toEqual({
'a': {x: 7, y: 0},
'b': {x: 2, y: 3},
@@ -397,11 +397,11 @@ describe('compute', () => {
};
test('no nodes', () => {
const nodes = placing({'max-link-length': 2, diagonals: false}).compute({}, []);
const nodes = placing({diagonals: false}).compute({}, []);
expect(nodes).toEqual({});
});
test('3 nodes no link', () => {
const nodes = placing({'max-link-length': 2, diagonals: false}).compute(createNodes(3), []);
const nodes = placing({diagonals: false}).compute(createNodes(3), []);
expect(nodes).toEqual({
'1': {name: '1', x: 0, y: 0},
'2': {name: '2', x: 1, y: 0},
@@ -409,7 +409,7 @@ describe('compute', () => {
});
});
test('6 nodes 6 links with directions', () => {
const nodes = placing({'max-link-length': 2, diagonals: true}).compute(createNodes(6), [
const nodes = placing().compute(createNodes(6), [
{from: '1', to: '2', direction: 'right'},
{from: '1', to: '3', direction: 'down'},
{from: '3', to: '4', direction: 'right'},
@@ -426,7 +426,7 @@ describe('compute', () => {
});
});
test('6 nodes 6 links with directions no diagonals', () => {
const nodes = placing({'max-link-length': 2, diagonals: false}).compute(createNodes(6), [
const nodes = placing({diagonals: false}).compute(createNodes(6), [
{from: '1', to: '2', direction: 'right'},
{from: '1', to: '3', direction: 'down'},
{from: '3', to: '4', direction: 'right'},
@@ -443,7 +443,7 @@ describe('compute', () => {
});
});
test('6 nodes 6 links no directions', () => {
const nodes = placing({'max-link-length': 2, diagonals: true}).compute(createNodes(6), [
const nodes = placing().compute(createNodes(6), [
{from: '1', to: '2'},
{from: '1', to: '3'},
{from: '3', to: '4'},
@@ -460,7 +460,7 @@ describe('compute', () => {
});
});
test('6 nodes 6 links no directions no diagonals', () => {
const nodes = placing({'max-link-length': 2, diagonals: false}).compute(createNodes(6), [
const nodes = placing({diagonals: false}).compute(createNodes(6), [
{from: '1', to: '2'},
{from: '1', to: '3'},
{from: '3', to: '4'},
@@ -477,7 +477,7 @@ describe('compute', () => {
});
});
test('3 nodes impossible', () => {
const nodes = placing({'max-link-length': 2, diagonals: false}).compute(createNodes(3), [
const nodes = placing({diagonals: false}).compute(createNodes(3), [
{from: '1', to: '2', direction: 'left'},
{from: '1', to: '3', direction: 'left'},
]);
+5 -5
View File
@@ -83,17 +83,17 @@ describe('getIcon', () => {
describe('getBounds', () => {
test('no nodes', () => {
const res = rendering({beautify: false}).getBounds({});
const res = rendering().getBounds({});
expect(res).toEqual({w: 0, h: 0});
});
test('1 node', () => {
const res = rendering({beautify: false}).getBounds({
const res = rendering().getBounds({
'1': {x: 0, y: 0}
});
expect(res).toEqual({w: 1, h: 1});
});
test('3 nodes', () => {
const res = rendering({beautify: false}).getBounds({
const res = rendering().getBounds({
'1': {x: 0, y: 0}, '2': {x: 5, y: 2}, '3': {x: 3, y: 8},
});
expect(res).toEqual({w: 6, h: 9});
@@ -102,11 +102,11 @@ describe('getBounds', () => {
describe('toXML', () => {
test('no data', () => {
const res = rendering({beautify: false, scale: 20, 'h-spacing': 1}).toXML({}, {w: 0, h: 0});
const res = rendering({scale: 20, 'h-spacing': 1}).toXML({}, {w: 0, h: 0});
expect(res).toEqual('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 0 0" width="0" height="0"/>');
});
test('sample svg data', () => {
const res = rendering({beautify: false, scale: 2, 'h-spacing': 1}).toXML({
const res = rendering({scale: 2, 'h-spacing': 1}).toXML({
'circle': {
'_attributes': {
'cx': 50,
+67
View File
@@ -0,0 +1,67 @@
/* jshint -W117 */
const utils = require('../src/utils');
describe('merge', () => {
test('undefined', () => {
const res = utils.merge({'a': 1}, undefined);
expect(res).toEqual({'a': 1});
});
test('redefine', () => {
const res = utils.merge({'a': 1}, {'a': 2});
expect(res).toEqual({'a': 2});
});
test('wrong type', () => {
const res = utils.merge({'a': 'hello'}, {'a': 2});
expect(res).toEqual({'a': 'hello'});
});
test('array redefine', () => {
const res = utils.merge({'a': [1, 2, 3]}, {'a': [4, 5, 6]});
expect(res).toEqual({'a': [4, 5, 6]});
});
test('sub object wrong type', () => {
const res = utils.merge({'a': {'b': 5}}, {'a': 5});
expect(res).toEqual({'a': {'b': 5}});
});
test('sub object redefine', () => {
const res = utils.merge({'a': {'b': 5}}, {'a': {'b': 6}});
expect(res).toEqual({'a': {'b': 6}});
});
test('add missing keys', () => {
const res = utils.merge({'a': 1, 'b': 3}, {'a': 2});
expect(res).toEqual({'a': 2, 'b': 3});
});
test('extra keys ignore', () => {
const res = utils.merge({'a': 1}, {'a': 2, 'b': 3});
expect(res).toEqual({'a': 2});
});
});
test('ezClone', () => {
const a = {
'a': 5,
'b': {
'c': [1, 2, 3]
}
};
const b = utils.ezClone(a);
expect(b).toEqual(a);
b.b.c[1] = 3;
expect(b).toEqual({
'a': 5,
'b': {
'c': [1, 3, 3]
}
});
expect(a).toEqual({
'a': 5,
'b': {
'c': [1, 2, 3]
}
});
});
test('newMap', () => {
expect(utils.newMap(2, 3, 3)).toEqual([
[3, 3, 3], [3, 3, 3]
]);
});