Split some minor functionality out of Renderer (beginnings of code cleanup)
This commit is contained in:
parent
b240990f3e
commit
c6bc372688
|
@ -0,0 +1,44 @@
|
||||||
|
define(() => {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
function mergeSets(target, b = null) {
|
||||||
|
if(!b) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for(let i = 0; i < b.length; ++ i) {
|
||||||
|
if(target.indexOf(b[i]) === -1) {
|
||||||
|
target.push(b[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeAll(target, b = null) {
|
||||||
|
if(!b) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for(let i = 0; i < b.length; ++ i) {
|
||||||
|
const p = target.indexOf(b[i]);
|
||||||
|
if(p !== -1) {
|
||||||
|
target.splice(p, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove(list, item) {
|
||||||
|
const p = list.indexOf(item);
|
||||||
|
if(p !== -1) {
|
||||||
|
list.splice(p, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function last(list) {
|
||||||
|
return list[list.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
mergeSets,
|
||||||
|
removeAll,
|
||||||
|
remove,
|
||||||
|
last,
|
||||||
|
};
|
||||||
|
});
|
|
@ -0,0 +1,111 @@
|
||||||
|
defineDescribe('ArrayUtilities', ['./ArrayUtilities'], (array) => {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
describe('.mergeSets', () => {
|
||||||
|
it('adds elements from the second array into the first', () => {
|
||||||
|
const p1 = ['a', 'b'];
|
||||||
|
const p2 = ['c', 'd'];
|
||||||
|
array.mergeSets(p1, p2);
|
||||||
|
expect(p1).toEqual(['a', 'b', 'c', 'd']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ignores null parameters', () => {
|
||||||
|
const p1 = ['a', 'b'];
|
||||||
|
array.mergeSets(p1, null);
|
||||||
|
expect(p1).toEqual(['a', 'b']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('leaves the second parameter unchanged', () => {
|
||||||
|
const p1 = ['a', 'b'];
|
||||||
|
const p2 = ['c', 'd'];
|
||||||
|
array.mergeSets(p1, p2);
|
||||||
|
expect(p2).toEqual(['c', 'd']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ignores duplicates', () => {
|
||||||
|
const p1 = ['a', 'b'];
|
||||||
|
const p2 = ['b', 'c'];
|
||||||
|
array.mergeSets(p1, p2);
|
||||||
|
expect(p1).toEqual(['a', 'b', 'c']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('maintains input ordering', () => {
|
||||||
|
const p1 = ['a', 'x', 'c', 'd'];
|
||||||
|
const p2 = ['d', 'x', 'e', 'a'];
|
||||||
|
array.mergeSets(p1, p2);
|
||||||
|
expect(p1).toEqual(['a', 'x', 'c', 'd', 'e']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('.removeAll', () => {
|
||||||
|
it('removes elements from the first array', () => {
|
||||||
|
const p1 = ['a', 'b', 'c'];
|
||||||
|
const p2 = ['a', 'b'];
|
||||||
|
array.removeAll(p1, p2);
|
||||||
|
expect(p1).toEqual(['c']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ignores null parameters', () => {
|
||||||
|
const p1 = ['a', 'b'];
|
||||||
|
array.removeAll(p1, null);
|
||||||
|
expect(p1).toEqual(['a', 'b']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('leaves the second parameter unchanged', () => {
|
||||||
|
const p1 = ['a', 'b', 'c'];
|
||||||
|
const p2 = ['a', 'b'];
|
||||||
|
array.removeAll(p1, p2);
|
||||||
|
expect(p2).toEqual(['a', 'b']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ignores duplicates', () => {
|
||||||
|
const p1 = ['a', 'b', 'c'];
|
||||||
|
const p2 = ['a', 'b', 'b'];
|
||||||
|
array.removeAll(p1, p2);
|
||||||
|
expect(p1).toEqual(['c']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('maintains input ordering', () => {
|
||||||
|
const p1 = ['a', 'x', 'c', 'd'];
|
||||||
|
const p2 = ['c'];
|
||||||
|
array.removeAll(p1, p2);
|
||||||
|
expect(p1).toEqual(['a', 'x', 'd']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('.remove', () => {
|
||||||
|
it('removes one element matching the parameter', () => {
|
||||||
|
const p1 = ['a', 'b'];
|
||||||
|
array.removeAll(p1, 'b');
|
||||||
|
expect(p1).toEqual(['a']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('removes only the first element matching the parameter', () => {
|
||||||
|
const p1 = ['a', 'b', 'c', 'b'];
|
||||||
|
array.removeAll(p1, 'b');
|
||||||
|
expect(p1).toEqual(['a', 'c', 'b']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ignores if not found', () => {
|
||||||
|
const p1 = ['a', 'b', 'c'];
|
||||||
|
array.removeAll(p1, 'nope');
|
||||||
|
expect(p1).toEqual(['a', 'b', 'c']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('maintains input ordering', () => {
|
||||||
|
const p1 = ['a', 'b', 'c'];
|
||||||
|
array.removeAll(p1, 'b');
|
||||||
|
expect(p1).toEqual(['a', 'c']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('.last', () => {
|
||||||
|
it('returns the last element of the array', () => {
|
||||||
|
expect(array.last(['a', 'b'])).toEqual('b');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns undefined for empty arrays', () => {
|
||||||
|
expect(array.last([])).toEqual(undefined);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,28 +1,6 @@
|
||||||
define(() => {
|
define(['./ArrayUtilities'], (array) => {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function mergeSets(target, b) {
|
|
||||||
if(!b) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for(let i = 0; i < b.length; ++ i) {
|
|
||||||
if(target.indexOf(b[i]) === -1) {
|
|
||||||
target.push(b[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeElement(list, item) {
|
|
||||||
const p = list.indexOf(item);
|
|
||||||
if(p !== -1) {
|
|
||||||
list.splice(p, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function lastElement(list) {
|
|
||||||
return list[list.length - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
class AgentState {
|
class AgentState {
|
||||||
constructor(visible, locked = false) {
|
constructor(visible, locked = false) {
|
||||||
this.visible = visible;
|
this.visible = visible;
|
||||||
|
@ -54,8 +32,8 @@ define(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
addBounds(target, agentL, agentR, involvedAgents = null) {
|
addBounds(target, agentL, agentR, involvedAgents = null) {
|
||||||
removeElement(target, agentL);
|
array.remove(target, agentL);
|
||||||
removeElement(target, agentR);
|
array.remove(target, agentR);
|
||||||
|
|
||||||
let indexL = 0;
|
let indexL = 0;
|
||||||
let indexR = target.length;
|
let indexR = target.length;
|
||||||
|
@ -96,9 +74,9 @@ define(() => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const type = (visible ? 'agent begin' : 'agent end');
|
const type = (visible ? 'agent begin' : 'agent end');
|
||||||
const existing = lastElement(this.currentSection.stages) || {};
|
const existing = array.last(this.currentSection.stages) || {};
|
||||||
if(existing.type === type && existing.mode === mode) {
|
if(existing.type === type && existing.mode === mode) {
|
||||||
mergeSets(existing.agents, filteredAgents);
|
array.mergeSets(existing.agents, filteredAgents);
|
||||||
} else {
|
} else {
|
||||||
this.currentSection.stages.push({
|
this.currentSection.stages.push({
|
||||||
type,
|
type,
|
||||||
|
@ -106,8 +84,8 @@ define(() => {
|
||||||
mode,
|
mode,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
mergeSets(this.currentNest.agents, filteredAgents);
|
array.mergeSets(this.currentNest.agents, filteredAgents);
|
||||||
mergeSets(this.agents, filteredAgents);
|
array.mergeSets(this.agents, filteredAgents);
|
||||||
}
|
}
|
||||||
|
|
||||||
beginNested(mode, label, name) {
|
beginNested(mode, label, name) {
|
||||||
|
@ -137,8 +115,8 @@ define(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAgentDefine({agents}) {
|
handleAgentDefine({agents}) {
|
||||||
mergeSets(this.currentNest.agents, agents);
|
array.mergeSets(this.currentNest.agents, agents);
|
||||||
mergeSets(this.agents, agents);
|
array.mergeSets(this.agents, agents);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAgentBegin({agents, mode}) {
|
handleAgentBegin({agents, mode}) {
|
||||||
|
@ -172,11 +150,11 @@ define(() => {
|
||||||
throw new Error('Invalid block nesting');
|
throw new Error('Invalid block nesting');
|
||||||
}
|
}
|
||||||
const {stage, agents} = this.nesting.pop();
|
const {stage, agents} = this.nesting.pop();
|
||||||
this.currentNest = lastElement(this.nesting);
|
this.currentNest = array.last(this.nesting);
|
||||||
this.currentSection = lastElement(this.currentNest.stage.sections);
|
this.currentSection = array.last(this.currentNest.stage.sections);
|
||||||
if(stage.sections.some((section) => section.stages.length > 0)) {
|
if(stage.sections.some((section) => section.stages.length > 0)) {
|
||||||
mergeSets(this.currentNest.agents, agents);
|
array.mergeSets(this.currentNest.agents, agents);
|
||||||
mergeSets(this.agents, agents);
|
array.mergeSets(this.agents, agents);
|
||||||
this.addBounds(
|
this.addBounds(
|
||||||
this.agents,
|
this.agents,
|
||||||
stage.left,
|
stage.left,
|
||||||
|
@ -190,8 +168,8 @@ define(() => {
|
||||||
handleUnknownStage(stage) {
|
handleUnknownStage(stage) {
|
||||||
this.setAgentVis(stage.agents, true, 'box');
|
this.setAgentVis(stage.agents, true, 'box');
|
||||||
this.currentSection.stages.push(stage);
|
this.currentSection.stages.push(stage);
|
||||||
mergeSets(this.currentNest.agents, stage.agents);
|
array.mergeSets(this.currentNest.agents, stage.agents);
|
||||||
mergeSets(this.agents, stage.agents);
|
array.mergeSets(this.agents, stage.agents);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleStage(stage) {
|
handleStage(stage) {
|
||||||
|
|
|
@ -1,66 +1,19 @@
|
||||||
define(() => {
|
define(['./ArrayUtilities', './SVGUtilities'], (array, svg) => {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/* jshint -W071 */ // TODO: break up rendering logic
|
|
||||||
|
|
||||||
const NS = 'http://www.w3.org/2000/svg';
|
|
||||||
|
|
||||||
function makeText(text = '') {
|
|
||||||
return document.createTextNode(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeSVGNode(type, attrs = {}) {
|
|
||||||
const o = document.createElementNS(NS, type);
|
|
||||||
for(let k in attrs) {
|
|
||||||
if(attrs.hasOwnProperty(k)) {
|
|
||||||
o.setAttribute(k, attrs[k]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
function empty(node) {
|
|
||||||
while(node.childNodes.length > 0) {
|
|
||||||
node.removeChild(node.lastChild);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function mergeSets(target, b) {
|
|
||||||
if(!b) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for(let i = 0; i < b.length; ++ i) {
|
|
||||||
if(target.indexOf(b[i]) === -1) {
|
|
||||||
target.push(b[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeAll(target, b) {
|
|
||||||
if(!b) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for(let i = 0; i < b.length; ++ i) {
|
|
||||||
const p = target.indexOf(b[i]);
|
|
||||||
if(p !== -1) {
|
|
||||||
target.splice(p, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function boxRenderer(attrs, position) {
|
function boxRenderer(attrs, position) {
|
||||||
return makeSVGNode('rect', Object.assign({}, position, attrs));
|
return svg.make('rect', Object.assign({}, position, attrs));
|
||||||
}
|
}
|
||||||
|
|
||||||
function noteRenderer(attrs, flickAttrs, position) {
|
function noteRenderer(attrs, flickAttrs, position) {
|
||||||
const g = makeSVGNode('g');
|
const g = svg.make('g');
|
||||||
const x0 = position.x;
|
const x0 = position.x;
|
||||||
const x1 = position.x + position.width;
|
const x1 = position.x + position.width;
|
||||||
const y0 = position.y;
|
const y0 = position.y;
|
||||||
const y1 = position.y + position.height;
|
const y1 = position.y + position.height;
|
||||||
const flick = 7;
|
const flick = 7;
|
||||||
|
|
||||||
g.appendChild(makeSVGNode('path', Object.assign({
|
g.appendChild(svg.make('path', Object.assign({
|
||||||
'd': (
|
'd': (
|
||||||
'M ' + x0 + ' ' + y0 +
|
'M ' + x0 + ' ' + y0 +
|
||||||
' L ' + (x1 - flick) + ' ' + y0 +
|
' L ' + (x1 - flick) + ' ' + y0 +
|
||||||
|
@ -71,7 +24,7 @@ define(() => {
|
||||||
),
|
),
|
||||||
}, attrs)));
|
}, attrs)));
|
||||||
|
|
||||||
g.appendChild(makeSVGNode('path', Object.assign({
|
g.appendChild(svg.make('path', Object.assign({
|
||||||
'd': (
|
'd': (
|
||||||
'M ' + (x1 - flick) + ' ' + y0 +
|
'M ' + (x1 - flick) + ' ' + y0 +
|
||||||
' L ' + (x1 - flick) + ' ' + (y0 + flick) +
|
' L ' + (x1 - flick) + ' ' + (y0 + flick) +
|
||||||
|
@ -87,40 +40,117 @@ define(() => {
|
||||||
const LINE_HEIGHT = 1.3;
|
const LINE_HEIGHT = 1.3;
|
||||||
const TITLE_MARGIN = 10;
|
const TITLE_MARGIN = 10;
|
||||||
const OUTER_MARGIN = 5;
|
const OUTER_MARGIN = 5;
|
||||||
const BOX_PADDING = 10;
|
const AGENT_BOX_PADDING = 10;
|
||||||
const AGENT_MARGIN = 10;
|
const AGENT_MARGIN = 10;
|
||||||
const AGENT_CROSS_SIZE = 20;
|
const AGENT_CROSS_SIZE = 20;
|
||||||
const AGENT_NONE_HEIGHT = 10;
|
const AGENT_NONE_HEIGHT = 10;
|
||||||
const ACTION_MARGIN = 5;
|
const ACTION_MARGIN = 5;
|
||||||
const CONNECT_HEIGHT = 8;
|
|
||||||
const CONNECT_POINT = 4;
|
const CONNECT = {
|
||||||
const CONNECT_LABEL_PADDING = 6;
|
lineAttrs: {
|
||||||
const CONNECT_LABEL_MASK_PADDING = 3;
|
'solid': {
|
||||||
const CONNECT_LABEL_MARGIN = {
|
'fill': 'none',
|
||||||
top: 2,
|
'stroke': '#000000',
|
||||||
bottom: 1,
|
'stroke-width': 1,
|
||||||
|
},
|
||||||
|
'dash': {
|
||||||
|
'fill': 'none',
|
||||||
|
'stroke': '#000000',
|
||||||
|
'stroke-width': 1,
|
||||||
|
'stroke-dasharray': '4, 2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
arrow: {
|
||||||
|
width: 4,
|
||||||
|
height: 8,
|
||||||
|
attrs: {
|
||||||
|
'fill': '#000000',
|
||||||
|
'stroke': '#000000',
|
||||||
|
'stroke-width': 1,
|
||||||
|
'stroke-linejoin': 'miter',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
padding: 6,
|
||||||
|
margin: {top: 2, bottom: 1},
|
||||||
|
attrs: {
|
||||||
|
'font-family': 'sans-serif',
|
||||||
|
'font-size': 8,
|
||||||
|
'text-anchor': 'middle',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mask: {
|
||||||
|
padding: 3,
|
||||||
|
attrs: {
|
||||||
|
'fill': '#FFFFFF',
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
const BLOCK_MARGIN = {
|
|
||||||
|
const BLOCK = {
|
||||||
|
margin: {
|
||||||
top: 0,
|
top: 0,
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
};
|
},
|
||||||
const BLOCK_SECTION_PADDING = {
|
boxAttrs: {
|
||||||
|
'fill': 'none',
|
||||||
|
'stroke': '#000000',
|
||||||
|
'stroke-width': 1.5,
|
||||||
|
'rx': 2,
|
||||||
|
'ry': 2,
|
||||||
|
},
|
||||||
|
section: {
|
||||||
|
padding: {
|
||||||
top: 3,
|
top: 3,
|
||||||
bottom: 2,
|
bottom: 2,
|
||||||
};
|
},
|
||||||
const BLOCK_MODE_PADDING = {
|
mode: {
|
||||||
|
padding: {
|
||||||
top: 1,
|
top: 1,
|
||||||
left: 3,
|
left: 3,
|
||||||
right: 3,
|
right: 3,
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
};
|
},
|
||||||
const BLOCK_LABEL_PADDING = {
|
boxAttrs: {
|
||||||
left: 5,
|
'fill': '#FFFFFF',
|
||||||
right: 5,
|
'stroke': '#000000',
|
||||||
};
|
'stroke-width': 1,
|
||||||
const BLOCK_LABEL_MASK_PADDING = {
|
'rx': 2,
|
||||||
|
'ry': 2,
|
||||||
|
},
|
||||||
|
labelAttrs: {
|
||||||
|
'font-family': 'sans-serif',
|
||||||
|
'font-weight': 'bold',
|
||||||
|
'font-size': 9,
|
||||||
|
'text-anchor': 'left',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
maskPadding: {
|
||||||
left: 3,
|
left: 3,
|
||||||
right: 3,
|
right: 3,
|
||||||
|
},
|
||||||
|
maskAttrs: {
|
||||||
|
'fill': '#FFFFFF',
|
||||||
|
},
|
||||||
|
labelPadding: {
|
||||||
|
left: 5,
|
||||||
|
right: 5,
|
||||||
|
},
|
||||||
|
labelAttrs: {
|
||||||
|
'font-family': 'sans-serif',
|
||||||
|
'font-size': 8,
|
||||||
|
'text-anchor': 'left',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
separator: {
|
||||||
|
attrs: {
|
||||||
|
'stroke': '#000000',
|
||||||
|
'stroke-width': 1.5,
|
||||||
|
'stroke-dasharray': '4, 2',
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const NOTE = {
|
const NOTE = {
|
||||||
|
@ -193,66 +223,6 @@ define(() => {
|
||||||
'fill': '#000000',
|
'fill': '#000000',
|
||||||
'height': 5,
|
'height': 5,
|
||||||
},
|
},
|
||||||
|
|
||||||
BLOCK_BOX: {
|
|
||||||
'fill': 'none',
|
|
||||||
'stroke': '#000000',
|
|
||||||
'stroke-width': 1.5,
|
|
||||||
'rx': 2,
|
|
||||||
'ry': 2,
|
|
||||||
},
|
|
||||||
BLOCK_SEPARATOR: {
|
|
||||||
'stroke': '#000000',
|
|
||||||
'stroke-width': 1.5,
|
|
||||||
'stroke-dasharray': '4, 2',
|
|
||||||
},
|
|
||||||
BLOCK_MODE: {
|
|
||||||
'fill': '#FFFFFF',
|
|
||||||
'stroke': '#000000',
|
|
||||||
'stroke-width': 1,
|
|
||||||
'rx': 2,
|
|
||||||
'ry': 2,
|
|
||||||
},
|
|
||||||
BLOCK_MODE_LABEL: {
|
|
||||||
'font-family': 'sans-serif',
|
|
||||||
'font-weight': 'bold',
|
|
||||||
'font-size': 9,
|
|
||||||
'text-anchor': 'left',
|
|
||||||
},
|
|
||||||
BLOCK_LABEL: {
|
|
||||||
'font-family': 'sans-serif',
|
|
||||||
'font-size': 8,
|
|
||||||
'text-anchor': 'left',
|
|
||||||
},
|
|
||||||
BLOCK_LABEL_MASK: {
|
|
||||||
'fill': '#FFFFFF',
|
|
||||||
},
|
|
||||||
|
|
||||||
CONNECT_LINE_SOLID: {
|
|
||||||
'fill': 'none',
|
|
||||||
'stroke': '#000000',
|
|
||||||
'stroke-width': 1,
|
|
||||||
},
|
|
||||||
CONNECT_LINE_DASH: {
|
|
||||||
'fill': 'none',
|
|
||||||
'stroke': '#000000',
|
|
||||||
'stroke-width': 1,
|
|
||||||
'stroke-dasharray': '4, 2',
|
|
||||||
},
|
|
||||||
CONNECT_LABEL: {
|
|
||||||
'font-family': 'sans-serif',
|
|
||||||
'font-size': 8,
|
|
||||||
'text-anchor': 'middle',
|
|
||||||
},
|
|
||||||
CONNECT_LABEL_MASK: {
|
|
||||||
'fill': '#FFFFFF',
|
|
||||||
},
|
|
||||||
CONNECT_HEAD: {
|
|
||||||
'fill': '#000000',
|
|
||||||
'stroke': '#000000',
|
|
||||||
'stroke-width': 1,
|
|
||||||
'stroke-linejoin': 'miter',
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function traverse(stages, callbacks) {
|
function traverse(stages, callbacks) {
|
||||||
|
@ -282,36 +252,6 @@ define(() => {
|
||||||
|
|
||||||
return class Renderer {
|
return class Renderer {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.base = makeSVGNode('svg', {
|
|
||||||
'xmlns': NS,
|
|
||||||
'version': '1.1',
|
|
||||||
'width': '100%',
|
|
||||||
'height': '100%',
|
|
||||||
});
|
|
||||||
|
|
||||||
this.title = makeSVGNode('text', Object.assign({
|
|
||||||
'y': ATTRS.TITLE['font-size'] + OUTER_MARGIN,
|
|
||||||
}, ATTRS.TITLE));
|
|
||||||
this.titleText = makeText();
|
|
||||||
this.title.appendChild(this.titleText);
|
|
||||||
this.base.appendChild(this.title);
|
|
||||||
|
|
||||||
this.diagram = makeSVGNode('g');
|
|
||||||
this.agentLines = makeSVGNode('g');
|
|
||||||
this.blocks = makeSVGNode('g');
|
|
||||||
this.sections = makeSVGNode('g');
|
|
||||||
this.agentDecor = makeSVGNode('g');
|
|
||||||
this.actions = makeSVGNode('g');
|
|
||||||
this.diagram.appendChild(this.agentLines);
|
|
||||||
this.diagram.appendChild(this.blocks);
|
|
||||||
this.diagram.appendChild(this.sections);
|
|
||||||
this.diagram.appendChild(this.agentDecor);
|
|
||||||
this.diagram.appendChild(this.actions);
|
|
||||||
this.base.appendChild(this.diagram);
|
|
||||||
|
|
||||||
this.testers = makeSVGNode('g');
|
|
||||||
this.testersCache = new Map();
|
|
||||||
|
|
||||||
this.separationAgentCap = {
|
this.separationAgentCap = {
|
||||||
'box': this.separationAgentCapBox.bind(this),
|
'box': this.separationAgentCapBox.bind(this),
|
||||||
'cross': this.separationAgentCapCross.bind(this),
|
'cross': this.separationAgentCapCross.bind(this),
|
||||||
|
@ -363,6 +303,37 @@ define(() => {
|
||||||
|
|
||||||
this.width = 0;
|
this.width = 0;
|
||||||
this.height = 0;
|
this.height = 0;
|
||||||
|
this.buildStaticElements();
|
||||||
|
}
|
||||||
|
|
||||||
|
buildStaticElements() {
|
||||||
|
this.base = svg.makeContainer({
|
||||||
|
'width': '100%',
|
||||||
|
'height': '100%',
|
||||||
|
});
|
||||||
|
|
||||||
|
this.title = svg.make('text', Object.assign({
|
||||||
|
'y': ATTRS.TITLE['font-size'] + OUTER_MARGIN,
|
||||||
|
}, ATTRS.TITLE));
|
||||||
|
this.titleText = svg.makeText();
|
||||||
|
this.title.appendChild(this.titleText);
|
||||||
|
this.base.appendChild(this.title);
|
||||||
|
|
||||||
|
this.diagram = svg.make('g');
|
||||||
|
this.agentLines = svg.make('g');
|
||||||
|
this.blocks = svg.make('g');
|
||||||
|
this.sections = svg.make('g');
|
||||||
|
this.agentDecor = svg.make('g');
|
||||||
|
this.actions = svg.make('g');
|
||||||
|
this.diagram.appendChild(this.agentLines);
|
||||||
|
this.diagram.appendChild(this.blocks);
|
||||||
|
this.diagram.appendChild(this.sections);
|
||||||
|
this.diagram.appendChild(this.agentDecor);
|
||||||
|
this.diagram.appendChild(this.actions);
|
||||||
|
this.base.appendChild(this.diagram);
|
||||||
|
|
||||||
|
this.testers = svg.make('g');
|
||||||
|
this.testersCache = new Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
findExtremes(agents) {
|
findExtremes(agents) {
|
||||||
|
@ -440,7 +411,7 @@ define(() => {
|
||||||
|
|
||||||
separationAgent({type, mode, agents}) {
|
separationAgent({type, mode, agents}) {
|
||||||
if(type === 'agent begin') {
|
if(type === 'agent begin') {
|
||||||
mergeSets(this.visibleAgents, agents);
|
array.mergeSets(this.visibleAgents, agents);
|
||||||
}
|
}
|
||||||
|
|
||||||
const agentSpaces = new Map();
|
const agentSpaces = new Map();
|
||||||
|
@ -452,7 +423,7 @@ define(() => {
|
||||||
this.addSeparations(this.visibleAgents, agentSpaces);
|
this.addSeparations(this.visibleAgents, agentSpaces);
|
||||||
|
|
||||||
if(type === 'agent end') {
|
if(type === 'agent end') {
|
||||||
removeAll(this.visibleAgents, agents);
|
array.removeAll(this.visibleAgents, agents);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -461,9 +432,9 @@ define(() => {
|
||||||
agents[0],
|
agents[0],
|
||||||
agents[1],
|
agents[1],
|
||||||
|
|
||||||
this.testTextWidth(ATTRS.CONNECT_LABEL, label) +
|
this.testTextWidth(CONNECT.label.attrs, label) +
|
||||||
CONNECT_POINT * 2 +
|
CONNECT.arrow.width * 2 +
|
||||||
CONNECT_LABEL_PADDING * 2 +
|
CONNECT.label.padding * 2 +
|
||||||
ATTRS.AGENT_LINE['stroke-width']
|
ATTRS.AGENT_LINE['stroke-width']
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -553,22 +524,24 @@ define(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
separationBlockBegin(scope, {left, right}) {
|
separationBlockBegin(scope, {left, right}) {
|
||||||
mergeSets(this.visibleAgents, [left, right]);
|
array.mergeSets(this.visibleAgents, [left, right]);
|
||||||
this.addSeparations(this.visibleAgents, new Map());
|
this.addSeparations(this.visibleAgents, new Map());
|
||||||
}
|
}
|
||||||
|
|
||||||
separationSectionBegin(scope, {left, right}, {mode, label}) {
|
separationSectionBegin(scope, {left, right}, {mode, label}) {
|
||||||
const width = (
|
const width = (
|
||||||
this.testTextWidth(ATTRS.BLOCK_MODE_LABEL, mode) +
|
this.testTextWidth(BLOCK.section.mode.labelAttrs, mode) +
|
||||||
BLOCK_MODE_PADDING.left + BLOCK_MODE_PADDING.right +
|
BLOCK.section.mode.padding.left +
|
||||||
this.testTextWidth(ATTRS.BLOCK_LABEL, label) +
|
BLOCK.section.mode.padding.right +
|
||||||
BLOCK_LABEL_PADDING.left + BLOCK_LABEL_PADDING.right
|
this.testTextWidth(BLOCK.section.label.labelAttrs, label) +
|
||||||
|
BLOCK.section.label.labelPadding.left +
|
||||||
|
BLOCK.section.label.labelPadding.right
|
||||||
);
|
);
|
||||||
this.addSeparation(left, right, width);
|
this.addSeparation(left, right, width);
|
||||||
}
|
}
|
||||||
|
|
||||||
separationBlockEnd(scope, {left, right}) {
|
separationBlockEnd(scope, {left, right}) {
|
||||||
removeAll(this.visibleAgents, [left, right]);
|
array.removeAll(this.visibleAgents, [left, right]);
|
||||||
}
|
}
|
||||||
|
|
||||||
checkSeparation(stage) {
|
checkSeparation(stage) {
|
||||||
|
@ -576,20 +549,20 @@ define(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderAgentCapBox({x, labelWidth, label}) {
|
renderAgentCapBox({x, labelWidth, label}) {
|
||||||
this.agentDecor.appendChild(makeSVGNode('rect', Object.assign({
|
this.agentDecor.appendChild(svg.make('rect', Object.assign({
|
||||||
'x': x - labelWidth / 2,
|
'x': x - labelWidth / 2,
|
||||||
'y': this.currentY,
|
'y': this.currentY,
|
||||||
'width': labelWidth,
|
'width': labelWidth,
|
||||||
}, ATTRS.AGENT_BOX)));
|
}, ATTRS.AGENT_BOX)));
|
||||||
|
|
||||||
const name = makeSVGNode('text', Object.assign({
|
const name = svg.make('text', Object.assign({
|
||||||
'x': x,
|
'x': x,
|
||||||
'y': this.currentY + (
|
'y': this.currentY + (
|
||||||
ATTRS.AGENT_BOX.height +
|
ATTRS.AGENT_BOX.height +
|
||||||
ATTRS.AGENT_BOX_LABEL['font-size'] * (2 - LINE_HEIGHT)
|
ATTRS.AGENT_BOX_LABEL['font-size'] * (2 - LINE_HEIGHT)
|
||||||
) / 2,
|
) / 2,
|
||||||
}, ATTRS.AGENT_BOX_LABEL));
|
}, ATTRS.AGENT_BOX_LABEL));
|
||||||
name.appendChild(makeText(label));
|
name.appendChild(svg.makeText(label));
|
||||||
this.agentDecor.appendChild(name);
|
this.agentDecor.appendChild(name);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -603,7 +576,7 @@ define(() => {
|
||||||
const y = this.currentY;
|
const y = this.currentY;
|
||||||
const d = AGENT_CROSS_SIZE / 2;
|
const d = AGENT_CROSS_SIZE / 2;
|
||||||
|
|
||||||
this.agentDecor.appendChild(makeSVGNode('path', Object.assign({
|
this.agentDecor.appendChild(svg.make('path', Object.assign({
|
||||||
'd': (
|
'd': (
|
||||||
'M ' + (x - d) + ' ' + y +
|
'M ' + (x - d) + ' ' + y +
|
||||||
' L ' + (x + d) + ' ' + (y + d * 2) +
|
' L ' + (x + d) + ' ' + (y + d * 2) +
|
||||||
|
@ -620,7 +593,7 @@ define(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderAgentCapBar({x, labelWidth}) {
|
renderAgentCapBar({x, labelWidth}) {
|
||||||
this.agentDecor.appendChild(makeSVGNode('rect', Object.assign({
|
this.agentDecor.appendChild(svg.make('rect', Object.assign({
|
||||||
'x': x - labelWidth / 2,
|
'x': x - labelWidth / 2,
|
||||||
'y': this.currentY,
|
'y': this.currentY,
|
||||||
'width': labelWidth,
|
'width': labelWidth,
|
||||||
|
@ -657,7 +630,7 @@ define(() => {
|
||||||
const agentInfo = this.agentInfos.get(agent);
|
const agentInfo = this.agentInfos.get(agent);
|
||||||
const x = agentInfo.x;
|
const x = agentInfo.x;
|
||||||
shifts = this.renderAgentCap[mode](agentInfo);
|
shifts = this.renderAgentCap[mode](agentInfo);
|
||||||
this.agentLines.appendChild(makeSVGNode('path', Object.assign({
|
this.agentLines.appendChild(svg.make('path', Object.assign({
|
||||||
'd': (
|
'd': (
|
||||||
'M ' + x + ' ' + agentInfo.latestYStart +
|
'M ' + x + ' ' + agentInfo.latestYStart +
|
||||||
' L ' + x + ' ' + (this.currentY + shifts.lineTop)
|
' L ' + x + ' ' + (this.currentY + shifts.lineTop)
|
||||||
|
@ -674,74 +647,69 @@ define(() => {
|
||||||
const from = this.agentInfos.get(agents[0]);
|
const from = this.agentInfos.get(agents[0]);
|
||||||
const to = this.agentInfos.get(agents[1]);
|
const to = this.agentInfos.get(agents[1]);
|
||||||
|
|
||||||
const dy = CONNECT_HEIGHT / 2;
|
const dy = CONNECT.arrow.height / 2;
|
||||||
const dx = CONNECT_POINT;
|
const dx = CONNECT.arrow.width;
|
||||||
const dir = (from.x < to.x) ? 1 : -1;
|
const dir = (from.x < to.x) ? 1 : -1;
|
||||||
const short = ATTRS.AGENT_LINE['stroke-width'];
|
const short = ATTRS.AGENT_LINE['stroke-width'];
|
||||||
let y = this.currentY;
|
let y = this.currentY;
|
||||||
|
|
||||||
const lineAttrs = {
|
|
||||||
'solid': ATTRS.CONNECT_LINE_SOLID,
|
|
||||||
'dash': ATTRS.CONNECT_LINE_DASH,
|
|
||||||
};
|
|
||||||
|
|
||||||
if(label) {
|
if(label) {
|
||||||
const mask = makeSVGNode('rect', ATTRS.CONNECT_LABEL_MASK);
|
const mask = svg.make('rect', CONNECT.mask.attrs);
|
||||||
const labelNode = makeSVGNode('text', ATTRS.CONNECT_LABEL);
|
const labelNode = svg.make('text', CONNECT.label.attrs);
|
||||||
labelNode.appendChild(makeText(label));
|
labelNode.appendChild(svg.makeText(label));
|
||||||
const sz = ATTRS.CONNECT_LABEL['font-size'];
|
const sz = CONNECT.label.attrs['font-size'];
|
||||||
this.actions.appendChild(mask);
|
this.actions.appendChild(mask);
|
||||||
this.actions.appendChild(labelNode);
|
this.actions.appendChild(labelNode);
|
||||||
y += Math.max(
|
y += Math.max(
|
||||||
dy,
|
dy,
|
||||||
CONNECT_LABEL_MARGIN.top +
|
CONNECT.label.margin.top +
|
||||||
sz * LINE_HEIGHT +
|
sz * LINE_HEIGHT +
|
||||||
CONNECT_LABEL_MARGIN.bottom
|
CONNECT.label.margin.bottom
|
||||||
);
|
);
|
||||||
const w = labelNode.getComputedTextLength();
|
const w = labelNode.getComputedTextLength();
|
||||||
const x = (from.x + to.x) / 2;
|
const x = (from.x + to.x) / 2;
|
||||||
const yBase = (
|
const yBase = (
|
||||||
y -
|
y -
|
||||||
sz * (LINE_HEIGHT - 1) -
|
sz * (LINE_HEIGHT - 1) -
|
||||||
CONNECT_LABEL_MARGIN.bottom
|
CONNECT.label.margin.bottom
|
||||||
);
|
);
|
||||||
labelNode.setAttribute('x', x);
|
labelNode.setAttribute('x', x);
|
||||||
labelNode.setAttribute('y', yBase);
|
labelNode.setAttribute('y', yBase);
|
||||||
mask.setAttribute('x', x - w / 2 - CONNECT_LABEL_MASK_PADDING);
|
mask.setAttribute('x', x - w / 2 - CONNECT.mask.padding);
|
||||||
mask.setAttribute('y', yBase - sz);
|
mask.setAttribute('y', yBase - sz);
|
||||||
mask.setAttribute('width', w + CONNECT_LABEL_MASK_PADDING * 2);
|
mask.setAttribute('width', w + CONNECT.mask.padding * 2);
|
||||||
mask.setAttribute('height', sz * LINE_HEIGHT);
|
mask.setAttribute('height', sz * LINE_HEIGHT);
|
||||||
} else {
|
} else {
|
||||||
y += dy;
|
y += dy;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.actions.appendChild(makeSVGNode('path', Object.assign({
|
this.actions.appendChild(svg.make('path', Object.assign({
|
||||||
'd': (
|
'd': (
|
||||||
'M ' + (from.x + (left ? short : 0) * dir) + ' ' + y +
|
'M ' + (from.x + (left ? short : 0) * dir) + ' ' + y +
|
||||||
' L ' + (to.x - (right ? short : 0) * dir) + ' ' + y
|
' L ' + (to.x - (right ? short : 0) * dir) + ' ' + y
|
||||||
),
|
),
|
||||||
}, lineAttrs[line])));
|
}, CONNECT.lineAttrs[line])));
|
||||||
|
|
||||||
if(left) {
|
if(left) {
|
||||||
this.actions.appendChild(makeSVGNode('path', Object.assign({
|
this.actions.appendChild(svg.make('path', Object.assign({
|
||||||
'd': (
|
'd': (
|
||||||
'M ' + (from.x + (dx + short) * dir) + ' ' + (y - dy) +
|
'M ' + (from.x + (dx + short) * dir) + ' ' + (y - dy) +
|
||||||
' L ' + (from.x + short * dir) + ' ' + y +
|
' L ' + (from.x + short * dir) + ' ' + y +
|
||||||
' L ' + (from.x + (dx + short) * dir) + ' ' + (y + dy) +
|
' L ' + (from.x + (dx + short) * dir) + ' ' + (y + dy) +
|
||||||
(ATTRS.CONNECT_HEAD.fill === 'none' ? '' : ' Z')
|
(CONNECT.arrow.attrs.fill === 'none' ? '' : ' Z')
|
||||||
),
|
),
|
||||||
}, ATTRS.CONNECT_HEAD)));
|
}, CONNECT.arrow.attrs)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(right) {
|
if(right) {
|
||||||
this.actions.appendChild(makeSVGNode('path', Object.assign({
|
this.actions.appendChild(svg.make('path', Object.assign({
|
||||||
'd': (
|
'd': (
|
||||||
'M ' + (to.x - (dx + short) * dir) + ' ' + (y - dy) +
|
'M ' + (to.x - (dx + short) * dir) + ' ' + (y - dy) +
|
||||||
' L ' + (to.x - short * dir) + ' ' + y +
|
' L ' + (to.x - short * dir) + ' ' + y +
|
||||||
' L ' + (to.x - (dx + short) * dir) + ' ' + (y + dy) +
|
' L ' + (to.x - (dx + short) * dir) + ' ' + (y + dy) +
|
||||||
(ATTRS.CONNECT_HEAD.fill === 'none' ? '' : ' Z')
|
(CONNECT.arrow.attrs.fill === 'none' ? '' : ' Z')
|
||||||
),
|
),
|
||||||
}, ATTRS.CONNECT_HEAD)));
|
}, CONNECT.arrow.attrs)));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.currentY = y + dy + ACTION_MARGIN;
|
this.currentY = y + dy + ACTION_MARGIN;
|
||||||
|
@ -754,11 +722,11 @@ define(() => {
|
||||||
|
|
||||||
this.currentY += config.margin.top;
|
this.currentY += config.margin.top;
|
||||||
|
|
||||||
const labelNode = makeSVGNode('text', Object.assign({
|
const labelNode = svg.make('text', Object.assign({
|
||||||
'y': this.currentY + config.padding.top + sz,
|
'y': this.currentY + config.padding.top + sz,
|
||||||
'text-anchor': anchor,
|
'text-anchor': anchor,
|
||||||
}, config.labelAttrs));
|
}, config.labelAttrs));
|
||||||
labelNode.appendChild(makeText(label));
|
labelNode.appendChild(svg.makeText(label));
|
||||||
this.actions.appendChild(labelNode);
|
this.actions.appendChild(labelNode);
|
||||||
|
|
||||||
const w = labelNode.getComputedTextLength();
|
const w = labelNode.getComputedTextLength();
|
||||||
|
@ -847,54 +815,56 @@ define(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderBlockBegin(scope) {
|
renderBlockBegin(scope) {
|
||||||
this.currentY += BLOCK_MARGIN.top;
|
this.currentY += BLOCK.margin.top;
|
||||||
|
|
||||||
scope.y = this.currentY;
|
scope.y = this.currentY;
|
||||||
scope.first = true;
|
scope.first = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderSectionBegin(scope, {left, right}, {mode, label}) {
|
renderSectionBegin(scope, {left, right}, {mode, label}) {
|
||||||
|
/* jshint -W071 */ // TODO: tidy this up (split text rendering)
|
||||||
|
|
||||||
const agentInfoL = this.agentInfos.get(left);
|
const agentInfoL = this.agentInfos.get(left);
|
||||||
const agentInfoR = this.agentInfos.get(right);
|
const agentInfoR = this.agentInfos.get(right);
|
||||||
|
|
||||||
if(scope.first) {
|
if(scope.first) {
|
||||||
scope.first = false;
|
scope.first = false;
|
||||||
} else {
|
} else {
|
||||||
this.currentY += BLOCK_SECTION_PADDING.bottom;
|
this.currentY += BLOCK.section.padding.bottom;
|
||||||
this.sections.appendChild(makeSVGNode('path', Object.assign({
|
this.sections.appendChild(svg.make('path', Object.assign({
|
||||||
'd': (
|
'd': (
|
||||||
'M' + agentInfoL.x + ' ' + this.currentY +
|
'M' + agentInfoL.x + ' ' + this.currentY +
|
||||||
' L' + agentInfoR.x + ' ' + this.currentY
|
' L' + agentInfoR.x + ' ' + this.currentY
|
||||||
),
|
),
|
||||||
}, ATTRS.BLOCK_SEPARATOR)));
|
}, BLOCK.separator.attrs)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let x = agentInfoL.x;
|
let x = agentInfoL.x;
|
||||||
if(mode) {
|
if(mode) {
|
||||||
const sz = ATTRS.BLOCK_MODE_LABEL['font-size'];
|
const sz = BLOCK.section.mode.labelAttrs['font-size'];
|
||||||
const modeBox = makeSVGNode('rect', Object.assign({
|
const modeBox = svg.make('rect', Object.assign({
|
||||||
'x': x,
|
'x': x,
|
||||||
'y': this.currentY,
|
'y': this.currentY,
|
||||||
'height': (
|
'height': (
|
||||||
sz * LINE_HEIGHT +
|
sz * LINE_HEIGHT +
|
||||||
BLOCK_MODE_PADDING.top +
|
BLOCK.section.mode.padding.top +
|
||||||
BLOCK_MODE_PADDING.bottom
|
BLOCK.section.mode.padding.bottom
|
||||||
),
|
),
|
||||||
}, ATTRS.BLOCK_MODE));
|
}, BLOCK.section.mode.boxAttrs));
|
||||||
const modeLabel = makeSVGNode('text', Object.assign({
|
const modeLabel = svg.make('text', Object.assign({
|
||||||
'x': x + BLOCK_MODE_PADDING.left,
|
'x': x + BLOCK.section.mode.padding.left,
|
||||||
'y': (
|
'y': (
|
||||||
this.currentY + sz +
|
this.currentY + sz +
|
||||||
BLOCK_MODE_PADDING.top
|
BLOCK.section.mode.padding.top
|
||||||
),
|
),
|
||||||
}, ATTRS.BLOCK_MODE_LABEL));
|
}, BLOCK.section.mode.labelAttrs));
|
||||||
modeLabel.appendChild(makeText(mode));
|
modeLabel.appendChild(svg.makeText(mode));
|
||||||
this.blocks.appendChild(modeBox);
|
this.blocks.appendChild(modeBox);
|
||||||
this.actions.appendChild(modeLabel);
|
this.actions.appendChild(modeLabel);
|
||||||
const w = (
|
const w = (
|
||||||
modeLabel.getComputedTextLength() +
|
modeLabel.getComputedTextLength() +
|
||||||
BLOCK_MODE_PADDING.left +
|
BLOCK.section.mode.padding.left +
|
||||||
BLOCK_MODE_PADDING.right
|
BLOCK.section.mode.padding.right
|
||||||
);
|
);
|
||||||
modeBox.setAttribute('width', w);
|
modeBox.setAttribute('width', w);
|
||||||
x += w;
|
x += w;
|
||||||
|
@ -903,47 +873,47 @@ define(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(label) {
|
if(label) {
|
||||||
x += BLOCK_LABEL_PADDING.left;
|
x += BLOCK.section.label.labelPadding.left;
|
||||||
const sz = ATTRS.BLOCK_LABEL['font-size'];
|
const sz = BLOCK.section.label.labelAttrs['font-size'];
|
||||||
const mask = makeSVGNode('rect', Object.assign({
|
const mask = svg.make('rect', Object.assign({
|
||||||
'x': x - BLOCK_LABEL_MASK_PADDING.left,
|
'x': x - BLOCK.section.label.maskPadding.left,
|
||||||
'y': this.currentY - sz * LINE_HEIGHT,
|
'y': this.currentY - sz * LINE_HEIGHT,
|
||||||
'height': sz * LINE_HEIGHT,
|
'height': sz * LINE_HEIGHT,
|
||||||
}, ATTRS.BLOCK_LABEL_MASK));
|
}, BLOCK.section.label.maskAttrs));
|
||||||
const labelLabel = makeSVGNode('text', Object.assign({
|
const labelLabel = svg.make('text', Object.assign({
|
||||||
'x': x,
|
'x': x,
|
||||||
'y': this.currentY - sz * (LINE_HEIGHT - 1),
|
'y': this.currentY - sz * (LINE_HEIGHT - 1),
|
||||||
}, ATTRS.BLOCK_LABEL));
|
}, BLOCK.section.label.labelAttrs));
|
||||||
labelLabel.appendChild(makeText(label));
|
labelLabel.appendChild(svg.makeText(label));
|
||||||
this.actions.appendChild(mask);
|
this.actions.appendChild(mask);
|
||||||
this.actions.appendChild(labelLabel);
|
this.actions.appendChild(labelLabel);
|
||||||
const w = (
|
const w = (
|
||||||
labelLabel.getComputedTextLength() +
|
labelLabel.getComputedTextLength() +
|
||||||
BLOCK_LABEL_MASK_PADDING.left +
|
BLOCK.section.label.maskPadding.left +
|
||||||
BLOCK_LABEL_MASK_PADDING.right
|
BLOCK.section.label.maskPadding.right
|
||||||
);
|
);
|
||||||
mask.setAttribute('width', w);
|
mask.setAttribute('width', w);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.currentY += BLOCK_SECTION_PADDING.top;
|
this.currentY += BLOCK.section.padding.top;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderSectionEnd(/*scope, block, section*/) {
|
renderSectionEnd(/*scope, block, section*/) {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderBlockEnd(scope, {left, right}) {
|
renderBlockEnd(scope, {left, right}) {
|
||||||
this.currentY += BLOCK_SECTION_PADDING.bottom;
|
this.currentY += BLOCK.section.padding.bottom;
|
||||||
|
|
||||||
const agentInfoL = this.agentInfos.get(left);
|
const agentInfoL = this.agentInfos.get(left);
|
||||||
const agentInfoR = this.agentInfos.get(right);
|
const agentInfoR = this.agentInfos.get(right);
|
||||||
this.blocks.appendChild(makeSVGNode('rect', Object.assign({
|
this.blocks.appendChild(svg.make('rect', Object.assign({
|
||||||
'x': agentInfoL.x,
|
'x': agentInfoL.x,
|
||||||
'y': scope.y,
|
'y': scope.y,
|
||||||
'width': agentInfoR.x - agentInfoL.x,
|
'width': agentInfoR.x - agentInfoL.x,
|
||||||
'height': this.currentY - scope.y,
|
'height': this.currentY - scope.y,
|
||||||
}, ATTRS.BLOCK_BOX)));
|
}, BLOCK.boxAttrs)));
|
||||||
|
|
||||||
this.currentY += BLOCK_MARGIN.bottom + ACTION_MARGIN;
|
this.currentY += BLOCK.margin.bottom + ACTION_MARGIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
addAction(stage) {
|
addAction(stage) {
|
||||||
|
@ -953,8 +923,8 @@ define(() => {
|
||||||
testTextWidth(attrs, content) {
|
testTextWidth(attrs, content) {
|
||||||
let tester = this.testersCache.get(attrs);
|
let tester = this.testersCache.get(attrs);
|
||||||
if(!tester) {
|
if(!tester) {
|
||||||
const text = makeText();
|
const text = svg.makeText();
|
||||||
const node = makeSVGNode('text', attrs);
|
const node = svg.make('text', attrs);
|
||||||
node.appendChild(text);
|
node.appendChild(text);
|
||||||
this.testers.appendChild(node);
|
this.testers.appendChild(node);
|
||||||
tester = {text, node};
|
tester = {text, node};
|
||||||
|
@ -966,7 +936,7 @@ define(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
buildAgentInfos(agents, stages) {
|
buildAgentInfos(agents, stages) {
|
||||||
empty(this.testers);
|
svg.empty(this.testers);
|
||||||
this.testersCache.clear();
|
this.testersCache.clear();
|
||||||
this.diagram.appendChild(this.testers);
|
this.diagram.appendChild(this.testers);
|
||||||
|
|
||||||
|
@ -976,7 +946,7 @@ define(() => {
|
||||||
label: agent,
|
label: agent,
|
||||||
labelWidth: (
|
labelWidth: (
|
||||||
this.testTextWidth(ATTRS.AGENT_BOX_LABEL, agent) +
|
this.testTextWidth(ATTRS.AGENT_BOX_LABEL, agent) +
|
||||||
BOX_PADDING * 2
|
AGENT_BOX_PADDING * 2
|
||||||
),
|
),
|
||||||
index,
|
index,
|
||||||
x: null,
|
x: null,
|
||||||
|
@ -1036,11 +1006,11 @@ define(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
render({meta, agents, stages}) {
|
render({meta, agents, stages}) {
|
||||||
empty(this.agentLines);
|
svg.empty(this.agentLines);
|
||||||
empty(this.blocks);
|
svg.empty(this.blocks);
|
||||||
empty(this.sections);
|
svg.empty(this.sections);
|
||||||
empty(this.agentDecor);
|
svg.empty(this.agentDecor);
|
||||||
empty(this.actions);
|
svg.empty(this.actions);
|
||||||
|
|
||||||
this.titleText.nodeValue = meta.title || '';
|
this.titleText.nodeValue = meta.title || '';
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
define(() => {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const NS = 'http://www.w3.org/2000/svg';
|
||||||
|
|
||||||
|
function makeText(text = '') {
|
||||||
|
return document.createTextNode(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
function make(type, attrs = {}) {
|
||||||
|
const o = document.createElementNS(NS, type);
|
||||||
|
for(let k in attrs) {
|
||||||
|
if(attrs.hasOwnProperty(k)) {
|
||||||
|
o.setAttribute(k, attrs[k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeContainer(attrs = {}) {
|
||||||
|
return make('svg', Object.assign({
|
||||||
|
'xmlns': NS,
|
||||||
|
'version': '1.1',
|
||||||
|
}, attrs));
|
||||||
|
}
|
||||||
|
|
||||||
|
function empty(node) {
|
||||||
|
while(node.childNodes.length > 0) {
|
||||||
|
node.removeChild(node.lastChild);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
makeText,
|
||||||
|
make,
|
||||||
|
makeContainer,
|
||||||
|
empty,
|
||||||
|
};
|
||||||
|
});
|
|
@ -0,0 +1,58 @@
|
||||||
|
defineDescribe('SVGUtilities', ['./SVGUtilities'], (svg) => {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const expectedNS = 'http://www.w3.org/2000/svg';
|
||||||
|
|
||||||
|
describe('.makeText', () => {
|
||||||
|
it('creates a text node with the given content', () => {
|
||||||
|
const node = svg.makeText('foo');
|
||||||
|
expect(node.nodeValue).toEqual('foo');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('defaults to empty', () => {
|
||||||
|
const node = svg.makeText();
|
||||||
|
expect(node.nodeValue).toEqual('');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('.make', () => {
|
||||||
|
it('creates a node with the SVG namespace', () => {
|
||||||
|
const node = svg.make('path');
|
||||||
|
expect(node.namespaceURI).toEqual(expectedNS);
|
||||||
|
expect(node.tagName).toEqual('path');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('assigns the given attributes', () => {
|
||||||
|
const node = svg.make('path', {'foo': 'bar'});
|
||||||
|
expect(node.getAttribute('foo')).toEqual('bar');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('.makeContainer', () => {
|
||||||
|
it('creates an svg node with the SVG namespace', () => {
|
||||||
|
const node = svg.makeContainer();
|
||||||
|
expect(node.namespaceURI).toEqual(expectedNS);
|
||||||
|
expect(node.getAttribute('xmlns')).toEqual(expectedNS);
|
||||||
|
expect(node.getAttribute('version')).toEqual('1.1');
|
||||||
|
expect(node.tagName).toEqual('svg');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('assigns the given attributes', () => {
|
||||||
|
const node = svg.makeContainer({'foo': 'bar'});
|
||||||
|
expect(node.getAttribute('foo')).toEqual('bar');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('.empty', () => {
|
||||||
|
it('removes all child nodes from the given node', () => {
|
||||||
|
const node = document.createElement('p');
|
||||||
|
const a = document.createElement('p');
|
||||||
|
const b = document.createElement('p');
|
||||||
|
node.appendChild(a);
|
||||||
|
node.appendChild(b);
|
||||||
|
|
||||||
|
svg.empty(node);
|
||||||
|
expect(node.children.length).toEqual(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -3,4 +3,6 @@ define([
|
||||||
'sequence/Parser_spec',
|
'sequence/Parser_spec',
|
||||||
'sequence/Generator_spec',
|
'sequence/Generator_spec',
|
||||||
'sequence/Renderer_spec',
|
'sequence/Renderer_spec',
|
||||||
|
'sequence/ArrayUtilities_spec',
|
||||||
|
'sequence/SVGUtilities_spec',
|
||||||
]);
|
]);
|
||||||
|
|
Loading…
Reference in New Issue