Add initial support for asynchronous action blocks [#12]
This commit is contained in:
parent
4772b16783
commit
912c9dbb64
33
README.md
33
README.md
|
@ -150,6 +150,39 @@ Foo -> Bar
|
||||||
Bar -> Baz
|
Bar -> Baz
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Simultaneous Actions (Beta!)
|
||||||
|
|
||||||
|
<img src="screenshots/SimultaneousActions.png" alt="Simultaneous Actions preview" width="200" align="right" />
|
||||||
|
|
||||||
|
This is a work-in-progress feature. There are situations where this can
|
||||||
|
lead to [ugly / unreadable overlapping content](https://github.com/davidje13/SequenceDiagram/issues/13).
|
||||||
|
|
||||||
|
```
|
||||||
|
begin A, B, C, D
|
||||||
|
A -> C
|
||||||
|
|
||||||
|
# Define a marker which can be returned to later
|
||||||
|
|
||||||
|
some primary process:
|
||||||
|
A -> B
|
||||||
|
B -> A
|
||||||
|
A -> B
|
||||||
|
B -> A
|
||||||
|
|
||||||
|
# Return to the defined marker
|
||||||
|
# (should be interpreted as no-higher-then the marker; may still be
|
||||||
|
# pushed down to keep relative action ordering consistent)
|
||||||
|
|
||||||
|
simultaneously with some primary process:
|
||||||
|
C -> D
|
||||||
|
D -> C
|
||||||
|
end D
|
||||||
|
C -> A
|
||||||
|
|
||||||
|
# The marker name is optional; using "simultaneously:" with no marker
|
||||||
|
# will jump to the top of the entire sequence.
|
||||||
|
```
|
||||||
|
|
||||||
## DSL Basics
|
## DSL Basics
|
||||||
|
|
||||||
Comments begin with a `#` and end at the next newline:
|
Comments begin with a `#` and end at the next newline:
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 5.5 KiB |
|
@ -17,10 +17,13 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
this.agents = [];
|
this.agents = [];
|
||||||
this.blockCount = 0;
|
this.blockCount = 0;
|
||||||
this.nesting = [];
|
this.nesting = [];
|
||||||
|
this.markers = new Set();
|
||||||
this.currentSection = null;
|
this.currentSection = null;
|
||||||
this.currentNest = null;
|
this.currentNest = null;
|
||||||
|
|
||||||
this.stageHandlers = {
|
this.stageHandlers = {
|
||||||
|
'mark': this.handleMark.bind(this),
|
||||||
|
'async': this.handleAsync.bind(this),
|
||||||
'agent define': this.handleAgentDefine.bind(this),
|
'agent define': this.handleAgentDefine.bind(this),
|
||||||
'agent begin': this.handleAgentBegin.bind(this),
|
'agent begin': this.handleAgentBegin.bind(this),
|
||||||
'agent end': this.handleAgentEnd.bind(this),
|
'agent end': this.handleAgentEnd.bind(this),
|
||||||
|
@ -83,6 +86,7 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
agents: filteredAgents,
|
agents: filteredAgents,
|
||||||
mode,
|
mode,
|
||||||
});
|
});
|
||||||
|
this.currentNest.hasContent = true;
|
||||||
}
|
}
|
||||||
array.mergeSets(this.currentNest.agents, filteredAgents);
|
array.mergeSets(this.currentNest.agents, filteredAgents);
|
||||||
array.mergeSets(this.agents, filteredAgents);
|
array.mergeSets(this.agents, filteredAgents);
|
||||||
|
@ -100,6 +104,7 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
};
|
};
|
||||||
this.currentNest = {
|
this.currentNest = {
|
||||||
agents,
|
agents,
|
||||||
|
hasContent: false,
|
||||||
stage: {
|
stage: {
|
||||||
type: 'block',
|
type: 'block',
|
||||||
sections: [this.currentSection],
|
sections: [this.currentSection],
|
||||||
|
@ -114,6 +119,18 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
return {agents, stages};
|
return {agents, stages};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleMark(stage) {
|
||||||
|
this.markers.add(stage.name);
|
||||||
|
this.currentSection.stages.push(stage);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleAsync(stage) {
|
||||||
|
if(stage.target !== '' && !this.markers.has(stage.target)) {
|
||||||
|
throw new Error('Unknown marker: ' + stage.target);
|
||||||
|
}
|
||||||
|
this.currentSection.stages.push(stage);
|
||||||
|
}
|
||||||
|
|
||||||
handleAgentDefine({agents}) {
|
handleAgentDefine({agents}) {
|
||||||
array.mergeSets(this.currentNest.agents, agents);
|
array.mergeSets(this.currentNest.agents, agents);
|
||||||
array.mergeSets(this.agents, agents);
|
array.mergeSets(this.agents, agents);
|
||||||
|
@ -149,10 +166,10 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
if(this.nesting.length <= 1) {
|
if(this.nesting.length <= 1) {
|
||||||
throw new Error('Invalid block nesting');
|
throw new Error('Invalid block nesting');
|
||||||
}
|
}
|
||||||
const {stage, agents} = this.nesting.pop();
|
const {hasContent, stage, agents} = this.nesting.pop();
|
||||||
this.currentNest = array.last(this.nesting);
|
this.currentNest = array.last(this.nesting);
|
||||||
this.currentSection = array.last(this.currentNest.stage.sections);
|
this.currentSection = array.last(this.currentNest.stage.sections);
|
||||||
if(stage.sections.some((section) => section.stages.length > 0)) {
|
if(hasContent) {
|
||||||
array.mergeSets(this.currentNest.agents, agents);
|
array.mergeSets(this.currentNest.agents, agents);
|
||||||
array.mergeSets(this.agents, agents);
|
array.mergeSets(this.agents, agents);
|
||||||
this.addBounds(
|
this.addBounds(
|
||||||
|
@ -162,14 +179,18 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
agents
|
agents
|
||||||
);
|
);
|
||||||
this.currentSection.stages.push(stage);
|
this.currentSection.stages.push(stage);
|
||||||
|
this.currentNest.hasContent = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUnknownStage(stage) {
|
handleUnknownStage(stage) {
|
||||||
this.setAgentVis(stage.agents, true, 'box');
|
if(stage.agents) {
|
||||||
|
this.setAgentVis(stage.agents, true, 'box');
|
||||||
|
array.mergeSets(this.currentNest.agents, stage.agents);
|
||||||
|
array.mergeSets(this.agents, stage.agents);
|
||||||
|
}
|
||||||
this.currentSection.stages.push(stage);
|
this.currentSection.stages.push(stage);
|
||||||
array.mergeSets(this.currentNest.agents, stage.agents);
|
this.currentNest.hasContent = true;
|
||||||
array.mergeSets(this.agents, stage.agents);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleStage(stage) {
|
handleStage(stage) {
|
||||||
|
@ -183,6 +204,7 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
|
|
||||||
generate({stages, meta = {}}) {
|
generate({stages, meta = {}}) {
|
||||||
this.agentStates.clear();
|
this.agentStates.clear();
|
||||||
|
this.markers.clear();
|
||||||
this.agents.length = 0;
|
this.agents.length = 0;
|
||||||
this.blockCount = 0;
|
this.blockCount = 0;
|
||||||
this.nesting.length = 0;
|
this.nesting.length = 0;
|
||||||
|
|
|
@ -31,6 +31,26 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
expect(sequence.agents).toEqual(['[', ']']);
|
expect(sequence.agents).toEqual(['[', ']']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('passes marks and async through', () => {
|
||||||
|
const sequence = generator.generate({stages: [
|
||||||
|
{type: 'mark', name: 'foo'},
|
||||||
|
{type: 'async', target: 'foo'},
|
||||||
|
{type: 'async', target: ''},
|
||||||
|
]});
|
||||||
|
expect(sequence.stages).toEqual([
|
||||||
|
{type: 'mark', name: 'foo'},
|
||||||
|
{type: 'async', target: 'foo'},
|
||||||
|
{type: 'async', target: ''},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects attempts to jump to markers not yet defined', () => {
|
||||||
|
expect(() => generator.generate({stages: [
|
||||||
|
{type: 'async', target: 'foo'},
|
||||||
|
{type: 'mark', name: 'foo'},
|
||||||
|
]})).toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
it('returns aggregated agents', () => {
|
it('returns aggregated agents', () => {
|
||||||
const sequence = generator.generate({stages: [
|
const sequence = generator.generate({stages: [
|
||||||
{type: '->', agents: ['A', 'B']},
|
{type: '->', agents: ['A', 'B']},
|
||||||
|
@ -369,10 +389,11 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
expect(sequence.stages).toEqual([]);
|
expect(sequence.stages).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('removes blocks which only contain define statements', () => {
|
it('removes blocks containing only define statements / markers', () => {
|
||||||
const sequence = generator.generate({stages: [
|
const sequence = generator.generate({stages: [
|
||||||
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
||||||
{type: AGENT_DEFINE, agents: ['A']},
|
{type: AGENT_DEFINE, agents: ['A']},
|
||||||
|
{type: 'mark', name: 'foo'},
|
||||||
{type: BLOCK_END},
|
{type: BLOCK_END},
|
||||||
]});
|
]});
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(() => {
|
define(['core/ArrayUtilities'], (array) => {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function execAt(str, reg, i) {
|
function execAt(str, reg, i) {
|
||||||
|
@ -160,124 +160,162 @@ define(() => {
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseBlockCommand(line) {
|
const PARSERS = [
|
||||||
if(line[0] === 'end' && line.length === 1) {
|
(line, meta) => { // title
|
||||||
return {type: 'block end'};
|
if(line[0] !== 'title') {
|
||||||
}
|
return null;
|
||||||
|
|
||||||
const type = BLOCK_TYPES[line[0]];
|
|
||||||
if(!type) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
let skip = 1;
|
|
||||||
if(line.length > skip) {
|
|
||||||
skip = skipOver(line, skip, type.skip, 'Invalid block command');
|
|
||||||
}
|
|
||||||
skip = skipOver(line, skip, [':']);
|
|
||||||
return {
|
|
||||||
type: type.type,
|
|
||||||
mode: type.mode,
|
|
||||||
label: line.slice(skip).join(' '),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseAgentCommand(line) {
|
|
||||||
const type = AGENT_MANIPULATION_TYPES[line[0]];
|
|
||||||
if(!type) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if(line.length <= 1) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return Object.assign({
|
|
||||||
agents: parseCommaList(line.slice(1)),
|
|
||||||
}, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseNote(line) {
|
|
||||||
const mode = NOTE_TYPES[line[0]];
|
|
||||||
const labelSplit = line.indexOf(':');
|
|
||||||
if(!mode || labelSplit === -1) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const type = mode.types[line[1]];
|
|
||||||
if(!type) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
let skip = 2;
|
|
||||||
skip = skipOver(line, skip, type.skip);
|
|
||||||
const agents = parseCommaList(line.slice(skip, labelSplit));
|
|
||||||
if(
|
|
||||||
agents.length < type.min ||
|
|
||||||
(type.max !== null && agents.length > type.max)
|
|
||||||
) {
|
|
||||||
throw new Error('Invalid ' + line[0] + ': ' + line.join(' '));
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
type: type.type,
|
|
||||||
agents,
|
|
||||||
mode: mode.mode,
|
|
||||||
label: line.slice(labelSplit + 1).join(' '),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseConnection(line) {
|
|
||||||
let labelSplit = line.indexOf(':');
|
|
||||||
if(labelSplit === -1) {
|
|
||||||
labelSplit = line.length;
|
|
||||||
}
|
|
||||||
let typeSplit = -1;
|
|
||||||
let options = null;
|
|
||||||
for(let j = 0; j < line.length; ++ j) {
|
|
||||||
const opts = CONNECTION_TYPES[line[j]];
|
|
||||||
if(opts) {
|
|
||||||
typeSplit = j;
|
|
||||||
options = opts;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if(typeSplit <= 0 || typeSplit >= labelSplit - 1) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return Object.assign({
|
|
||||||
type: 'connection',
|
|
||||||
agents: [
|
|
||||||
line.slice(0, typeSplit).join(' '),
|
|
||||||
line.slice(typeSplit + 1, labelSplit).join(' '),
|
|
||||||
],
|
|
||||||
label: line.slice(labelSplit + 1).join(' '),
|
|
||||||
}, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseMeta(line, meta) {
|
|
||||||
if(line[0] === 'title') {
|
|
||||||
meta.title = line.slice(1).join(' ');
|
meta.title = line.slice(1).join(' ');
|
||||||
return true;
|
return true;
|
||||||
}
|
},
|
||||||
if(line[0] === 'terminators') {
|
|
||||||
|
(line, meta) => { // terminators
|
||||||
|
if(line[0] !== 'terminators') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if(TERMINATOR_TYPES.indexOf(line[1]) === -1) {
|
if(TERMINATOR_TYPES.indexOf(line[1]) === -1) {
|
||||||
throw new Error('Unrecognised termination: ' + line.join(' '));
|
throw new Error('Unknown termination: ' + line.join(' '));
|
||||||
}
|
}
|
||||||
meta.terminators = line[1];
|
meta.terminators = line[1];
|
||||||
return true;
|
return true;
|
||||||
}
|
},
|
||||||
return false;
|
|
||||||
}
|
(line) => { // block
|
||||||
|
if(line[0] === 'end' && line.length === 1) {
|
||||||
|
return {type: 'block end'};
|
||||||
|
}
|
||||||
|
|
||||||
|
const type = BLOCK_TYPES[line[0]];
|
||||||
|
if(!type) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let skip = 1;
|
||||||
|
if(line.length > skip) {
|
||||||
|
skip = skipOver(line, skip, type.skip, 'Invalid block command');
|
||||||
|
}
|
||||||
|
skip = skipOver(line, skip, [':']);
|
||||||
|
return {
|
||||||
|
type: type.type,
|
||||||
|
mode: type.mode,
|
||||||
|
label: line.slice(skip).join(' '),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
(line) => { // agent
|
||||||
|
const type = AGENT_MANIPULATION_TYPES[line[0]];
|
||||||
|
if(!type) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if(line.length <= 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return Object.assign({
|
||||||
|
agents: parseCommaList(line.slice(1)),
|
||||||
|
}, type);
|
||||||
|
},
|
||||||
|
|
||||||
|
(line) => { // async
|
||||||
|
if(line[0] !== 'simultaneously') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if(array.last(line) !== ':') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let target = '';
|
||||||
|
if(line.length > 2) {
|
||||||
|
if(line[1] !== 'with') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
target = line.slice(2, line.length - 1).join(' ');
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type: 'async',
|
||||||
|
target,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
(line) => { // note
|
||||||
|
const mode = NOTE_TYPES[line[0]];
|
||||||
|
const labelSplit = line.indexOf(':');
|
||||||
|
if(!mode || labelSplit === -1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const type = mode.types[line[1]];
|
||||||
|
if(!type) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let skip = 2;
|
||||||
|
skip = skipOver(line, skip, type.skip);
|
||||||
|
const agents = parseCommaList(line.slice(skip, labelSplit));
|
||||||
|
if(
|
||||||
|
agents.length < type.min ||
|
||||||
|
(type.max !== null && agents.length > type.max)
|
||||||
|
) {
|
||||||
|
throw new Error('Invalid ' + line[0] + ': ' + line.join(' '));
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type: type.type,
|
||||||
|
agents,
|
||||||
|
mode: mode.mode,
|
||||||
|
label: line.slice(labelSplit + 1).join(' '),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
(line) => { // connection
|
||||||
|
let labelSplit = line.indexOf(':');
|
||||||
|
if(labelSplit === -1) {
|
||||||
|
labelSplit = line.length;
|
||||||
|
}
|
||||||
|
let typeSplit = -1;
|
||||||
|
let options = null;
|
||||||
|
for(let j = 0; j < line.length; ++ j) {
|
||||||
|
const opts = CONNECTION_TYPES[line[j]];
|
||||||
|
if(opts) {
|
||||||
|
typeSplit = j;
|
||||||
|
options = opts;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(typeSplit <= 0 || typeSplit >= labelSplit - 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return Object.assign({
|
||||||
|
type: 'connection',
|
||||||
|
agents: [
|
||||||
|
line.slice(0, typeSplit).join(' '),
|
||||||
|
line.slice(typeSplit + 1, labelSplit).join(' '),
|
||||||
|
],
|
||||||
|
label: line.slice(labelSplit + 1).join(' '),
|
||||||
|
}, options);
|
||||||
|
},
|
||||||
|
|
||||||
|
(line) => { // marker
|
||||||
|
if(line.length < 2 || array.last(line) !== ':') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type: 'mark',
|
||||||
|
name: line.slice(0, line.length - 1).join(' '),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
function parseLine(line, {meta, stages}) {
|
function parseLine(line, {meta, stages}) {
|
||||||
if(parseMeta(line, meta)) {
|
let stage = null;
|
||||||
return;
|
for(let i = 0; i < PARSERS.length; ++ i) {
|
||||||
|
stage = PARSERS[i](line, meta);
|
||||||
|
if(stage) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const stage = (
|
|
||||||
parseBlockCommand(line) ||
|
|
||||||
parseAgentCommand(line) ||
|
|
||||||
parseNote(line) ||
|
|
||||||
parseConnection(line)
|
|
||||||
);
|
|
||||||
if(!stage) {
|
if(!stage) {
|
||||||
throw new Error('Unrecognised command: ' + line.join(' '));
|
throw new Error('Unrecognised command: ' + line.join(' '));
|
||||||
}
|
}
|
||||||
stages.push(stage);
|
if(typeof stage === 'object') {
|
||||||
|
stages.push(stage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return class Parser {
|
return class Parser {
|
||||||
|
|
|
@ -355,6 +355,30 @@ defineDescribe('Sequence Parser', ['./Parser'], (Parser) => {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('converts markers', () => {
|
||||||
|
const parsed = parser.parse('abc:');
|
||||||
|
expect(parsed.stages).toEqual([{
|
||||||
|
type: 'mark',
|
||||||
|
name: 'abc',
|
||||||
|
}]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('converts "simultaneously" flow commands', () => {
|
||||||
|
const parsed = parser.parse('simultaneously:');
|
||||||
|
expect(parsed.stages).toEqual([{
|
||||||
|
type: 'async',
|
||||||
|
target: '',
|
||||||
|
}]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('converts named "simultaneously" flow commands', () => {
|
||||||
|
const parsed = parser.parse('simultaneously with abc:');
|
||||||
|
expect(parsed.stages).toEqual([{
|
||||||
|
type: 'async',
|
||||||
|
target: 'abc',
|
||||||
|
}]);
|
||||||
|
});
|
||||||
|
|
||||||
it('converts conditional blocks', () => {
|
it('converts conditional blocks', () => {
|
||||||
const parsed = parser.parse(
|
const parsed = parser.parse(
|
||||||
'if something happens\n' +
|
'if something happens\n' +
|
||||||
|
|
|
@ -61,12 +61,14 @@ define([
|
||||||
};
|
};
|
||||||
|
|
||||||
this.separationAction = {
|
this.separationAction = {
|
||||||
|
'mark': this.separationMark.bind(this),
|
||||||
|
'async': this.separationAsync.bind(this),
|
||||||
'agent begin': this.separationAgent.bind(this),
|
'agent begin': this.separationAgent.bind(this),
|
||||||
'agent end': this.separationAgent.bind(this),
|
'agent end': this.separationAgent.bind(this),
|
||||||
'connection': this.separationConnection.bind(this),
|
'connection': this.separationConnection.bind(this),
|
||||||
'note over': this.separationNoteOver.bind(this),
|
'note over': this.separationNoteOver.bind(this),
|
||||||
'note left': this.separationNoteLeft.bind(this),
|
'note left': this.separationNoteSide.bind(this, false),
|
||||||
'note right': this.separationNoteRight.bind(this),
|
'note right': this.separationNoteSide.bind(this, true),
|
||||||
'note between': this.separationNoteBetween.bind(this),
|
'note between': this.separationNoteBetween.bind(this),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -78,6 +80,8 @@ define([
|
||||||
};
|
};
|
||||||
|
|
||||||
this.renderAction = {
|
this.renderAction = {
|
||||||
|
'mark': this.renderMark.bind(this),
|
||||||
|
'async': this.renderAsync.bind(this),
|
||||||
'agent begin': this.renderAgentBegin.bind(this),
|
'agent begin': this.renderAgentBegin.bind(this),
|
||||||
'agent end': this.renderAgentEnd.bind(this),
|
'agent end': this.renderAgentEnd.bind(this),
|
||||||
'connection': this.renderConnection.bind(this),
|
'connection': this.renderConnection.bind(this),
|
||||||
|
@ -104,6 +108,7 @@ define([
|
||||||
|
|
||||||
this.width = 0;
|
this.width = 0;
|
||||||
this.height = 0;
|
this.height = 0;
|
||||||
|
this.marks = new Map();
|
||||||
this.theme = theme;
|
this.theme = theme;
|
||||||
this.currentSequence = null;
|
this.currentSequence = null;
|
||||||
this.buildStaticElements();
|
this.buildStaticElements();
|
||||||
|
@ -180,6 +185,12 @@ define([
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
separationMark() {
|
||||||
|
}
|
||||||
|
|
||||||
|
separationAsync() {
|
||||||
|
}
|
||||||
|
|
||||||
separationAgentCapBox({label}) {
|
separationAgentCapBox({label}) {
|
||||||
const config = this.theme.agentCap.box;
|
const config = this.theme.agentCap.box;
|
||||||
const width = (
|
const width = (
|
||||||
|
@ -300,39 +311,23 @@ define([
|
||||||
this.addSeparations(this.visibleAgents, agentSpaces);
|
this.addSeparations(this.visibleAgents, agentSpaces);
|
||||||
}
|
}
|
||||||
|
|
||||||
separationNoteLeft({agents, mode, label}) {
|
separationNoteSide(isRight, {agents, mode, label}) {
|
||||||
const config = this.theme.note[mode];
|
const config = this.theme.note[mode];
|
||||||
const {left} = this.findExtremes(agents);
|
const {left, right} = this.findExtremes(agents);
|
||||||
|
const width = (
|
||||||
|
this.sizer.measure(config.labelAttrs, label).width +
|
||||||
|
config.padding.left +
|
||||||
|
config.padding.right +
|
||||||
|
config.margin.left +
|
||||||
|
config.margin.right
|
||||||
|
);
|
||||||
|
|
||||||
const agentSpaces = new Map();
|
const agentSpaces = new Map();
|
||||||
agentSpaces.set(left, {
|
if(isRight) {
|
||||||
left: (
|
agentSpaces.set(right, {left: 0, right: width});
|
||||||
this.sizer.measure(config.labelAttrs, label).width +
|
} else {
|
||||||
config.padding.left +
|
agentSpaces.set(left, {left: width, right: 0});
|
||||||
config.padding.right +
|
}
|
||||||
config.margin.left +
|
|
||||||
config.margin.right
|
|
||||||
),
|
|
||||||
right: 0,
|
|
||||||
});
|
|
||||||
this.addSeparations(this.visibleAgents, agentSpaces);
|
|
||||||
}
|
|
||||||
|
|
||||||
separationNoteRight({agents, mode, label}) {
|
|
||||||
const config = this.theme.note[mode];
|
|
||||||
const {right} = this.findExtremes(agents);
|
|
||||||
|
|
||||||
const agentSpaces = new Map();
|
|
||||||
agentSpaces.set(right, {
|
|
||||||
left: 0,
|
|
||||||
right: (
|
|
||||||
this.sizer.measure(config.labelAttrs, label).width +
|
|
||||||
config.padding.left +
|
|
||||||
config.padding.right +
|
|
||||||
config.margin.left +
|
|
||||||
config.margin.right
|
|
||||||
),
|
|
||||||
});
|
|
||||||
this.addSeparations(this.visibleAgents, agentSpaces);
|
this.addSeparations(this.visibleAgents, agentSpaces);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -378,6 +373,18 @@ define([
|
||||||
this.separationAction[stage.type](stage);
|
this.separationAction[stage.type](stage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderMark({name}) {
|
||||||
|
this.marks.set(name, this.currentY);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderAsync({target}) {
|
||||||
|
if(target) {
|
||||||
|
this.currentY = this.marks.get(target) || 0;
|
||||||
|
} else {
|
||||||
|
this.currentY = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
renderAgentCapBox({x, label}) {
|
renderAgentCapBox({x, label}) {
|
||||||
const config = this.theme.agentCap.box;
|
const config = this.theme.agentCap.box;
|
||||||
const {height} = SVGShapes.renderBoxedText(label, {
|
const {height} = SVGShapes.renderBoxedText(label, {
|
||||||
|
@ -449,7 +456,30 @@ define([
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkAgentRange(agents) {
|
||||||
|
const {left, right} = this.findExtremes(agents);
|
||||||
|
const leftX = this.agentInfos.get(left).x;
|
||||||
|
const rightX = this.agentInfos.get(right).x;
|
||||||
|
this.agentInfos.forEach((agentInfo) => {
|
||||||
|
if(agentInfo.x >= leftX && agentInfo.x <= rightX) {
|
||||||
|
this.currentY = Math.max(this.currentY, agentInfo.latestY);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
markAgentRange(agents) {
|
||||||
|
const {left, right} = this.findExtremes(agents);
|
||||||
|
const leftX = this.agentInfos.get(left).x;
|
||||||
|
const rightX = this.agentInfos.get(right).x;
|
||||||
|
this.agentInfos.forEach((agentInfo) => {
|
||||||
|
if(agentInfo.x >= leftX && agentInfo.x <= rightX) {
|
||||||
|
agentInfo.latestY = this.currentY;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
renderAgentBegin({mode, agents}) {
|
renderAgentBegin({mode, agents}) {
|
||||||
|
this.checkAgentRange(agents);
|
||||||
let maxHeight = 0;
|
let maxHeight = 0;
|
||||||
agents.forEach((agent) => {
|
agents.forEach((agent) => {
|
||||||
const agentInfo = this.agentInfos.get(agent);
|
const agentInfo = this.agentInfos.get(agent);
|
||||||
|
@ -458,9 +488,11 @@ define([
|
||||||
agentInfo.latestYStart = this.currentY + shifts.lineBottom;
|
agentInfo.latestYStart = this.currentY + shifts.lineBottom;
|
||||||
});
|
});
|
||||||
this.currentY += maxHeight + this.theme.actionMargin;
|
this.currentY += maxHeight + this.theme.actionMargin;
|
||||||
|
this.markAgentRange(agents);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderAgentEnd({mode, agents}) {
|
renderAgentEnd({mode, agents}) {
|
||||||
|
this.checkAgentRange(agents);
|
||||||
let maxHeight = 0;
|
let maxHeight = 0;
|
||||||
agents.forEach((agent) => {
|
agents.forEach((agent) => {
|
||||||
const agentInfo = this.agentInfos.get(agent);
|
const agentInfo = this.agentInfos.get(agent);
|
||||||
|
@ -477,6 +509,7 @@ define([
|
||||||
agentInfo.latestYStart = null;
|
agentInfo.latestYStart = null;
|
||||||
});
|
});
|
||||||
this.currentY += maxHeight + this.theme.actionMargin;
|
this.currentY += maxHeight + this.theme.actionMargin;
|
||||||
|
this.markAgentRange(agents);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderSelfConnection({label, agents, line, left, right}) {
|
renderSelfConnection({label, agents, line, left, right}) {
|
||||||
|
@ -609,11 +642,13 @@ define([
|
||||||
}
|
}
|
||||||
|
|
||||||
renderConnection(stage) {
|
renderConnection(stage) {
|
||||||
|
this.checkAgentRange(stage.agents);
|
||||||
if(stage.agents[0] === stage.agents[1]) {
|
if(stage.agents[0] === stage.agents[1]) {
|
||||||
this.renderSelfConnection(stage);
|
this.renderSelfConnection(stage);
|
||||||
} else {
|
} else {
|
||||||
this.renderSimpleConnection(stage);
|
this.renderSimpleConnection(stage);
|
||||||
}
|
}
|
||||||
|
this.markAgentRange(stage.agents);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderNote({xMid = null, x0 = null, x1 = null}, anchor, mode, label) {
|
renderNote({xMid = null, x0 = null, x1 = null}, anchor, mode, label) {
|
||||||
|
@ -679,6 +714,7 @@ define([
|
||||||
}
|
}
|
||||||
|
|
||||||
renderNoteOver({agents, mode, label}) {
|
renderNoteOver({agents, mode, label}) {
|
||||||
|
this.checkAgentRange(agents);
|
||||||
const config = this.theme.note[mode];
|
const config = this.theme.note[mode];
|
||||||
|
|
||||||
if(agents.length > 1) {
|
if(agents.length > 1) {
|
||||||
|
@ -691,25 +727,31 @@ define([
|
||||||
const xMid = this.agentInfos.get(agents[0]).x;
|
const xMid = this.agentInfos.get(agents[0]).x;
|
||||||
this.renderNote({xMid}, 'middle', mode, label);
|
this.renderNote({xMid}, 'middle', mode, label);
|
||||||
}
|
}
|
||||||
|
this.markAgentRange(agents);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderNoteLeft({agents, mode, label}) {
|
renderNoteLeft({agents, mode, label}) {
|
||||||
|
this.checkAgentRange(agents);
|
||||||
const config = this.theme.note[mode];
|
const config = this.theme.note[mode];
|
||||||
|
|
||||||
const {left} = this.findExtremes(agents);
|
const {left} = this.findExtremes(agents);
|
||||||
const x1 = this.agentInfos.get(left).x - config.margin.right;
|
const x1 = this.agentInfos.get(left).x - config.margin.right;
|
||||||
this.renderNote({x1}, 'end', mode, label);
|
this.renderNote({x1}, 'end', mode, label);
|
||||||
|
this.markAgentRange(agents);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderNoteRight({agents, mode, label}) {
|
renderNoteRight({agents, mode, label}) {
|
||||||
|
this.checkAgentRange(agents);
|
||||||
const config = this.theme.note[mode];
|
const config = this.theme.note[mode];
|
||||||
|
|
||||||
const {right} = this.findExtremes(agents);
|
const {right} = this.findExtremes(agents);
|
||||||
const x0 = this.agentInfos.get(right).x + config.margin.left;
|
const x0 = this.agentInfos.get(right).x + config.margin.left;
|
||||||
this.renderNote({x0}, 'start', mode, label);
|
this.renderNote({x0}, 'start', mode, label);
|
||||||
|
this.markAgentRange(agents);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderNoteBetween({agents, mode, label}) {
|
renderNoteBetween({agents, mode, label}) {
|
||||||
|
this.checkAgentRange(agents);
|
||||||
const {left, right} = this.findExtremes(agents);
|
const {left, right} = this.findExtremes(agents);
|
||||||
const xMid = (
|
const xMid = (
|
||||||
this.agentInfos.get(left).x +
|
this.agentInfos.get(left).x +
|
||||||
|
@ -717,16 +759,20 @@ define([
|
||||||
) / 2;
|
) / 2;
|
||||||
|
|
||||||
this.renderNote({xMid}, 'middle', mode, label);
|
this.renderNote({xMid}, 'middle', mode, label);
|
||||||
|
this.markAgentRange(agents);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderBlockBegin(scope) {
|
renderBlockBegin(scope, {left, right}) {
|
||||||
|
this.checkAgentRange([left, right]);
|
||||||
this.currentY += this.theme.block.margin.top;
|
this.currentY += this.theme.block.margin.top;
|
||||||
|
|
||||||
scope.y = this.currentY;
|
scope.y = this.currentY;
|
||||||
scope.first = true;
|
scope.first = true;
|
||||||
|
this.markAgentRange([left, right]);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderSectionBegin(scope, {left, right}, {mode, label}) {
|
renderSectionBegin(scope, {left, right}, {mode, label}) {
|
||||||
|
this.checkAgentRange([left, right]);
|
||||||
const config = this.theme.block;
|
const config = this.theme.block;
|
||||||
const agentInfoL = this.agentInfos.get(left);
|
const agentInfoL = this.agentInfos.get(left);
|
||||||
const agentInfoR = this.agentInfos.get(right);
|
const agentInfoR = this.agentInfos.get(right);
|
||||||
|
@ -767,12 +813,14 @@ define([
|
||||||
Math.max(modeRender.height, labelRender.height) +
|
Math.max(modeRender.height, labelRender.height) +
|
||||||
config.section.padding.top
|
config.section.padding.top
|
||||||
);
|
);
|
||||||
|
this.markAgentRange([left, right]);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderSectionEnd(/*scope, block, section*/) {
|
renderSectionEnd(/*scope, block, section*/) {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderBlockEnd(scope, {left, right}) {
|
renderBlockEnd(scope, {left, right}) {
|
||||||
|
this.checkAgentRange([left, right]);
|
||||||
const config = this.theme.block;
|
const config = this.theme.block;
|
||||||
this.currentY += config.section.padding.bottom;
|
this.currentY += config.section.padding.bottom;
|
||||||
|
|
||||||
|
@ -786,6 +834,7 @@ define([
|
||||||
}, config.boxAttrs)));
|
}, config.boxAttrs)));
|
||||||
|
|
||||||
this.currentY += config.margin.bottom + this.theme.actionMargin;
|
this.currentY += config.margin.bottom + this.theme.actionMargin;
|
||||||
|
this.markAgentRange([left, right]);
|
||||||
}
|
}
|
||||||
|
|
||||||
addAction(stage) {
|
addAction(stage) {
|
||||||
|
@ -835,6 +884,7 @@ define([
|
||||||
index,
|
index,
|
||||||
x: null,
|
x: null,
|
||||||
latestYStart: null,
|
latestYStart: null,
|
||||||
|
latestY: 0,
|
||||||
separations: new Map(),
|
separations: new Map(),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -884,6 +934,7 @@ define([
|
||||||
svg.empty(this.sections);
|
svg.empty(this.sections);
|
||||||
svg.empty(this.actionShapes);
|
svg.empty(this.actionShapes);
|
||||||
svg.empty(this.actionLabels);
|
svg.empty(this.actionLabels);
|
||||||
|
this.marks.clear();
|
||||||
|
|
||||||
this.title.set({
|
this.title.set({
|
||||||
attrs: this.theme.titleAttrs,
|
attrs: this.theme.titleAttrs,
|
||||||
|
@ -896,6 +947,7 @@ define([
|
||||||
|
|
||||||
this.currentY = 0;
|
this.currentY = 0;
|
||||||
traverse(sequence.stages, this.renderTraversalFns);
|
traverse(sequence.stages, this.renderTraversalFns);
|
||||||
|
this.checkAgentRange(['[', ']']);
|
||||||
|
|
||||||
const stagesHeight = Math.max(
|
const stagesHeight = Math.max(
|
||||||
this.currentY - this.theme.actionMargin,
|
this.currentY - this.theme.actionMargin,
|
||||||
|
|
Loading…
Reference in New Issue