Add parts library
This commit is contained in:
parent
25ffd6a904
commit
85b4f99ccd
|
@ -28,6 +28,18 @@ define([
|
||||||
events.forEach((event) => element.addEventListener(event, fn));
|
events.forEach((event) => element.addEventListener(event, fn));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function simplifyPreview(code) {
|
||||||
|
code = code.replace(/\{Agent([0-9]*)\}/g, (match, num) => {
|
||||||
|
if(num === undefined) {
|
||||||
|
return 'A';
|
||||||
|
} else {
|
||||||
|
return String.fromCharCode('A'.charCodeAt(0) + Number(num) - 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
code = code.replace(/[{}]/g, '');
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
return class Interface {
|
return class Interface {
|
||||||
constructor({
|
constructor({
|
||||||
parser,
|
parser,
|
||||||
|
@ -36,6 +48,7 @@ define([
|
||||||
exporter,
|
exporter,
|
||||||
defaultCode = '',
|
defaultCode = '',
|
||||||
localStorage = '',
|
localStorage = '',
|
||||||
|
library = null,
|
||||||
}) {
|
}) {
|
||||||
this.parser = parser;
|
this.parser = parser;
|
||||||
this.generator = generator;
|
this.generator = generator;
|
||||||
|
@ -43,6 +56,7 @@ define([
|
||||||
this.exporter = exporter;
|
this.exporter = exporter;
|
||||||
this.defaultCode = defaultCode;
|
this.defaultCode = defaultCode;
|
||||||
this.localStorage = localStorage;
|
this.localStorage = localStorage;
|
||||||
|
this.library = library;
|
||||||
this.minScale = 1.5;
|
this.minScale = 1.5;
|
||||||
|
|
||||||
this.debounced = null;
|
this.debounced = null;
|
||||||
|
@ -133,8 +147,8 @@ define([
|
||||||
code.on('endCompletion', () => {
|
code.on('endCompletion', () => {
|
||||||
lastKey = 0;
|
lastKey = 0;
|
||||||
});
|
});
|
||||||
code.on('change', (cm) => {
|
code.on('change', (cm, change) => {
|
||||||
if(cm.state.completionActive) {
|
if(cm.state.completionActive || change.origin === 'library') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(lastKey === 13 || lastKey === 8) {
|
if(lastKey === 13 || lastKey === 8) {
|
||||||
|
@ -193,12 +207,43 @@ define([
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
build(container) {
|
buildLibrary(container) {
|
||||||
const codePane = makeNode('div', {'class': 'pane-code'});
|
this.library.forEach((lib) => {
|
||||||
const viewPane = makeNode('div', {'class': 'pane-view'});
|
const hold = makeNode('div', {
|
||||||
this.errorPane = makeNode('div', {'class': 'pane-error'});
|
'class': 'library-item',
|
||||||
|
});
|
||||||
|
const holdInner = makeNode('div', {
|
||||||
|
'title': lib.title || lib.code,
|
||||||
|
});
|
||||||
|
hold.appendChild(holdInner);
|
||||||
|
hold.addEventListener(
|
||||||
|
'click',
|
||||||
|
this.addCodeBlock.bind(this, lib.code)
|
||||||
|
);
|
||||||
|
container.appendChild(hold);
|
||||||
|
try {
|
||||||
|
const preview = simplifyPreview(lib.preview || lib.code);
|
||||||
|
const parsed = this.parser.parse(preview);
|
||||||
|
const generated = this.generator.generate(parsed);
|
||||||
|
const rendering = this.renderer.clone();
|
||||||
|
holdInner.appendChild(rendering.svg());
|
||||||
|
rendering.render(generated);
|
||||||
|
} catch(e) {
|
||||||
|
hold.setAttribute('class', 'library-item broken');
|
||||||
|
holdInner.appendChild(makeText(lib.code));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
buildErrorReport() {
|
||||||
|
this.errorMsg = makeNode('div', {'class': 'msg-error'});
|
||||||
this.errorText = makeText();
|
this.errorText = makeText();
|
||||||
this.errorPane.appendChild(this.errorText);
|
this.errorMsg.appendChild(this.errorText);
|
||||||
|
return this.errorMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
buildViewPane() {
|
||||||
|
const viewPane = makeNode('div', {'class': 'pane-view'});
|
||||||
const viewPaneScroller = makeNode('div', {
|
const viewPaneScroller = makeNode('div', {
|
||||||
'class': 'pane-view-scroller',
|
'class': 'pane-view-scroller',
|
||||||
});
|
});
|
||||||
|
@ -210,10 +255,31 @@ define([
|
||||||
viewPaneScroller.appendChild(this.viewPaneInner);
|
viewPaneScroller.appendChild(this.viewPaneInner);
|
||||||
viewPane.appendChild(this.buildOptionsLinks());
|
viewPane.appendChild(this.buildOptionsLinks());
|
||||||
viewPane.appendChild(this.buildOptionsDownloads());
|
viewPane.appendChild(this.buildOptionsDownloads());
|
||||||
|
viewPane.appendChild(this.buildErrorReport());
|
||||||
|
|
||||||
|
return viewPane;
|
||||||
|
}
|
||||||
|
|
||||||
|
build(container) {
|
||||||
|
const codePane = makeNode('div', {'class': 'pane-code'});
|
||||||
container.appendChild(codePane);
|
container.appendChild(codePane);
|
||||||
container.appendChild(this.errorPane);
|
|
||||||
container.appendChild(viewPane);
|
if(this.library !== null) {
|
||||||
|
const libPane = makeNode('div', {'class': 'pane-library'});
|
||||||
|
const libPaneScroller = makeNode('div', {
|
||||||
|
'class': 'pane-library-scroller',
|
||||||
|
});
|
||||||
|
const libPaneInner = makeNode('div', {
|
||||||
|
'class': 'pane-library-inner',
|
||||||
|
});
|
||||||
|
libPaneScroller.appendChild(libPaneInner);
|
||||||
|
libPane.appendChild(libPaneScroller);
|
||||||
|
container.appendChild(libPane);
|
||||||
|
codePane.setAttribute('class', 'pane-code reduced');
|
||||||
|
this.buildLibrary(libPaneInner);
|
||||||
|
}
|
||||||
|
|
||||||
|
container.appendChild(this.buildViewPane());
|
||||||
|
|
||||||
this.code = this.buildEditor(codePane);
|
this.code = this.buildEditor(codePane);
|
||||||
this.viewPaneInner.appendChild(this.renderer.svg());
|
this.viewPaneInner.appendChild(this.renderer.svg());
|
||||||
|
@ -222,6 +288,20 @@ define([
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addCodeBlock(block) {
|
||||||
|
const cur = this.code.getCursor('head');
|
||||||
|
const pos = {line: cur.line + ((cur.ch > 0) ? 1 : 0), ch: 0};
|
||||||
|
const lines = block.split('\n').length;
|
||||||
|
this.code.replaceRange(
|
||||||
|
block + '\n',
|
||||||
|
pos,
|
||||||
|
null,
|
||||||
|
'library'
|
||||||
|
);
|
||||||
|
this.code.setCursor({line: pos.line + lines, ch: 0});
|
||||||
|
this.code.focus();
|
||||||
|
}
|
||||||
|
|
||||||
updateMinSize(width, height) {
|
updateMinSize(width, height) {
|
||||||
const style = this.viewPaneInner.style;
|
const style = this.viewPaneInner.style;
|
||||||
style.minWidth = Math.ceil(width * this.minScale) + 'px';
|
style.minWidth = Math.ceil(width * this.minScale) + 'px';
|
||||||
|
@ -265,12 +345,12 @@ define([
|
||||||
} else {
|
} else {
|
||||||
this.errorText.nodeValue = error;
|
this.errorText.nodeValue = error;
|
||||||
}
|
}
|
||||||
this.errorPane.setAttribute('class', 'pane-error error');
|
this.errorMsg.setAttribute('class', 'msg-error error');
|
||||||
}
|
}
|
||||||
|
|
||||||
markOK() {
|
markOK() {
|
||||||
this.errorText.nodeValue = 'All OK';
|
this.errorText.nodeValue = '';
|
||||||
this.errorPane.setAttribute('class', 'pane-error ok');
|
this.errorMsg.setAttribute('class', 'msg-error');
|
||||||
}
|
}
|
||||||
|
|
||||||
update(immediate = true) {
|
update(immediate = true) {
|
||||||
|
|
151
scripts/main.js
151
scripts/main.js
|
@ -40,6 +40,156 @@
|
||||||
'\n' +
|
'\n' +
|
||||||
'terminators box\n'
|
'terminators box\n'
|
||||||
);
|
);
|
||||||
|
const library = [
|
||||||
|
{
|
||||||
|
title: 'Simple arrow',
|
||||||
|
code: '{Agent1} -> {Agent2}: {Message}',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Arrow with dotted line',
|
||||||
|
code: '{Agent1} --> {Agent2}: {Message}',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Open arrow',
|
||||||
|
code: '{Agent1} ->> {Agent2}: {Message}',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Self-connection',
|
||||||
|
code: '{Agent1} -> {Agent1}: {Message}',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Request/response pair',
|
||||||
|
code: (
|
||||||
|
'{Agent1} -> +{Agent2}: {Request}\n' +
|
||||||
|
'{Agent1} <-- -{Agent2}: {Response}'
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Inline agent creation / destruction',
|
||||||
|
code: (
|
||||||
|
'{Agent1} -> *{Agent2}: {Request}\n' +
|
||||||
|
'{Agent1} <-- !{Agent2}: {Response}'
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Agent creation / destruction',
|
||||||
|
code: (
|
||||||
|
'{Agent1} -> {Agent2}: {Request}\n' +
|
||||||
|
'{Agent1} <-- {Agent2}: {Response}\n' +
|
||||||
|
'end {Agent2}'
|
||||||
|
),
|
||||||
|
preview: (
|
||||||
|
'begin A\n' +
|
||||||
|
'::\n' +
|
||||||
|
'A -> B: Request\n' +
|
||||||
|
'A <-- B: Response\n' +
|
||||||
|
'end B'
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Conditional blocks',
|
||||||
|
code: (
|
||||||
|
'if {Condition1}\n' +
|
||||||
|
' {Agent1} -> {Agent2}\n' +
|
||||||
|
'else if {Condition2}\n' +
|
||||||
|
' {Agent1} -> {Agent2}\n' +
|
||||||
|
'else\n' +
|
||||||
|
' {Agent1} -> {Agent2}\n' +
|
||||||
|
'end'
|
||||||
|
),
|
||||||
|
preview: (
|
||||||
|
'begin A, B\n' +
|
||||||
|
'if Condition1\n' +
|
||||||
|
' A -> B\n' +
|
||||||
|
'else if Condition2\n' +
|
||||||
|
' A -> B\n' +
|
||||||
|
'else\n' +
|
||||||
|
' A -> B\n' +
|
||||||
|
'end'
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Repeated blocks',
|
||||||
|
code: (
|
||||||
|
'repeat {Condition}\n' +
|
||||||
|
' {Agent1} -> {Agent2}\n' +
|
||||||
|
'end'
|
||||||
|
),
|
||||||
|
preview: (
|
||||||
|
'begin A, B\n' +
|
||||||
|
'repeat Condition\n' +
|
||||||
|
' A -> B\n' +
|
||||||
|
'end'
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Note over agent',
|
||||||
|
code: 'note over {Agent1}: {Message}',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Note over multiple agents',
|
||||||
|
code: 'note over {Agent1}, {Agent2}: {Message}',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Note left of agent',
|
||||||
|
code: 'note left of {Agent1}: {Message}',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Note right of agent',
|
||||||
|
code: 'note right of {Agent1}: {Message}',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Note between agents',
|
||||||
|
code: 'note between {Agent1}, {Agent2}: {Message}',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'State over agent',
|
||||||
|
code: 'state over {Agent1}: {State}',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Arrows to/from the sides',
|
||||||
|
code: '[ -> {Agent1}: {Message1}\n{Agent1} -> ]: {Message2}',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Text beside the diagram',
|
||||||
|
code: 'text right: {Message}',
|
||||||
|
preview: (
|
||||||
|
'A -> B\n' +
|
||||||
|
'simultaneously:\n' +
|
||||||
|
'text right: "Message\\non the\\nside"'
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Title',
|
||||||
|
code: 'title {Title}',
|
||||||
|
preview: 'title Title\nA -> B',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Chunky theme',
|
||||||
|
code: 'theme chunky',
|
||||||
|
preview: 'theme chunky\nA -> B',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Cross terminators',
|
||||||
|
code: 'terminators cross',
|
||||||
|
preview: 'A -> B\nterminators cross',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Fade terminators',
|
||||||
|
code: 'terminators fade',
|
||||||
|
preview: 'A -> B\nterminators fade',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Bar terminators',
|
||||||
|
code: 'terminators bar',
|
||||||
|
preview: 'A -> B\nterminators bar',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Box terminators',
|
||||||
|
code: 'terminators box',
|
||||||
|
preview: 'A -> B\nterminators box',
|
||||||
|
},
|
||||||
|
];
|
||||||
const ui = new Interface({
|
const ui = new Interface({
|
||||||
defaultCode,
|
defaultCode,
|
||||||
parser: new Parser(),
|
parser: new Parser(),
|
||||||
|
@ -49,6 +199,7 @@
|
||||||
new ChunkyTheme(),
|
new ChunkyTheme(),
|
||||||
]}),
|
]}),
|
||||||
exporter: new Exporter(),
|
exporter: new Exporter(),
|
||||||
|
library,
|
||||||
localStorage: 'src',
|
localStorage: 'src',
|
||||||
});
|
});
|
||||||
ui.build(document.body);
|
ui.build(document.body);
|
||||||
|
|
|
@ -135,6 +135,15 @@ define([
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clone({namespace = null} = {}) {
|
||||||
|
return new Renderer({
|
||||||
|
themes: this.getThemes(),
|
||||||
|
namespace,
|
||||||
|
components: this.components,
|
||||||
|
SVGTextBlockClass: this.SVGTextBlockClass,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
buildStaticElements() {
|
buildStaticElements() {
|
||||||
this.base = svg.makeContainer();
|
this.base = svg.makeContainer();
|
||||||
|
|
||||||
|
@ -625,6 +634,10 @@ define([
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getThemes() {
|
||||||
|
return this.getThemeNames().map((name) => this.themes.get(name));
|
||||||
|
}
|
||||||
|
|
||||||
getAgentX(name) {
|
getAgentX(name) {
|
||||||
return this.agentInfos.get(name).x;
|
return this.agentInfos.get(name).x;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,12 +7,16 @@ html, body {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 100px;
|
bottom: 0;
|
||||||
width: 30%;
|
width: 30%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border-right: 1px solid #808080;
|
border-right: 1px solid #808080;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pane-code.reduced {
|
||||||
|
bottom: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
.pane-code .CodeMirror {
|
.pane-code .CodeMirror {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -67,29 +71,88 @@ html, body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pane-error {
|
.pane-library {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
width: 30%;
|
width: 30%;
|
||||||
|
height: 200px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: #EEEEEE;
|
||||||
|
border-top: 1px solid #808080;
|
||||||
|
border-right: 1px solid #808080;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pane-library-scroller {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pane-library-inner {
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.library-item {
|
||||||
|
display: inline-block;
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.library-item > div {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
border: 2px solid #EEEEEE;
|
||||||
|
background: #FFFFFF;
|
||||||
|
box-sizing: border-box;
|
||||||
|
cursor: pointer;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: transform 0.2s, border-color 0.2s, background 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.library-item.broken > div {
|
||||||
|
padding: 5px;
|
||||||
|
font: 6px monospace;
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
|
|
||||||
|
.library-item svg {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.library-item:hover > div {
|
||||||
|
border-color: #FFCC00;
|
||||||
|
background: #FFFFDD;
|
||||||
|
/* position: absolute;*/
|
||||||
|
transform: scale(1.1);
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.msg-error {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 20%;
|
||||||
|
margin-top: -50px;
|
||||||
|
width: 60%;
|
||||||
height: 100px;
|
height: 100px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
background: #DDDDDD;
|
|
||||||
border-top: 1px solid #808080;
|
|
||||||
border-right: 1px solid #808080;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pane-error.ok {
|
|
||||||
color: #007700;
|
|
||||||
background: #E8EEE8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pane-error.error {
|
|
||||||
color: #770000;
|
color: #770000;
|
||||||
background: #EEE8E8;
|
background: #EEE8E8;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.5);
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: 0 0 30px rgba(0, 0, 0, 0.2);
|
||||||
|
opacity: 0.95;
|
||||||
|
z-index: 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.msg-error.error {
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.options {
|
.options {
|
||||||
|
|
Loading…
Reference in New Issue