From 918c62f049af4b64584856a38b2e87edf9de5396 Mon Sep 17 00:00:00 2001 From: David Evans Date: Thu, 3 May 2018 23:22:51 +0100 Subject: [PATCH] Add support for parallel actions using & syntax [#11] --- lib/sequence-diagram-web.js | 574 +++++++++----- lib/sequence-diagram-web.min.js | 2 +- lib/sequence-diagram.js | 574 +++++++++----- scripts/sequence/codemirror/Mode.mjs | 32 +- scripts/sequence/codemirror/Mode_webspec.mjs | 24 + scripts/sequence/generator/Generator.mjs | 261 +++++-- scripts/sequence/generator/Generator_spec.mjs | 200 ++++- scripts/sequence/parser/Parser.mjs | 281 +++---- scripts/sequence/parser/Parser_spec.mjs | 722 ++++++++---------- spec/images/Parallel.svg | 23 + spec/images/list.mjs | 1 + web/lib/editor.js | 14 + web/lib/editor.min.js | 2 +- web/scripts/interface/ComponentsLibrary.mjs | 14 + 14 files changed, 1694 insertions(+), 1030 deletions(-) create mode 100644 spec/images/Parallel.svg diff --git a/lib/sequence-diagram-web.js b/lib/sequence-diagram-web.js index bca6da4..1639950 100644 --- a/lib/sequence-diagram-web.js +++ b/lib/sequence-diagram-web.js @@ -1643,6 +1643,87 @@ } } + function extractParallel(target, stages) { + for(const stage of stages) { + if(!stage) { + continue; + } + if(stage.type === 'parallel') { + extractParallel(target, stage.stages); + } else { + target.push(stage); + } + } + } + + function checkAgentConflicts(allStages) { + const createIDs = flatMap( + allStages + .filter((stage) => (stage.type === 'agent begin')), + (stage) => stage.agentIDs + ); + + for(const stage of allStages) { + if(stage.type !== 'agent end') { + continue; + } + for(const id of stage.agentIDs) { + if(createIDs.indexOf(id) !== -1) { + return 'Cannot create and destroy ' + id + ' simultaneously'; + } + } + } + return null; + } + + function checkReferenceConflicts(allStages) { + const leftIDs = allStages + .filter((stage) => (stage.type === 'block begin')) + .map((stage) => stage.left); + + for(const stage of allStages) { + if(stage.type !== 'block end') { + continue; + } + if(leftIDs.indexOf(stage.left) !== -1) { + return 'Cannot create and destroy reference simultaneously'; + } + } + return null; + } + + function checkDelayedConflicts(allStages) { + const tags = allStages + .filter((stage) => (stage.type === 'connect-delay-begin')) + .map((stage) => stage.tag); + + for(const stage of allStages) { + if(stage.type !== 'connect-delay-end') { + continue; + } + if(tags.indexOf(stage.tag) !== -1) { + return 'Cannot start and finish delayed connection simultaneously'; + } + } + return null; + } + + function errorForParallel(existing, latest) { + if(!existing) { + return 'Nothing to run statement in parallel with'; + } + + const allStages = []; + extractParallel(allStages, [existing]); + extractParallel(allStages, [latest]); + + return ( + checkAgentConflicts(allStages) || + checkReferenceConflicts(allStages) || + checkDelayedConflicts(allStages) + ); + } + function swapBegin(stage, mode) { if(stage.type === 'agent begin') { stage.mode = mode; @@ -1758,37 +1839,75 @@ }); } - addStage(stage, isVisible = true) { + addStage(stage, {isVisible = true, parallel = false} = {}) { + if(!stage) { + return; + } + if(isVisible) { + this.currentNest.hasContent = true; + } + if(typeof stage.ln === 'undefined') { + stage.ln = this.latestLine; + } + const {stages} = this.currentSection; + if(parallel) { + const target = last(stages); + const err = errorForParallel(target, stage); + if(err) { + throw new Error(err); + } + const pstage = this.makeParallel([target, stage]); + pstage.ln = stage.ln; + -- stages.length; + stages.push(pstage); + } else { + stages.push(stage); + } + } + + addImpStage(stage, {parallel = false} = {}) { if(!stage) { return; } if(typeof stage.ln === 'undefined') { stage.ln = this.latestLine; } - this.currentSection.stages.push(stage); - if(isVisible) { - this.currentNest.hasContent = true; + const {stages} = this.currentSection; + if(parallel) { + const target = stages[stages.length - 2]; + if(!target) { + throw new Error('Nothing to run statement in parallel with'); + } + if(errorForParallel(target, stage)) { + stages.splice(stages.length - 1, 0, stage); + } else { + const pstage = this.makeParallel([target, stage]); + pstage.ln = stage.ln; + stages.splice(stages.length - 2, 1, pstage); + } + } else { + stages.push(stage); } } - addParallelStages(stages) { - const viableStages = stages.filter((stage) => Boolean(stage)); + makeParallel(stages) { + const viableStages = []; + extractParallel(viableStages, stages); if(viableStages.length === 0) { - return; + return null; } if(viableStages.length === 1) { - this.addStage(viableStages[0]); - return; + return viableStages[0]; } viableStages.forEach((stage) => { if(typeof stage.ln === 'undefined') { stage.ln = this.latestLine; } }); - this.addStage({ + return { stages: viableStages, type: 'parallel', - }); + }; } defineGAgents(gAgents) { @@ -2004,26 +2123,26 @@ this.currentNest = last(this.nesting); this.currentSection = last(this.currentNest.sections); - if(nested.hasContent) { - this.defineGAgents(nested.gAgents); - addBounds( - this.gAgents, - nested.leftGAgent, - nested.rightGAgent, - nested.gAgents - ); - nested.sections.forEach((section) => { - this.currentSection.stages.push(section.header); - this.currentSection.stages.push(...section.stages); - }); - this.addStage({ - left: nested.leftGAgent.id, - right: nested.rightGAgent.id, - type: 'block end', - }); - } else { + if(!nested.hasContent) { throw new Error('Empty block'); } + + this.defineGAgents(nested.gAgents); + addBounds( + this.gAgents, + nested.leftGAgent, + nested.rightGAgent, + nested.gAgents + ); + nested.sections.forEach((section) => { + this.currentSection.stages.push(section.header); + this.currentSection.stages.push(...section.stages); + }); + this.addStage({ + left: nested.leftGAgent.id, + right: nested.rightGAgent.id, + type: 'block end', + }); } makeGroupDetails(pAgents, alias) { @@ -2065,7 +2184,7 @@ }; } - handleGroupBegin({agents, blockType, tag, label, alias}) { + handleGroupBegin({agents, blockType, tag, label, alias, parallel}) { const details = this.makeGroupDetails(agents, alias); details.gAgentsContained.forEach((gAgent) => { @@ -2075,7 +2194,10 @@ this.updateGAgentState(gAgent, {covered: true}); }); this.activeGroups.set(alias, details); - this.addStage(this.setGAgentVis(details.gAgents, true, 'box')); + this.addImpStage( + this.setGAgentVis(details.gAgents, true, 'box'), + {parallel} + ); this.addStage({ blockType, canHide: false, @@ -2084,7 +2206,7 @@ right: details.rightGAgent.id, tag: this.textFormatter(tag), type: 'block begin', - }); + }, {parallel}); } endGroup({name}) { @@ -2111,7 +2233,7 @@ handleMark({name}) { this.markers.add(name); - this.addStage({name, type: 'mark'}, false); + this.addStage({name, type: 'mark'}, {isVisible: false}); } handleDivider({mode, height, label}) { @@ -2120,14 +2242,14 @@ height, mode, type: 'divider', - }, false); + }, {isVisible: false}); } handleAsync({target}) { if(target !== '' && !this.markers.has(target)) { throw new Error('Unknown marker: ' + target); } - this.addStage({target, type: 'async'}, false); + this.addStage({target, type: 'async'}, {isVisible: false}); } handleLabelPattern({pattern}) { @@ -2281,7 +2403,7 @@ return gAgents; } - _handlePartialConnect(agents) { + _handlePartialConnect(agents, parallel) { const flags = this.filterConnectFlags(agents); const gAgents = agents.map(this.toGAgent); @@ -2298,19 +2420,22 @@ .map(this.toGAgent) .filter((gAgent) => !gAgent.isVirtualSource) ); - this.addStage(this.setGAgentVis(implicitBeginGAgents, true, 'box')); + this.addImpStage( + this.setGAgentVis(implicitBeginGAgents, true, 'box'), + {parallel} + ); return {flags, gAgents}; } _makeConnectParallelStages(flags, connectStage) { - return [ + return this.makeParallel([ this.setGAgentVis(flags.beginGAgents, true, 'box', true), this.setGAgentHighlight(flags.startGAgents, true, true), connectStage, this.setGAgentHighlight(flags.stopGAgents, false, true), this.setGAgentVis(flags.endGAgents, false, 'cross', true), - ]; + ]); } _isSelfConnect(agents) { @@ -2325,7 +2450,7 @@ return true; } - handleConnect({agents, label, options}) { + handleConnect({agents, label, options, parallel}) { if(this._isSelfConnect(agents)) { const tag = {}; this.handleConnectDelayBegin({ @@ -2333,6 +2458,7 @@ ln: 0, options, tag, + parallel, }); this.handleConnectDelayEnd({ agent: agents[1], @@ -2343,7 +2469,7 @@ return; } - let {flags, gAgents} = this._handlePartialConnect(agents); + let {flags, gAgents} = this._handlePartialConnect(agents, parallel); gAgents = this.expandGroupedGAgentConnection(gAgents); gAgents = this.expandVirtualSourceAgents(gAgents); @@ -2355,19 +2481,19 @@ type: 'connect', }; - this.addParallelStages(this._makeConnectParallelStages( - flags, - connectStage - )); + this.addStage( + this._makeConnectParallelStages(flags, connectStage), + {parallel} + ); } - handleConnectDelayBegin({agent, tag, options, ln}) { + handleConnectDelayBegin({agent, tag, options, ln, parallel}) { const dcs = this.currentSection.delayedConnections; if(dcs.has(tag)) { throw new Error('Duplicate delayed connection "' + tag + '"'); } - const {flags, gAgents} = this._handlePartialConnect([agent]); + const {flags, gAgents} = this._handlePartialConnect([agent], parallel); const uniqueTag = this.nextVirtualAgentName(); const connectStage = { @@ -2380,20 +2506,20 @@ dcs.set(tag, {connectStage, gAgents, ln, tag, uniqueTag}); - this.addParallelStages(this._makeConnectParallelStages( - flags, - connectStage - )); + this.addStage( + this._makeConnectParallelStages(flags, connectStage), + {parallel} + ); } - handleConnectDelayEnd({agent, tag, label, options}) { + handleConnectDelayEnd({agent, tag, label, options, parallel}) { const dcs = this.currentSection.delayedConnections; const dcInfo = dcs.get(tag); if(!dcInfo) { throw new Error('Unknown delayed connection "' + tag + '"'); } - let {flags, gAgents} = this._handlePartialConnect([agent]); + let {flags, gAgents} = this._handlePartialConnect([agent], parallel); gAgents = this.expandGroupedGAgentConnection([ ...dcInfo.gAgents, @@ -2422,15 +2548,15 @@ type: 'connect-delay-end', }; - this.addParallelStages(this._makeConnectParallelStages( - flags, - connectEndStage - )); + this.addStage( + this._makeConnectParallelStages(flags, connectEndStage), + {parallel} + ); dcs.delete(tag); } - handleNote({type, agents, mode, label}) { + handleNote({type, agents, mode, label, parallel}) { let gAgents = null; if(agents.length === 0) { gAgents = NOTE_DEFAULT_G_AGENTS[type] || []; @@ -2446,15 +2572,14 @@ throw new Error('note between requires at least 2 agents'); } - this.addStage(this.setGAgentVis(gAgents, true, 'box')); this.defineGAgents(gAgents); - + this.addImpStage(this.setGAgentVis(gAgents, true, 'box'), {parallel}); this.addStage({ agentIDs, label: this.textFormatter(label), mode, type, - }); + }, {parallel}); } handleAgentDefine({agents}) { @@ -2482,13 +2607,13 @@ }); } - handleAgentBegin({agents, mode}) { + handleAgentBegin({agents, mode, parallel}) { const gAgents = agents.map(this.toGAgent); this.validateGAgents(gAgents); - this.addStage(this.setGAgentVis(gAgents, true, mode, true)); + this.addStage(this.setGAgentVis(gAgents, true, mode, true), {parallel}); } - handleAgentEnd({agents, mode}) { + handleAgentEnd({agents, mode, parallel}) { const groupPAgents = (agents .filter((pAgent) => this.activeGroups.has(pAgent.name)) ); @@ -2497,11 +2622,11 @@ .map(this.toGAgent) ); this.validateGAgents(gAgents); - this.addParallelStages([ + this.addStage(this.makeParallel([ this.setGAgentHighlight(gAgents, false), this.setGAgentVis(gAgents, false, mode, true), ...groupPAgents.map(this.endGroup), - ]); + ]), {parallel}); } handleStage(stage) { @@ -2570,10 +2695,10 @@ this._checkSectionEnd(); const terminators = meta.terminators || 'none'; - this.addParallelStages([ + this.addStage(this.makeParallel([ this.setGAgentHighlight(this.gAgents, false), this.setGAgentVis(this.gAgents, false, terminators), - ]); + ])); this._finalise(globals); @@ -3026,6 +3151,14 @@ 'red', ]; + const PARALLEL_TASKS = [ + 'begin', + 'end', + 'note', + 'state', + 'text', + ]; + const makeCommands = ((() => { function agentListTo(exit, next = 1) { return { @@ -3386,10 +3519,25 @@ }}, }; - return (arrows) => ({ - type: 'error line-error', - then: Object.assign({}, BASE_THEN, makeCMConnect(arrows)), - }); + return (arrows) => { + const arrowConnect = makeCMConnect(arrows); + + const parallel = {}; + for(const task of PARALLEL_TASKS) { + parallel[task] = BASE_THEN[task]; + } + Object.assign(parallel, arrowConnect); + + return { + type: 'error line-error', + then: Object.assign( + {}, + BASE_THEN, + {'&': {type: 'keyword', then: parallel}}, + arrowConnect + ), + }; + }; })()); /* eslint-enable sort-keys */ @@ -3637,6 +3785,7 @@ if(state.currentType === NO_TOKEN) { if(stream.sol()) { state.line.length = 0; + state.valid = true; } if(!this._tokenBegin(stream, state)) { return ''; @@ -4070,36 +4219,40 @@ return result; } - /* eslint-disable sort-keys */ // Maybe later - - const BLOCK_TYPES = { - 'if': { - type: 'block begin', - blockType: 'if', - tag: 'if', - skip: [], - }, - 'else': { - type: 'block split', - blockType: 'else', - tag: 'else', - skip: ['if'], - }, - 'repeat': { - type: 'block begin', - blockType: 'repeat', - tag: 'repeat', - skip: [], - }, - 'group': { - type: 'block begin', - blockType: 'group', - tag: '', - skip: [], - }, - }; + const BLOCK_TYPES = new Map(); + BLOCK_TYPES.set('if', { + blockType: 'if', + skip: [], + tag: 'if', + type: 'block begin', + }); + BLOCK_TYPES.set('else', { + blockType: 'else', + skip: ['if'], + tag: 'else', + type: 'block split', + }); + BLOCK_TYPES.set('repeat', { + blockType: 'repeat', + skip: [], + tag: 'repeat', + type: 'block begin', + }); + BLOCK_TYPES.set('group', { + blockType: 'group', + skip: [], + tag: '', + type: 'block begin', + }); const CONNECT = { + agentFlags: { + '!': {flag: 'end'}, + '*': {allowBlankName: true, blankNameFlag: 'source', flag: 'begin'}, + '+': {flag: 'start'}, + '-': {flag: 'stop'}, + }, + types: ((() => { const lTypes = [ {tok: '', type: 0}, @@ -4125,21 +4278,14 @@ arrows.forEach((arrow) => { types.set(arrow.map((part) => part.tok).join(''), { - line: arrow[1].type, left: arrow[0].type, + line: arrow[1].type, right: arrow[2].type, }); }); return types; })()), - - agentFlags: { - '*': {flag: 'begin', allowBlankName: true, blankNameFlag: 'source'}, - '+': {flag: 'start'}, - '-': {flag: 'stop'}, - '!': {flag: 'end'}, - }, }; const TERMINATOR_TYPES = [ @@ -4150,73 +4296,70 @@ 'bar', ]; - const NOTE_TYPES = { - 'text': { - mode: 'text', - types: { - 'left': { - type: 'note left', - skip: ['of'], - min: 0, - max: Number.POSITIVE_INFINITY, - }, - 'right': { - type: 'note right', - skip: ['of'], - min: 0, - max: Number.POSITIVE_INFINITY, - }, + const NOTE_TYPES = new Map(); + NOTE_TYPES.set('text', { + mode: 'text', + types: { + 'left': { + max: Number.POSITIVE_INFINITY, + min: 0, + skip: ['of'], + type: 'note left', + }, + 'right': { + max: Number.POSITIVE_INFINITY, + min: 0, + skip: ['of'], + type: 'note right', }, }, - 'note': { - mode: 'note', - types: { - 'over': { - type: 'note over', - skip: [], - min: 0, - max: Number.POSITIVE_INFINITY, - }, - 'left': { - type: 'note left', - skip: ['of'], - min: 0, - max: Number.POSITIVE_INFINITY, - }, - 'right': { - type: 'note right', - skip: ['of'], - min: 0, - max: Number.POSITIVE_INFINITY, - }, - 'between': { - type: 'note between', - skip: [], - min: 2, - max: Number.POSITIVE_INFINITY, - }, + }); + NOTE_TYPES.set('note', { + mode: 'note', + types: { + 'between': { + max: Number.POSITIVE_INFINITY, + min: 2, + skip: [], + type: 'note between', + }, + 'left': { + max: Number.POSITIVE_INFINITY, + min: 0, + skip: ['of'], + type: 'note left', + }, + 'over': { + max: Number.POSITIVE_INFINITY, + min: 0, + skip: [], + type: 'note over', + }, + 'right': { + max: Number.POSITIVE_INFINITY, + min: 0, + skip: ['of'], + type: 'note right', }, }, - 'state': { - mode: 'state', - types: { - 'over': {type: 'note over', skip: [], min: 1, max: 1}, - }, + }); + NOTE_TYPES.set('state', { + mode: 'state', + types: { + 'over': {max: 1, min: 1, skip: [], type: 'note over'}, }, - }; + }); - const DIVIDER_TYPES = { - 'line': {defaultHeight: 6}, - 'space': {defaultHeight: 6}, - 'delay': {defaultHeight: 30}, - 'tear': {defaultHeight: 6}, - }; + const DIVIDER_TYPES = new Map(); + DIVIDER_TYPES.set('line', {defaultHeight: 6}); + DIVIDER_TYPES.set('space', {defaultHeight: 6}); + DIVIDER_TYPES.set('delay', {defaultHeight: 30}); + DIVIDER_TYPES.set('tear', {defaultHeight: 6}); - const AGENT_MANIPULATION_TYPES = { - 'define': {type: 'agent define'}, - 'begin': {type: 'agent begin', mode: 'box'}, - 'end': {type: 'agent end', mode: 'cross'}, - }; + const AGENT_MANIPULATION_TYPES = new Map(); + AGENT_MANIPULATION_TYPES.set('define', {type: 'agent define'}); + AGENT_MANIPULATION_TYPES.set('begin', {mode: 'box', type: 'agent begin'}); + AGENT_MANIPULATION_TYPES.set('end', {mode: 'cross', type: 'agent end'}); function makeError(message, token = null) { let suffix = ''; @@ -4316,8 +4459,8 @@ throw makeError('Missing agent name', errPosToken); } return { - name: joinLabel(line, start, aliasSep), alias: joinLabel(line, aliasSep + 1, end), + name: joinLabel(line, start, aliasSep), }; } @@ -4344,13 +4487,13 @@ blankNameFlags.push(flag.blankNameFlag); } const {name, alias} = readAgentAlias(line, p, end, { - enableAlias: aliases, allowBlankName, + enableAlias: aliases, }); return { - name, alias, flags: name ? flags : blankNameFlags, + name, }; } @@ -4417,7 +4560,7 @@ }); const mode = joinLabel(line, 1, heightSep) || 'line'; - if(!DIVIDER_TYPES[mode]) { + if(!DIVIDER_TYPES.has(mode)) { throw makeError('Unknown divider type', line[1]); } @@ -4425,17 +4568,17 @@ line, heightSep + 2, labelSep, - DIVIDER_TYPES[mode].defaultHeight + DIVIDER_TYPES.get(mode).defaultHeight ); if(Number.isNaN(height) || height < 0) { throw makeError('Invalid divider height', line[heightSep + 2]); } return { - type: 'divider', - mode, height, label: joinLabel(line, labelSep + 1), + mode, + type: 'divider', }; }}, @@ -4447,8 +4590,8 @@ raw = joinLabel(line, 1); } return { - type: 'label pattern', pattern: parsePattern(raw), + type: 'label pattern', }; }}, @@ -4460,7 +4603,7 @@ }}, {begin: [], fn: (line) => { // Block - const type = BLOCK_TYPES[tokenKeyword(line[0])]; + const type = BLOCK_TYPES.get(tokenKeyword(line[0])); if(!type) { return null; } @@ -4470,10 +4613,10 @@ } skip = skipOver(line, skip, [':']); return { - type: type.type, blockType: type.blockType, - tag: type.tag, label: joinLabel(line, skip), + tag: type.tag, + type: type.type, }; }}, @@ -4495,17 +4638,17 @@ throw makeError('Reference must have an alias', line[labelSep]); } return { - type: 'group begin', agents, - blockType: 'ref', - tag: 'ref', - label: def.name, alias: def.alias, + blockType: 'ref', + label: def.name, + tag: 'ref', + type: 'group begin', }; }}, {begin: [], fn: (line) => { // Agent - const type = AGENT_MANIPULATION_TYPES[tokenKeyword(line[0])]; + const type = AGENT_MANIPULATION_TYPES.get(tokenKeyword(line[0])); if(!type || line.length <= 1) { return null; } @@ -4526,13 +4669,13 @@ target = joinLabel(line, 2, line.length - 1); } return { - type: 'async', target, + type: 'async', }; }}, {begin: [], fn: (line) => { // Note - const mode = NOTE_TYPES[tokenKeyword(line[0])]; + const mode = NOTE_TYPES.get(tokenKeyword(line[0])); const labelSep = findTokens(line, [':']); if(!mode || labelSep === -1) { return null; @@ -4551,10 +4694,10 @@ throw makeError('Too many agents for ' + mode.mode, line[0]); } return { - type: type.type, agents, - mode: mode.mode, label: joinLabel(line, labelSep + 1), + mode: mode.mode, + type: type.type, }; }}, @@ -4563,7 +4706,7 @@ const connectionToken = findFirstToken( line, CONNECT.types, - {start: 0, limit: labelSep - 1} + {limit: labelSep - 1, start: 0} ); if(!connectionToken) { return null; @@ -4577,11 +4720,11 @@ if(tokenKeyword(line[0]) === '...') { return { - type: 'connect-delay-end', - tag: joinLabel(line, 1, connectPos), agent: readAgent(line, connectPos + 1, labelSep, readOpts), label: joinLabel(line, labelSep + 1), options: connectionToken.value, + tag: joinLabel(line, 1, connectPos), + type: 'connect-delay-end', }; } else if(tokenKeyword(line[connectPos + 1]) === '...') { if(labelSep !== line.length) { @@ -4591,20 +4734,20 @@ ); } return { - type: 'connect-delay-begin', - tag: joinLabel(line, connectPos + 2, labelSep), agent: readAgent(line, 0, connectPos, readOpts), options: connectionToken.value, + tag: joinLabel(line, connectPos + 2, labelSep), + type: 'connect-delay-begin', }; } else { return { - type: 'connect', agents: [ readAgent(line, 0, connectPos, readOpts), readAgent(line, connectPos + 1, labelSep, readOpts), ], label: joinLabel(line, labelSep + 1), options: connectionToken.value, + type: 'connect', }; } }}, @@ -4614,8 +4757,8 @@ return null; } return { - type: 'mark', name: joinLabel(line, 0, line.length - 1), + type: 'mark', }; }}, @@ -4638,33 +4781,44 @@ options.push(line[i].v); } return { - type: 'agent options', agent, options, + type: 'agent options', }; }}, ]; - function parseLine(line, {meta, stages}) { - let stage = null; + function stageFromLine(line, meta) { for(const {begin, fn} of PARSERS) { if(skipOver(line, 0, begin) !== begin.length) { continue; } - stage = fn(line, meta); + const stage = fn(line, meta); if(stage) { - break; + return stage; } } - if(!stage) { - throw makeError( - 'Unrecognised command: ' + joinLabel(line), - line[0] - ); + return null; + } + + function parseLine(line, {meta, stages}) { + let parallel = false; + const [start] = line; + if(tokenKeyword(start) === '&') { + parallel = true; + line.splice(0, 1); } - if(typeof stage === 'object') { - stage.ln = line[0].b.ln; + + const stage = stageFromLine(line, meta); + + if(!stage) { + throw makeError('Unrecognised command: ' + joinLabel(line), line[0]); + } else if(typeof stage === 'object') { + stage.ln = start.b.ln; + stage.parallel = parallel; stages.push(stage); + } else if(parallel) { + throw makeError('Metadata cannot be parallel', start); } } @@ -4680,12 +4834,12 @@ parseLines(lines, src) { const result = { meta: { - title: '', - theme: '', code: src, - terminators: 'none', headers: 'box', + terminators: 'none', textFormatter: parseMarkdown, + theme: '', + title: '', }, stages: [], }; diff --git a/lib/sequence-diagram-web.min.js b/lib/sequence-diagram-web.min.js index a27985d..924c0b0 100644 --- a/lib/sequence-diagram-web.min.js +++ b/lib/sequence-diagram-web.min.js @@ -1 +1 @@ -!function(){"use strict";function t(t,e,n=null){if(null===n)return t.indexOf(e);for(let s=0;s=t.length)return void s.push(n.slice());const r=t[e];if(!Array.isArray(r))return n.push(r),i(t,e+1,n,s),void n.pop();for(let a=0;a{n.push(...e(t))}),n}function o(t,e){const n=Ct[t.type];return!(!n||t.type!==e.type)&&!n.check.some(n=>t[n]!==e[n])}function h(t,n){Ct[t.type].merge.forEach(s=>{e(t[s],n[s])})}function l(t,e){for(let n=0;n{for(let s=0;st);return n.forEach(t=>{const s=Ct[t];s&&n.every(e=>t===e||s.siblings.has(e))&&e.add(t)}),e}function c(t,e,n,s){l(s,s=>{if(!t.has(s.type)||!e.has(s.type))return!1;for(let t=0;t{"agent begin"===t.type&&(t.mode=e,n=!0)}),n}return!1}function p(t,e,n,r=null){s(t,e,St.equals),s(t,n,St.equals);let i=0,a=t.length;if(r){const e=r.map(e=>St.indexOf(t,e)).filter(t=>-1!==t);i=e.reduce((t,e)=>Math.min(t,e),t.length),a=e.reduce((t,e)=>Math.max(t,e),i)+1}return t.splice(i,0,e),t.splice(a+1,0,n),{indexL:i,indexR:a+1}}function f(t,e=[]){return{type:"string",suggest:e,then:Object.assign({"":0},t)}}function m(t,e){return t.v===e.v&&t.prefix===e.prefix&&t.suffix===e.suffix&&t.q===e.q}function b(t,e,n){return a(n.suggest||[""],s=>{if("object"==typeof s)return s.known?t["known"+s.known]||[]:[s];if(""===s)return[function(t,e){return Object.keys(e.then).length>0?{q:!1,suffix:" ",v:t}:{q:!1,suffix:"\n",v:t}}(e,n)];if("string"==typeof s)return[{q:""===e,v:s}];throw new Error("Invalid suggestion type "+s)})}function y(t,n){const s=[],i=r(n);return Object.keys(i.then).forEach(r=>{let a=i.then[r];"number"==typeof a&&(a=n[n.length-a-1]),e(s,b(t,r,a),m)}),s}function x(t,n,s,r){const i=function(t){for(const e of t)if("object"==typeof e&&e.known)return e.known;return null}(r.suggest||[""]);n.type&&i!==n.type&&(!function(t,n,s){e(t["known"+n],[{q:!0,suffix:" ",v:s}],m)}(t,r.override||n.type,n.value),n.value=""),i&&(n.value=function(t,e){return t+(t?e.s:"")+e.v}(n.value,s)),n.type=i}function w(t,e,n){const s={type:"",value:""};let i=n;const a=[i];return t.line.forEach((e,n)=>{n===t.line.length-1&&(t.completions=y(t,a));const o=e.q?"":e.v;let h=i.then[o];void 0===h?(h=i.then[""],t.isVar=!0):t.isVar=e.q,"number"==typeof h?a.length-=h:a.push(h||Ot),i=r(a),x(t,s,e,i)}),e&&x(t,s,null,{}),t.nextCompletions=y(t,a),t.valid=Boolean(i.then["\n"])||0===Object.keys(i.then).length,i.type}function k(t){const e=t.baseToken||{};return{quoted:e.q||!1,value:e.v||""}}function v(t,e,n){return e.lastIndex=n,e.exec(t)}function A(t,e,n){return n?function(t,e,n){if(n.escape){const s=v(t,n.escape,e);if(s)return{appendSpace:"",appendValue:n.escapeWith(s),end:!1,newBlock:null,skip:s[0].length}}const s=v(t,n.end,e);return s?{appendSpace:"",appendValue:n.includeEnd?s[0]:"",end:!0,newBlock:null,skip:s[0].length}:{appendSpace:"",appendValue:t[e],end:!1,newBlock:null,skip:1}}(t,e,n):function(t,e){for(let n=0;n"}function R(t,e,n){const s=" "+t+" ";let r=-1,i=s.length,a=0;return Ut.forEach(({begin:t,end:o},h)=>{const l=n[h]?o:t;l.lastIndex=e;const d=l.exec(s);d&&(d.indexa)&&(r=h,i=d.index,a=l.lastIndex)}),{end:a-1,start:i,styleIndex:r}}function C(t,e){if(!t)return null;const n={};return e.forEach((t,e)=>{t&&Object.assign(n,Ut[e].attrs)}),n}function I(t){return t.replace(/^[\f\n\r\t\v ]+|[\f\n\r\t\v ]+$/g,"")}function E(t){if(!t)return[];const e=Ut.map(()=>!1);let n=0,s=null;const r=[];return I(t).split("\n").forEach(t=>{const i=function(t){return t.replace(/[\f\n\r\t\v ]+/g," ")}(I(t)),a=[];let o=0;for(;;){const{styleIndex:t,start:r,end:h}=R(i,o,e);if(-1===t)break;e[t]?(e[t]=!1,--n):(e[t]=!0,++n),r>o&&a.push({attrs:s,text:i.substring(o,r)}),s=C(n,e),o=h}o=a&&!i){let n=t[e];throw n||(n={b:r(t).e}),G("Missing agent name",n)}return{name:L(t,e,a),alias:L(t,a+1,n)}}(t,h,n,{enableAlias:i,allowBlankName:l});return{name:d,alias:g,flags:d?a:o}}function T(t,e,n,s){const r=[];let i=-1;for(let a=e;a{const r=t.get(e);(null===n||r.indexs.index)&&(s=r)}),{left:n.id,right:s.id}}function j(t=null,e=null){return null===t?e:null===e?t:Math.max(t,e)}function q(t,n){return e(t.agentIDs,n.agentIDs),{agentIDs:t.agentIDs,asynchronousY:j(t.asynchronousY,n.asynchronousY),topShift:Math.max(t.topShift,n.topShift),y:j(t.y,n.y)}}function Y(t){return null===t?null:t.element?t.element:t}function U(t,e,n){if(!Array.isArray(n))throw new Error("Invalid formatted text line: "+n);n.forEach(({text:n,attrs:s})=>{s?e.add(t.el("tspan").attrs(s).add(n)):e.add(n)})}function W(t,e){let n=null,s=null;return e.forEach(e=>{const r=t.get(e);(null===n||r.indexs.index)&&(s=r)}),{left:n.id,right:s.id}}function X(t,e){return t.v===e.v&&t.prefix===e.prefix&&t.suffix===e.suffix&&t.q===e.q}function Q(t,e,n){const s=t.getLine(e),r={squash:{ch:n,line:e},word:{ch:n,line:e}};return n>0&&" "===s[n-1]&&(Pe.after.includes(s[n-2])&&r.word.ch--,r.squash.ch--),r}function J(t,e,n){const s=function({v:t,q:e,prefix:n="",suffix:s=""},r){const i=r||!qe.test(t)?r:'"';return n+(i&&e?i+t.replace(Ye,"\\$&")+i:t)+s}(t,n),r=t.q?e.fromVar:e.fromKey;return"\n"===s?{className:"pick-virtual",displayFrom:null,displayText:"",from:r.squash,text:"\n",to:e.to.squash}:{className:null,displayFrom:r.word,displayText:s.trim(),from:Pe.start.test(s)?r.squash:r.word,text:s,to:Pe.end.test(s)?e.to.squash:e.to.word}}function Z({global:t,prefix:e="",suffix:n=""},s){const r=s[t];return r?r.map(t=>({prefix:e,q:!0,suffix:n,v:t})):[]}function K(t,n,s){let r=null;return r=t.ch>0&&n.state.line.length>0?n.state.completions.slice():n.state.beginCompletions.concat(n.state.knownAgent),function(t,n={}){for(let s=0;s=e.ch){n.length=t+1;break}return n}(t,n),i=r(s)||t.getTokenAt(n),a=function(t,e){let n="",s=0,r=0;t.forEach(t=>{t.state.isVar?(n+=t.string,r=t.end):(n="",s=t.end)}),r>e.ch&&(n=n.substr(0,e.ch-s));const i=He.exec(n);n=i[2];let a="";return je.test(n)&&(a=n.charAt(0),n=n.substr(1)),{from:s+i[1].length,partial:n,quote:a,valid:r>=s}}(s,n),o=function(t,e){let n=t.string;t.end>e.ch&&(n=n.substr(0,e.ch-t.start));const s=He.exec(n);return{from:t.start+s[1].length,partial:s[2],valid:!0}}(i,n),h=K(n,i,t.options.globals),l={fromKey:Q(t,n.line,o.from),fromVar:Q(t,n.line,a.from),to:function(t,e,n){const s={squash:{ch:n,line:e},word:{ch:n,line:e}};return" "===t.getLine(e)[n]&&s.squash.ch++,s}(t,n.line,i.end)};let d=null;const g=h.filter(t=>(t.q||!a.quote)&&function(t,e){return e.valid&&t.startsWith(e.partial)}(t.v,t.q?a:o)).map(t=>e.completeSingle||t.v!==(t.q?a:o).partial?J(t,l,a.quote):(d=t,null)).filter(t=>null!==t);return d&&g.length>0&&g.unshift(J(d,l,a.quote)),{from:function(t,e){let n=null;return t.forEach(({displayFrom:t})=>{t&&(!n||t.line>n.line||t.line===n.line&&t.ch>n.ch)&&(n=t)}),n||e.word}(g,l.fromKey),list:g,to:l.to.word}}function $(t,e="sequence"){const n=t||window.CodeMirror;n.defineMode(e,()=>Qe),n.registerHelper("hint",e,_)}function tt(t){const e=(new DOMParser).parseFromString(t,"image/svg+xml").querySelector("metadata");return e?e.textContent:""}function et(t){function e(t,e){n.push(e)}const n=[];if(t.forEach(t=>{t.addEventListener("error",e),t.optimisedRenderPreReflow()}),t.forEach(t=>{t.optimisedRenderReflow()}),t.forEach(t=>{t.optimisedRenderPostReflow(),t.removeEventListener("error",e)}),n.length>0)throw n}function nt(t,e=null,n={}){if("svg"===t.tagName)return null;const s=function(t){return{interactive:function(t){return void 0!==t&&"false"!==t}(t.dataset.sdInteractive),namespace:t.dataset.sdNamespace||null}}(t),r=new Je(null===e?t.textContent:e,Object.assign(s,n)),i=r.dom(),a=t.attributes;for(let t=0;tnt(t,s,e)).filter(t=>null!==t);return!1!==r.render&&et(n),n}return nt(t,s,r)}class rt{constructor(t,e){Array.isArray(e)?this.deltas=e:this.deltas=[0,2*-e/3,-e,2*-e/3,0,2*e/3,e,2*e/3],this.partWidth=t/this.deltas.length}getDelta(t){return this.deltas[t%this.deltas.length]}}class it{constructor(t){this.svg=t}reset(){}addDefs(){}getBlock(t){return this.blocks[t]||this.blocks[""]}getNote(t){return this.notes[t]||this.notes[""]}getDivider(t){return this.dividers[t]||this.dividers[""]}optionsAttributes(t,e){return function(t,e){const n=Object.assign({},t[""]);return e.forEach(e=>{Object.assign(n,t[e]||{})}),n}(t,e)}renderAgentLine({className:t,options:e,width:n,x:s,y0:r,y1:i}){const a=this.optionsAttributes(this.agentLineAttrs,e);return n>0?this.svg.box(a,{height:i-r,width:n,x:s-n/2,y:r}).addClass(t):this.svg.line(a,{x1:s,x2:s,y1:r,y2:i}).addClass(t)}renderArrowHead(t,{dir:e,height:n,width:s,x:r,y:i}){const a=s*e.dx,o=s*e.dy,h=.5*n*e.dx,l=.5*-n*e.dy;return this.svg.el("none"===t.fill?"polyline":"polygon").attr("points",r+a-l+" "+(i+o-h)+" "+r+" "+i+" "+(r+a+l)+" "+(i+o+h)).attrs(t)}renderTag(t,{height:e,width:n,x:s,y:r}){const{rx:i,ry:a}=t,o=s+n,h=r+e,l="M"+o+" "+r+"L"+o+" "+(h-a)+"L"+(o-i)+" "+h+"L"+s+" "+h,d=this.svg.el("g");return"none"!==t.fill&&d.add(this.svg.el("path").attr("d",l+"L"+s+" "+r).attrs(t).attr("stroke","none")),"none"!==t.stroke&&d.add(this.svg.el("path").attr("d",l).attrs(t).attr("fill","none")),d}renderDB(t,e){const n=t["db-z"];return this.svg.el("g").add(this.svg.box({rx:e.width/2,ry:n},e).attrs(t),this.svg.el("path").attr("d","M"+e.x+" "+(e.y+n)+"a"+e.width/2+" "+n+" 0 0 0 "+e.width+" 0").attrs(t).attr("fill","none"))}renderRef(t,e){return{fill:this.svg.box(t,e).attrs({stroke:"none"}),mask:this.svg.box(t,e).attrs({fill:"#000000",stroke:"none"}),shape:this.svg.box(t,e).attrs({fill:"none"})}}renderFlatConnect(t,e,{x1:n,y1:s,x2:r,y2:i}){return{p1:{x:n,y:s},p2:{x:r,y:i},shape:this.svg.el("path").attr("d",this.svg.patternedLine(t).move(n,s).line(r,i).cap().asPath()).attrs(e)}}renderRevConnect(t,e,{rad:n,x1:s,x2:r,xR:i,y1:a,y2:o}){const h=(o-a)/2,l=this.svg.patternedLine(t).move(s,a).line(i,a);return n0?this.svg.el("g").add(this.svg.line({fill:"none"},{x1:r,x2:r+(s-n)/2,y1:o,y2:o}).attrs(t),this.svg.line({fill:"none"},{x1:r+(s+n)/2,x2:r+s,y1:o,y2:o}).attrs(t)):this.svg.line({fill:"none"},{x1:r,x2:r+s,y1:o,y2:o}).attrs(t),{shape:a}}renderDelayDivider({dotSize:t,gapSize:e},{height:n,width:s,x:r,y:i}){const a=this.svg.el("g");for(let o=0;o+e<=n;o+=t+e)a.add(this.svg.box({fill:"#000000"},{height:e,width:s,x:r,y:i+o}));return{mask:a}}renderTearDivider({fadeBegin:t,fadeSize:e,lineAttrs:n,pattern:s,zigHeight:r,zigWidth:i},{env:a,height:o,labelHeight:h,labelWidth:l,width:d,x:g,y:c}){const u=a.addDef("tear-grad",()=>{const n=100/d;return this.svg.linearGradient({},[{offset:t*n+"%","stop-color":"#000000"},{offset:(t+e)*n+"%","stop-color":"#FFFFFF"},{offset:100-(t+e)*n+"%","stop-color":"#FFFFFF"},{offset:100-t*n+"%","stop-color":"#000000"}])}),p=this.svg.el("mask").attr("maskUnits","userSpaceOnUse").add(this.svg.box({fill:"url(#"+u+")"},{height:o+10,width:d,x:g,y:c-5})),f=a.addDef(p);l>0&&p.add(this.svg.box({fill:"#000000",rx:2,ry:2},{height:h+2,width:l,x:g+(d-l)/2,y:c+(o-h)/2-1}));const m=s||new rt(i,[r,-r]);let b=null;const y=this.svg.patternedLine(m).move(g,c).line(g+d,c),x=this.svg.el("g").attr("mask","url(#"+f+")").add(this.svg.el("path").attrs({d:y.asPath(),fill:"none"}).attrs(n));if(o>0){const t=this.svg.patternedLine(m).move(g,c+o).line(g+d,c+o);x.add(this.svg.el("path").attrs({d:t.asPath(),fill:"none"}).attrs(n)),y.line(t.x,t.y,{patterned:!1}).cap(),y.points.push(...t.points.reverse()),b=this.svg.el("path").attrs({d:y.asPath(),fill:"#000000"})}return{mask:b,shape:x}}}const at="Helvetica,Arial,Liberation Sans,sans-serif",ot=1.3,ht=new rt(6,.5),lt={"font-family":at,"font-size":8,"line-height":ot},dt={"font-family":at,"font-size":8,"line-height":ot,"text-anchor":"middle"};class gt extends it{constructor(t){super(t);const e={padding:{top:3,bottom:2},tag:{padding:{top:1,left:3,right:3,bottom:0},boxRenderer:this.renderTag.bind(this,{fill:"#FFFFFF",stroke:"#000000","stroke-width":1,rx:2,ry:2}),labelAttrs:{"font-family":at,"font-weight":"bold","font-size":9,"line-height":ot,"text-anchor":"left"}},label:{minHeight:4,padding:{top:1,left:5,right:3,bottom:1},labelAttrs:{"font-family":at,"font-size":8,"line-height":ot,"text-anchor":"left"}}};Object.assign(this,{titleMargin:10,outerMargin:5,agentMargin:10,actionMargin:10,minActionMargin:3,agentLineHighlightRadius:4,agentCap:{box:{padding:{top:5,left:10,right:10,bottom:5},arrowBottom:12.8,boxAttrs:{fill:"#FFFFFF",stroke:"#000000","stroke-width":1},labelAttrs:{"font-family":at,"font-size":12,"line-height":ot,"text-anchor":"middle"}},database:{padding:{top:12,left:10,right:10,bottom:3},arrowBottom:12.8,boxRenderer:this.renderDB.bind(this,{fill:"#FFFFFF",stroke:"#000000","stroke-width":1,"db-z":5}),labelAttrs:{"font-family":at,"font-size":12,"line-height":ot,"text-anchor":"middle"}},cross:{size:20,render:t.crossFactory({fill:"none",stroke:"#000000","stroke-width":1})},bar:{height:4,render:t.boxFactory({fill:"#000000",stroke:"#000000","stroke-width":1})},fade:{width:5,height:6,extend:1},none:{height:10}},connect:{loopbackRadius:6,line:{solid:{attrs:{fill:"none",stroke:"#000000","stroke-width":1},renderFlat:this.renderFlatConnect.bind(this,null),renderRev:this.renderRevConnect.bind(this,null)},dash:{attrs:{fill:"none",stroke:"#000000","stroke-width":1,"stroke-dasharray":"4, 2"},renderFlat:this.renderFlatConnect.bind(this,null),renderRev:this.renderRevConnect.bind(this,null)},wave:{attrs:{fill:"none",stroke:"#000000","stroke-width":1,"stroke-linejoin":"round","stroke-linecap":"round"},renderFlat:this.renderFlatConnect.bind(this,ht),renderRev:this.renderRevConnect.bind(this,ht)}},arrow:{single:{width:5,height:10,render:this.renderArrowHead.bind(this),attrs:{fill:"#000000","stroke-width":0,"stroke-linejoin":"miter"}},double:{width:4,height:6,render:this.renderArrowHead.bind(this),attrs:{fill:"none",stroke:"#000000","stroke-width":1,"stroke-linejoin":"miter"}},cross:{short:7,radius:3,render:t.crossFactory({fill:"none",stroke:"#000000","stroke-width":1})}},label:{padding:6,margin:{top:2,bottom:1},attrs:{"font-family":at,"font-size":8,"line-height":ot,"text-anchor":"middle"},loopbackAttrs:{"font-family":at,"font-size":8,"line-height":ot}},source:{radius:2,render:t.circleFactory({fill:"#000000",stroke:"#000000","stroke-width":1})},mask:{padding:{top:0,left:3,right:3,bottom:1}}},titleAttrs:{"font-family":at,"font-size":20,"line-height":ot,"text-anchor":"middle",class:"title"},agentLineAttrs:{"":{fill:"none",stroke:"#000000","stroke-width":1},red:{stroke:"#CC0000"}},blocks:{ref:{margin:{top:0,bottom:0},boxRenderer:this.renderRef.bind(this,{fill:"#FFFFFF",stroke:"#000000","stroke-width":1.5,rx:2,ry:2}),section:e},"":{margin:{top:0,bottom:0},boxRenderer:t.boxFactory({fill:"none",stroke:"#000000","stroke-width":1.5,rx:2,ry:2}),collapsedBoxRenderer:this.renderRef.bind(this,{fill:"#FFFFFF",stroke:"#000000","stroke-width":1.5,rx:2,ry:2}),section:e,sepRenderer:t.lineFactory({stroke:"#000000","stroke-width":1.5,"stroke-dasharray":"4, 2"})}},notes:{text:{margin:{top:0,left:2,right:2,bottom:0},padding:{top:2,left:2,right:2,bottom:2},overlap:{left:10,right:10},boxRenderer:t.boxFactory({fill:"#FFFFFF"}),labelAttrs:lt},note:{margin:{top:0,left:5,right:5,bottom:0},padding:{top:5,left:5,right:10,bottom:5},overlap:{left:10,right:10},boxRenderer:t.noteFactory({fill:"#FFFFFF",stroke:"#000000","stroke-width":1},{fill:"none",stroke:"#000000","stroke-width":1}),labelAttrs:lt},state:{margin:{top:0,left:5,right:5,bottom:0},padding:{top:7,left:7,right:7,bottom:7},overlap:{left:10,right:10},boxRenderer:t.boxFactory({fill:"#FFFFFF",stroke:"#000000","stroke-width":1,rx:10,ry:10}),labelAttrs:lt}},dividers:{"":{labelAttrs:dt,padding:{top:2,left:5,right:5,bottom:2},extend:0,margin:0,render:()=>({})},line:{labelAttrs:dt,padding:{top:2,left:5,right:5,bottom:2},extend:10,margin:0,render:this.renderLineDivider.bind(this,{lineAttrs:{stroke:"#000000"}})},delay:{labelAttrs:dt,padding:{top:2,left:5,right:5,bottom:2},extend:0,margin:0,render:this.renderDelayDivider.bind(this,{dotSize:1,gapSize:2})},tear:{labelAttrs:dt,padding:{top:2,left:5,right:5,bottom:2},extend:10,margin:10,render:this.renderTearDivider.bind(this,{fadeBegin:5,fadeSize:10,zigWidth:6,zigHeight:1,lineAttrs:{stroke:"#000000"}})}}})}}const ct="Helvetica,Arial,Liberation Sans,sans-serif",ut=1.3,pt=new rt(10,1),ft={"font-family":ct,"font-size":8,"line-height":ut},mt={"font-family":ct,"font-size":8,"line-height":ut,"text-anchor":"middle"};class bt extends it{constructor(t){super(t);const e={padding:{top:3,bottom:4},tag:{padding:{top:2,left:5,right:5,bottom:1},boxRenderer:this.renderTag.bind(this,{fill:"#FFFFFF",stroke:"#000000","stroke-width":2,rx:3,ry:3}),labelAttrs:{"font-family":ct,"font-weight":"bold","font-size":9,"line-height":ut,"text-anchor":"left"}},label:{minHeight:5,padding:{top:2,left:5,right:3,bottom:1},labelAttrs:{"font-family":ct,"font-size":8,"line-height":ut,"text-anchor":"left"}}};Object.assign(this,{titleMargin:12,outerMargin:5,agentMargin:8,actionMargin:5,minActionMargin:5,agentLineHighlightRadius:4,agentCap:{box:{padding:{top:1,left:3,right:3,bottom:1},arrowBottom:11.1,boxAttrs:{fill:"#FFFFFF",stroke:"#000000","stroke-width":3,rx:4,ry:4},labelAttrs:{"font-family":ct,"font-weight":"bold","font-size":14,"line-height":ut,"text-anchor":"middle"}},database:{padding:{top:4,left:3,right:3,bottom:0},arrowBottom:11.1,boxRenderer:this.renderDB.bind(this,{fill:"#FFFFFF",stroke:"#000000","stroke-width":3,"db-z":2}),labelAttrs:{"font-family":ct,"font-weight":"bold","font-size":14,"line-height":ut,"text-anchor":"middle"}},cross:{size:20,render:t.crossFactory({fill:"none",stroke:"#000000","stroke-width":3,"stroke-linecap":"round"})},bar:{height:4,render:t.boxFactory({fill:"#000000",stroke:"#000000","stroke-width":3,rx:2,ry:2})},fade:{width:5,height:10,extend:1},none:{height:10}},connect:{loopbackRadius:8,line:{solid:{attrs:{fill:"none",stroke:"#000000","stroke-width":3},renderFlat:this.renderFlatConnect.bind(this,null),renderRev:this.renderRevConnect.bind(this,null)},dash:{attrs:{fill:"none",stroke:"#000000","stroke-width":3,"stroke-dasharray":"10, 4"},renderFlat:this.renderFlatConnect.bind(this,null),renderRev:this.renderRevConnect.bind(this,null)},wave:{attrs:{fill:"none",stroke:"#000000","stroke-width":3,"stroke-linejoin":"round","stroke-linecap":"round"},renderFlat:this.renderFlatConnect.bind(this,pt),renderRev:this.renderRevConnect.bind(this,pt)}},arrow:{single:{width:10,height:12,render:this.renderArrowHead.bind(this),attrs:{fill:"#000000",stroke:"#000000","stroke-width":3,"stroke-linejoin":"round"}},double:{width:10,height:12,render:this.renderArrowHead.bind(this),attrs:{fill:"none",stroke:"#000000","stroke-width":3,"stroke-linejoin":"round","stroke-linecap":"round"}},cross:{short:10,radius:5,render:t.crossFactory({fill:"none",stroke:"#000000","stroke-width":3,"stroke-linejoin":"round","stroke-linecap":"round"})}},label:{padding:7,margin:{top:2,bottom:3},attrs:{"font-family":ct,"font-size":8,"line-height":ut,"text-anchor":"middle"},loopbackAttrs:{"font-family":ct,"font-size":8,"line-height":ut}},source:{radius:5,render:t.circleFactory({fill:"#000000",stroke:"#000000","stroke-width":3})},mask:{padding:{top:1,left:5,right:5,bottom:3}}},titleAttrs:{"font-family":ct,"font-weight":"bolder","font-size":20,"line-height":ut,"text-anchor":"middle",class:"title"},agentLineAttrs:{"":{fill:"none",stroke:"#000000","stroke-width":3},red:{stroke:"#DD0000"}},blocks:{ref:{margin:{top:0,bottom:0},boxRenderer:this.renderRef.bind(this,{fill:"#FFFFFF",stroke:"#000000","stroke-width":4,rx:5,ry:5}),section:e},"":{margin:{top:0,bottom:0},boxRenderer:t.boxFactory({fill:"none",stroke:"#000000","stroke-width":4,rx:5,ry:5}),collapsedBoxRenderer:this.renderRef.bind(this,{fill:"#FFFFFF",stroke:"#000000","stroke-width":4,rx:5,ry:5}),section:e,sepRenderer:t.lineFactory({stroke:"#000000","stroke-width":2,"stroke-dasharray":"5, 3"})}},notes:{text:{margin:{top:0,left:2,right:2,bottom:0},padding:{top:2,left:2,right:2,bottom:2},overlap:{left:10,right:10},boxRenderer:t.boxFactory({fill:"#FFFFFF"}),labelAttrs:ft},note:{margin:{top:0,left:5,right:5,bottom:0},padding:{top:3,left:3,right:10,bottom:3},overlap:{left:10,right:10},boxRenderer:t.noteFactory({fill:"#FFFFFF",stroke:"#000000","stroke-width":2,"stroke-linejoin":"round"},{fill:"none",stroke:"#000000","stroke-width":1}),labelAttrs:ft},state:{margin:{top:0,left:5,right:5,bottom:0},padding:{top:5,left:7,right:7,bottom:5},overlap:{left:10,right:10},boxRenderer:t.boxFactory({fill:"#FFFFFF",stroke:"#000000","stroke-width":3,rx:10,ry:10}),labelAttrs:ft}},dividers:{"":{labelAttrs:mt,padding:{top:2,left:5,right:5,bottom:2},extend:0,margin:0,render:()=>({})},line:{labelAttrs:mt,padding:{top:2,left:5,right:5,bottom:2},extend:10,margin:0,render:this.renderLineDivider.bind(this,{lineAttrs:{stroke:"#000000","stroke-width":2,"stroke-linecap":"round"}})},delay:{labelAttrs:mt,padding:{top:2,left:5,right:5,bottom:2},extend:0,margin:0,render:this.renderDelayDivider.bind(this,{dotSize:3,gapSize:3})},tear:{labelAttrs:mt,padding:{top:2,left:5,right:5,bottom:2},extend:10,margin:10,render:this.renderTearDivider.bind(this,{fadeBegin:5,fadeSize:10,zigWidth:6,zigHeight:1,lineAttrs:{stroke:"#000000","stroke-width":2,"stroke-linejoin":"round"}})}}})}}class yt{constructor(){this.listeners=new Map,this.forwards=new Set}addEventListener(t,e){const n=this.listeners.get(t);n?n.push(e):this.listeners.set(t,[e])}removeEventListener(t,e){const n=this.listeners.get(t);if(!n)return;const s=n.indexOf(e);-1!==s&&n.splice(s,1)}on(t,e){return this.addEventListener(t,e),this}off(t,e){return this.removeEventListener(t,e),this}countEventListeners(t){return(this.listeners.get(t)||[]).length}removeAllEventListeners(t){t?this.listeners.delete(t):this.listeners.clear()}addEventForwarding(t){this.forwards.add(t)}removeEventForwarding(t){this.forwards.delete(t)}removeAllEventForwardings(){this.forwards.clear()}trigger(t,e=[]){(this.listeners.get(t)||[]).forEach(t=>t(...e)),this.forwards.forEach(n=>n.trigger(t,e))}}const xt="undefined"==typeof window,wt=!xt&&/^((?!chrome|android).)*safari/i.test(window.navigator.userAgent),kt=!xt&&void 0!==window.InstallTrigger;class vt{constructor(){this.latestSVG=null,this.latestInternalSVG=null,this.canvas=null,this.context=null,this.indexPNG=0,this.latestPNGIndex=0,this.latestPNG=null}getSVGContent(t){let e=t.dom().outerHTML;return e=e.replace(/^{this.canvas.width=s,this.canvas.height=r,this.context.drawImage(i,0,0,s,r),a&&document.body.removeChild(a),n(this.canvas)};i.addEventListener("load",()=>{a?setTimeout(o,50):o()},{once:!0}),i.src=this.getSVGURL(t)}getPNGBlob(t,e,n){this.getCanvas(t,e,t=>{t.toBlob(n,"image/png")})}getPNGURL(t,e,n){++this.indexPNG;const s=this.indexPNG;this.getPNGBlob(t,e,t=>{const e=URL.createObjectURL(t);s>=this.latestPNGIndex?(this.latestPNG&&URL.revokeObjectURL(this.latestPNG),this.latestPNG=e,this.latestPNGIndex=s,n(e,!0)):(n(e,!1),URL.revokeObjectURL(e))})}}class At{constructor({visible:t=!1,locked:e=!1,blocked:n=!1,highlighted:s=!1,group:r=null,covered:i=!1}={}){this.visible=t,this.locked=e,this.blocked=n,this.highlighted=s,this.group=r,this.covered=i}}At.LOCKED=new At({locked:!0}),At.DEFAULT=new At;const Ft={equals:(t,e)=>t.name===e.name,hasFlag:(t,e=!0)=>n=>n.flags.includes(t)===e},St={addNearby:(e,n,s,r)=>{const i=t(e,n,St.equals);-1===i?e.push(s):e.splice(i+r,0,s)},equals:(t,e)=>t.id===e.id,hasIntersection:(e,n)=>(function(e,n,s=null){for(let r=0;rt(e,n,St.equals),make:(t,{anchorRight:e=!1,isVirtualSource:n=!1}={})=>({anchorRight:e,id:t,isVirtualSource:n,options:[]})},Mt={"note left":[St.make("[")],"note over":[St.make("["),St.make("]")],"note right":[St.make("]")]},Rt=["[","]"],Ct={"agent begin":{check:["mode"],merge:["agentIDs"],siblings:new Set(["agent highlight"])},"agent end":{check:["mode"],merge:["agentIDs"],siblings:new Set(["agent highlight"])},"agent highlight":{check:["highlighted"],merge:["agentIDs"],siblings:new Set(["agent begin","agent end"])}};class It{constructor(){this.agentStates=new Map,this.agentAliases=new Map,this.activeGroups=new Map,this.gAgents=[],this.labelPattern=null,this.nextID=0,this.nesting=[],this.markers=new Set,this.currentSection=null,this.currentNest=null,this.stageHandlers={"agent begin":this.handleAgentBegin.bind(this),"agent define":this.handleAgentDefine.bind(this),"agent end":this.handleAgentEnd.bind(this),"agent options":this.handleAgentOptions.bind(this),async:this.handleAsync.bind(this),"block begin":this.handleBlockBegin.bind(this),"block end":this.handleBlockEnd.bind(this),"block split":this.handleBlockSplit.bind(this),connect:this.handleConnect.bind(this),"connect-delay-begin":this.handleConnectDelayBegin.bind(this),"connect-delay-end":this.handleConnectDelayEnd.bind(this),divider:this.handleDivider.bind(this),"group begin":this.handleGroupBegin.bind(this),"label pattern":this.handleLabelPattern.bind(this),mark:this.handleMark.bind(this),"note between":this.handleNote.bind(this),"note left":this.handleNote.bind(this),"note over":this.handleNote.bind(this),"note right":this.handleNote.bind(this)},this.expandGroupedGAgent=this.expandGroupedGAgent.bind(this),this.handleStage=this.handleStage.bind(this),this.toGAgent=this.toGAgent.bind(this),this.endGroup=this.endGroup.bind(this)}_aliasInUse(t){const e=this.agentAliases.get(t);return!(!e||e===t)||this.gAgents.some(e=>e.id===t)}toGAgent({name:t,alias:e,flags:n}){if(e){if(this.agentAliases.has(t))throw new Error("Cannot alias "+t+"; it is already an alias");if(this._aliasInUse(e))throw new Error("Cannot use "+e+" as an alias; it is already in use");this.agentAliases.set(e,t)}return St.make(this.agentAliases.get(t)||t,{isVirtualSource:n.includes("source")})}addStage(t,e=!0){t&&(void 0===t.ln&&(t.ln=this.latestLine),this.currentSection.stages.push(t),e&&(this.currentNest.hasContent=!0))}addParallelStages(t){const e=t.filter(t=>Boolean(t));0!==e.length&&(1!==e.length?(e.forEach(t=>{void 0===t.ln&&(t.ln=this.latestLine)}),this.addStage({stages:e,type:"parallel"})):this.addStage(e[0]))}defineGAgents(t){e(this.currentNest.gAgents,t.filter(t=>!Rt.includes(t.id)),St.equals),e(this.gAgents,t,St.equals)}getGAgentState(t){return this.agentStates.get(t.id)||At.DEFAULT}updateGAgentState(t,e){const n=this.agentStates.get(t.id);n?Object.assign(n,e):this.agentStates.set(t.id,new At(e))}replaceGAgentState(t,e){this.agentStates.set(t.id,e)}validateGAgents(t,{allowGrouped:e=!1,allowCovered:n=!1,allowVirtual:s=!1}={}){t.forEach(t=>{const r=this.getGAgentState(t),i=t.id;if(function(t){return t.blocked&&null===t.group}(r))throw new Error("Duplicate agent name: "+i);if(!n&&r.covered)throw new Error("Agent "+i+" is hidden behind group");if(!e&&null!==r.group)throw new Error("Agent "+i+" is in a group");if(!s&&t.isVirtualSource)throw new Error("Cannot use message source here");if(function(t){return t.startsWith("__")}(i))throw new Error(i+" is a reserved name")})}setGAgentVis(t,e,n,s=!1){const r=new Set,i=t.filter(t=>{if(r.has(t.id))return!1;r.add(t.id);const n=this.getGAgentState(t);if(n.locked||n.blocked){if(s)throw new Error("Cannot begin/end agent: "+t.id);return!1}return n.visible!==e});return 0===i.length?null:(i.forEach(t=>{this.updateGAgentState(t,{visible:e})}),this.defineGAgents(i),{agentIDs:i.map(t=>t.id),mode:n,type:e?"agent begin":"agent end"})}setGAgentHighlight(t,e,n=!1){const s=t.filter(t=>{const s=this.getGAgentState(t);if(s.locked||s.blocked){if(n)throw new Error("Cannot highlight agent: "+t.id);return!1}return s.visible&&s.highlighted!==e});return 0===s.length?null:(s.forEach(t=>{this.updateGAgentState(t,{highlighted:e})}),{agentIDs:s.map(t=>t.id),highlighted:e,type:"agent highlight"})}_makeSection(t,e){return{delayedConnections:new Map,header:t,stages:e}}_checkSectionEnd(){const t=this.currentSection.delayedConnections;if(t.size>0){const e=t.values().next().value;throw new Error('Unused delayed connection "'+e.tag+'" at line '+(e.ln+1))}}beginNested(t,{tag:e,label:n,name:s,ln:r}){const i=St.make(s+"[",{anchorRight:!0}),a=St.make(s+"]"),o=[i,a],h=[];return this.currentSection=this._makeSection({blockType:t,canHide:!0,label:this.textFormatter(n),left:i.id,ln:r,right:a.id,tag:this.textFormatter(e),type:"block begin"},h),this.currentNest={blockType:t,gAgents:o,hasContent:!1,leftGAgent:i,rightGAgent:a,sections:[this.currentSection]},this.replaceGAgentState(i,At.LOCKED),this.replaceGAgentState(a,At.LOCKED),this.nesting.push(this.currentNest),{stages:h}}nextBlockName(){const t="__BLOCK"+this.nextID;return++this.nextID,t}nextVirtualAgentName(){const t="__"+this.nextID;return++this.nextID,t}handleBlockBegin({ln:t,blockType:e,tag:n,label:s}){this.beginNested(e,{label:s,ln:t,name:this.nextBlockName(),tag:n})}handleBlockSplit({ln:t,blockType:e,tag:n,label:s}){if("if"!==this.currentNest.blockType)throw new Error('Invalid block nesting ("else" inside '+this.currentNest.blockType+")");this._checkSectionEnd(),this.currentSection=this._makeSection({blockType:e,label:this.textFormatter(s),left:this.currentNest.leftGAgent.id,ln:t,right:this.currentNest.rightGAgent.id,tag:this.textFormatter(n),type:"block split"},[]),this.currentNest.sections.push(this.currentSection)}handleBlockEnd(){if(this.nesting.length<=1)throw new Error('Invalid block nesting (too many "end"s)');this._checkSectionEnd();const t=this.nesting.pop();if(this.currentNest=r(this.nesting),this.currentSection=r(this.currentNest.sections),!t.hasContent)throw new Error("Empty block");this.defineGAgents(t.gAgents),p(this.gAgents,t.leftGAgent,t.rightGAgent,t.gAgents),t.sections.forEach(t=>{this.currentSection.stages.push(t.header),this.currentSection.stages.push(...t.stages)}),this.addStage({left:t.leftGAgent.id,right:t.rightGAgent.id,type:"block end"})}makeGroupDetails(t,e){const s=t.map(this.toGAgent);if(this.validateGAgents(s),this.agentStates.has(e))throw new Error("Duplicate agent name: "+e);const r=this.nextBlockName(),i=St.make(r+"[",{anchorRight:!0}),a=St.make(r+"]");this.replaceGAgentState(i,At.LOCKED),this.replaceGAgentState(a,At.LOCKED),this.updateGAgentState(St.make(e),{blocked:!0,group:e}),this.defineGAgents([...s,i,a]);const{indexL:o,indexR:h}=p(this.gAgents,i,a,s),l=[],d=s.slice();for(let t=o+1;t{this.updateGAgentState(t,{group:r})}),i.gAgentsCovered.forEach(t=>{this.updateGAgentState(t,{covered:!0})}),this.activeGroups.set(r,i),this.addStage(this.setGAgentVis(i.gAgents,!0,"box")),this.addStage({blockType:e,canHide:!1,label:this.textFormatter(s),left:i.leftGAgent.id,right:i.rightGAgent.id,tag:this.textFormatter(n),type:"block begin"})}endGroup({name:t}){const e=this.activeGroups.get(t);return e?(this.activeGroups.delete(t),e.gAgentsContained.forEach(t=>{this.updateGAgentState(t,{group:null})}),e.gAgentsCovered.forEach(t=>{this.updateGAgentState(t,{covered:!1})}),this.updateGAgentState(St.make(t),{group:null}),{left:e.leftGAgent.id,right:e.rightGAgent.id,type:"block end"}):null}handleMark({name:t}){this.markers.add(t),this.addStage({name:t,type:"mark"},!1)}handleDivider({mode:t,height:e,label:n}){this.addStage({formattedLabel:this.textFormatter(n),height:e,mode:t,type:"divider"},!1)}handleAsync({target:t}){if(""!==t&&!this.markers.has(t))throw new Error("Unknown marker: "+t);this.addStage({target:t,type:"async"},!1)}handleLabelPattern({pattern:t}){this.labelPattern=t.slice();for(let t=0;t{"string"==typeof t?e+=t:void 0!==t.token?e+=n[t.token]:void 0!==t.current&&(e+=t.current.toFixed(t.dp),t.current+=t.inc)}),e}expandGroupedGAgent(t){const{group:e}=this.getGAgentState(t);if(!e)return[t];const n=this.activeGroups.get(e);return[n.leftGAgent,n.rightGAgent]}expandGroupedGAgentConnection(t){const e=this.expandGroupedGAgent(t[0]),n=this.expandGroupedGAgent(t[1]);let s=St.indexOf(this.gAgents,e[0]),i=St.indexOf(this.gAgents,n[0]);return-1===s&&(s=e[0].isVirtualSource?-1:this.gAgents.length),-1===i&&(i=this.gAgents.length),s===i?[r(e),r(n)]:s!t.isVirtualSource));const s=t.filter(Ft.hasFlag("begin",!1)).map(this.toGAgent).filter(t=>!t.isVirtualSource);return this.addStage(this.setGAgentVis(s,!0,"box")),{flags:e,gAgents:n}}_makeConnectParallelStages(t,e){return[this.setGAgentVis(t.beginGAgents,!0,"box",!0),this.setGAgentHighlight(t.startGAgents,!0,!0),e,this.setGAgentHighlight(t.stopGAgents,!1,!0),this.setGAgentVis(t.endGAgents,!1,"cross",!0)]}_isSelfConnect(t){const e=t.map(this.toGAgent),n=this.expandGroupedGAgentConnection(e);return n[0].id===n[1].id&&!n.some(t=>t.isVirtualSource)}handleConnect({agents:t,label:e,options:n}){if(this._isSelfConnect(t)){const s={};return this.handleConnectDelayBegin({agent:t[0],ln:0,options:n,tag:s}),void this.handleConnectDelayEnd({agent:t[1],label:e,options:n,tag:s})}let{flags:s,gAgents:r}=this._handlePartialConnect(t);r=this.expandGroupedGAgentConnection(r);const i={agentIDs:(r=this.expandVirtualSourceAgents(r)).map(t=>t.id),label:this.textFormatter(this.applyLabelPattern(e)),options:n,type:"connect"};this.addParallelStages(this._makeConnectParallelStages(s,i))}handleConnectDelayBegin({agent:t,tag:e,options:n,ln:s}){const r=this.currentSection.delayedConnections;if(r.has(e))throw new Error('Duplicate delayed connection "'+e+'"');const{flags:i,gAgents:a}=this._handlePartialConnect([t]),o=this.nextVirtualAgentName(),h={agentIDs:null,label:null,options:n,tag:o,type:"connect-delay-begin"};r.set(e,{connectStage:h,gAgents:a,ln:s,tag:e,uniqueTag:o}),this.addParallelStages(this._makeConnectParallelStages(i,h))}handleConnectDelayEnd({agent:t,tag:e,label:n,options:s}){const r=this.currentSection.delayedConnections,i=r.get(e);if(!i)throw new Error('Unknown delayed connection "'+e+'"');let{flags:a,gAgents:o}=this._handlePartialConnect([t]);o=this.expandGroupedGAgentConnection([...i.gAgents,...o]),o=this.expandVirtualSourceAgents(o);let h=i.connectStage.options;if(h.line!==s.line)throw new Error("Mismatched delayed connection arrows");s.right&&(h=Object.assign({},h,{right:s.right})),Object.assign(i.connectStage,{agentIDs:o.map(t=>t.id),label:this.textFormatter(this.applyLabelPattern(n)),options:h});const l={tag:i.uniqueTag,type:"connect-delay-end"};this.addParallelStages(this._makeConnectParallelStages(a,l)),r.delete(e)}handleNote({type:t,agents:e,mode:n,label:s}){let r=null;r=0===e.length?Mt[t]||[]:e.map(this.toGAgent),this.validateGAgents(r,{allowGrouped:!0});const i=(r=a(r,this.expandGroupedGAgent)).map(t=>t.id),o=new Set(i).size;if("note between"===t&&o<2)throw new Error("note between requires at least 2 agents");this.addStage(this.setGAgentVis(r,!0,"box")),this.defineGAgents(r),this.addStage({agentIDs:i,label:this.textFormatter(s),mode:n,type:t})}handleAgentDefine({agents:t}){const n=t.map(this.toGAgent);this.validateGAgents(n,{allowCovered:!0,allowGrouped:!0}),e(this.gAgents,n,St.equals)}handleAgentOptions({agent:t,options:n}){const s=this.toGAgent(t),r=[s];this.validateGAgents(r,{allowCovered:!0,allowGrouped:!0}),e(this.gAgents,r,St.equals),this.gAgents.filter(({id:t})=>t===s.id).forEach(t=>{e(t.options,n)})}handleAgentBegin({agents:t,mode:e}){const n=t.map(this.toGAgent);this.validateGAgents(n),this.addStage(this.setGAgentVis(n,!0,e,!0))}handleAgentEnd({agents:t,mode:e}){const n=t.filter(t=>this.activeGroups.has(t.name)),s=t.filter(t=>!this.activeGroups.has(t.name)).map(this.toGAgent);this.validateGAgents(s),this.addParallelStages([this.setGAgentHighlight(s,!1),this.setGAgentVis(s,!1,e,!0),...n.map(this.endGroup)])}handleStage(t){this.latestLine=t.ln;try{const e=this.stageHandlers[t.type];if(!e)throw new Error("Unknown command: "+t.type);e(t)}catch(e){if("object"==typeof e&&e.message)throw e.message+=" at line "+(t.ln+1),e}}_reset(){this.agentStates.clear(),this.markers.clear(),this.agentAliases.clear(),this.activeGroups.clear(),this.gAgents.length=0,this.nextID=0,this.nesting.length=0,this.labelPattern=[{token:"label"}]}_finalise(t){p(this.gAgents,this.currentNest.leftGAgent,this.currentNest.rightGAgent),function(t){let e=[],n=new Set;for(let s=0;s{t.formattedLabel=this.textFormatter(t.id)})}generate({stages:t,meta:e={}}){this._reset(),this.textFormatter=e.textFormatter;const n=this.beginNested("global",{label:"",ln:0,name:"",tag:""});if(t.forEach(this.handleStage),1!==this.nesting.length)throw new Error("Unterminated section at line "+(this.currentSection.header.ln+1));if(this.activeGroups.size>0)throw new Error("Unterminated group");this._checkSectionEnd();const s=e.terminators||"none";return this.addParallelStages([this.setGAgentHighlight(this.gAgents,!1),this.setGAgentVis(this.gAgents,!1,s)]),this._finalise(n),function(t,e){for(let n=0;n({})},line:{labelAttrs:Nt,padding:{top:2,left:5,right:5,bottom:2},extend:8,margin:0,render:this.renderLineDivider.bind(this,{lineAttrs:{stroke:"#000000"}})},delay:{labelAttrs:Nt,padding:{top:2,left:5,right:5,bottom:2},extend:0,margin:0,render:this.renderDelayDivider.bind(this,{dotSize:2,gapSize:2})},tear:{labelAttrs:Nt,padding:{top:2,left:5,right:5,bottom:2},extend:8,margin:8,render:this.renderTearDivider.bind(this,{fadeBegin:4,fadeSize:4,zigWidth:4,zigHeight:1,lineAttrs:{stroke:"#000000"}})}}})}}const Ot={type:"error line-error",suggest:[],then:{"":0}},Bt=["database","red"],Tt=(()=>{function t(t,e=1){return{type:"variable",suggest:[{known:"Agent"}],then:Object.assign({},t,{"":0,",":{type:"operator",then:{"":e}}})}}function e(t){return{type:"keyword",suggest:[t+" of ",t+": "],then:{of:{type:"keyword",then:{"":h}},":":{type:"operator",then:{"":i}},"":h}}}function n({exit:t,sourceExit:e,blankExit:n}){const s={type:"operator",then:{"+":Ot,"-":Ot,"*":Ot,"!":Ot,"":t}};return{"+":{type:"operator",then:{"+":Ot,"-":Ot,"*":s,"!":Ot,"":t}},"-":{type:"operator",then:{"+":Ot,"-":Ot,"*":s,"!":{type:"operator",then:{"+":Ot,"-":Ot,"*":Ot,"!":Ot,"":t}},"":t}},"*":{type:"operator",then:Object.assign({"+":s,"-":s,"*":Ot,"!":Ot,"":t},e||t)},"!":s,"":n||t}}const s={type:"",suggest:["\n"],then:{}},r={type:"",suggest:[],then:{}},i=f({"\n":s}),a={type:"operator",then:{"":i,"\n":r}},o=t({"\n":s,as:{type:"keyword",then:{"":{type:"variable",suggest:[{known:"Agent"}],then:{"":0,",":{type:"operator",then:{"":3}},"\n":s}}}}}),h=t({":":a}),l={type:"variable",suggest:[{known:"Agent"}],then:{"":0,":":{type:"operator",then:{"":i,"\n":r}},"\n":s}},d={":":{type:"operator",then:{"":f({as:{type:"keyword",then:{"":{type:"variable",suggest:[{known:"Agent"}],then:{"":0,"\n":s}}}}})}}},g={type:"keyword",then:Object.assign({over:{type:"keyword",then:{"":t(d)}}},d)},c={"\n":s,":":{type:"operator",then:{"":i,"\n":r}},with:{type:"keyword",suggest:["with height "],then:{height:{type:"keyword",then:{"":{type:"number",suggest:["6 ","30 "],then:{"\n":s,":":{type:"operator",then:{"":i,"\n":r}}}}}}}}},u=function(t,e,n){const s=Object.assign({},n);return e.forEach(e=>{s[e]={type:t,then:n}}),s}("keyword",["a","an"],function(t,e,n){const s={},r=Object.assign({},n);return e.forEach(e=>{s[e]={type:t,then:r},r[e]=0}),s}("keyword",Bt,{"\n":s})),p={type:"keyword",then:{"":i,":":{type:"operator",then:{"":i}},"\n":s}},m={title:{type:"keyword",then:{"":i}},theme:{type:"keyword",then:{"":{type:"string",suggest:[{global:"themes",suffix:"\n"}],then:{"":0,"\n":s}}}},headers:{type:"keyword",then:{none:{type:"keyword",then:{}},cross:{type:"keyword",then:{}},box:{type:"keyword",then:{}},fade:{type:"keyword",then:{}},bar:{type:"keyword",then:{}}}},terminators:{type:"keyword",then:{none:{type:"keyword",then:{}},cross:{type:"keyword",then:{}},box:{type:"keyword",then:{}},fade:{type:"keyword",then:{}},bar:{type:"keyword",then:{}}}},divider:{type:"keyword",then:Object.assign({line:{type:"keyword",then:c},space:{type:"keyword",then:c},delay:{type:"keyword",then:c},tear:{type:"keyword",then:c}},c)},define:{type:"keyword",then:{"":o,as:Ot}},begin:{type:"keyword",then:{"":o,reference:g,as:Ot}},end:{type:"keyword",then:{"":o,as:Ot,"\n":s}},if:p,else:{type:"keyword",suggest:["else\n","else if: "],then:{if:{type:"keyword",suggest:["if: "],then:{"":i,":":{type:"operator",then:{"":i}}}},"\n":s}},repeat:p,group:p,note:{type:"keyword",then:{over:{type:"keyword",then:{"":h}},left:e("left"),right:e("right"),between:{type:"keyword",then:{"":t({":":Ot},h)}}}},state:{type:"keyword",suggest:["state over "],then:{over:{type:"keyword",then:{"":{type:"variable",suggest:[{known:"Agent"}],then:{"":0,",":Ot,":":a}}}}}},text:{type:"keyword",then:{left:e("left"),right:e("right")}},autolabel:{type:"keyword",then:{off:{type:"keyword",then:{}},"":f({"\n":s},[{v:"