diff --git a/readme_images.htm b/readme_images.htm
index 421275b..0da590f 100644
--- a/readme_images.htm
+++ b/readme_images.htm
@@ -14,7 +14,7 @@
form-action 'none';
">
-
Sequence Diagram
+Readme Image Generator
diff --git a/scripts/interface/Interface.js b/scripts/interface/Interface.js
index a77bc58..7ab0d19 100644
--- a/scripts/interface/Interface.js
+++ b/scripts/interface/Interface.js
@@ -109,6 +109,9 @@ define([
const code = new CodeMirror(container, {
value,
mode: 'sequence',
+ globals: {
+ themes: this.renderer.getThemeNames(),
+ },
lineNumbers: true,
showTrailingSpace: true,
extraKeys: {
diff --git a/scripts/interface/Interface_spec.js b/scripts/interface/Interface_spec.js
index 909a125..f4fd6f4 100644
--- a/scripts/interface/Interface_spec.js
+++ b/scripts/interface/Interface_spec.js
@@ -24,7 +24,11 @@ defineDescribe('Interface', ['./Interface'], (Interface) => {
agents: [],
stages: [],
});
- renderer = jasmine.createSpyObj('renderer', ['render', 'svg']);
+ renderer = jasmine.createSpyObj('renderer', [
+ 'render',
+ 'svg',
+ 'getThemeNames',
+ ]);
renderer.svg.and.returnValue(document.createElement('svg'));
container = jasmine.createSpyObj('container', ['appendChild']);
exporter = jasmine.createSpyObj('exporter', ['getSVGURL']);
diff --git a/scripts/readme_images.js b/scripts/readme_images.js
index 85fee0a..d7ca8ac 100644
--- a/scripts/readme_images.js
+++ b/scripts/readme_images.js
@@ -17,6 +17,8 @@
return o;
}
+ const PNG_RESOLUTION = 4;
+
const FAVICON_SRC = (
'theme chunky\n' +
'define ABC as A, DEF as B\n' +
@@ -47,7 +49,7 @@
results.push({
file: 'favicon.png',
code: FAVICON_SRC,
- height: 64,
+ size: {width: 16, height: 16},
});
return results;
}
@@ -61,8 +63,6 @@
}
}
- const PNG_RESOLUTION = 4;
-
/* jshint -W072 */ // Allow several required modules
requirejs([
'sequence/Parser',
@@ -91,7 +91,7 @@
status.appendChild(statusText);
document.body.appendChild(status);
- function renderSample({file, code, height}) {
+ function renderSample({file, code, size}) {
const renderer = new Renderer({themes});
const exporter = new Exporter();
@@ -124,11 +124,11 @@
const parsed = parser.parse(code);
const sequence = generator.generate(parsed);
renderer.render(sequence);
- let resolution = PNG_RESOLUTION;
- if(height) {
- resolution = height / renderer.height;
+ if(size) {
+ renderer.width = size.width;
+ renderer.height = size.height;
}
- exporter.getPNGURL(renderer, resolution, (url) => {
+ exporter.getPNGURL(renderer, PNG_RESOLUTION, (url) => {
raster.setAttribute('src', url);
downloadPNG.setAttribute('href', url);
});
diff --git a/scripts/sequence/CodeMirrorHints.js b/scripts/sequence/CodeMirrorHints.js
index 7b1da23..c15e329 100644
--- a/scripts/sequence/CodeMirrorHints.js
+++ b/scripts/sequence/CodeMirrorHints.js
@@ -1,4 +1,4 @@
-define(() => {
+define(['core/ArrayUtilities'], (array) => {
'use strict';
const TRIMMER = /^([ \t]*)(.*)$/;
@@ -32,6 +32,26 @@ define(() => {
};
}
+ function getGlobals({global, prefix = '', suffix = ''}, globals) {
+ const identified = globals[global];
+ if(!identified) {
+ return [];
+ }
+ return identified.map((item) => (prefix + item + suffix));
+ }
+
+ function populateGlobals(suggestions, globals = {}) {
+ for(let i = 0; i < suggestions.length;) {
+ if(typeof suggestions[i] === 'object') {
+ const identified = getGlobals(suggestions[i], globals);
+ array.mergeSets(suggestions, identified);
+ suggestions.splice(i, 1);
+ } else {
+ ++ i;
+ }
+ }
+ }
+
function getHints(cm, options) {
const cur = cm.getCursor();
const token = cm.getTokenAt(cur);
@@ -52,6 +72,8 @@ define(() => {
comp = comp.concat(token.state.knownAgent);
}
+ populateGlobals(comp, cm.options.globals);
+
const ranges = makeRanges(cm, cur.line, from, token.end);
let selfValid = false;
const list = (comp
diff --git a/scripts/sequence/CodeMirrorMode.js b/scripts/sequence/CodeMirrorMode.js
index e897d9a..7ea91a4 100644
--- a/scripts/sequence/CodeMirrorMode.js
+++ b/scripts/sequence/CodeMirrorMode.js
@@ -26,11 +26,21 @@ define(['core/ArrayUtilities'], (array) => {
',': {type: 'operator', suggest: true, then: {'': 1}},
'\n': end,
}};
- const agentListToEnd = {type: 'variable', suggest: 'Agent', then: {
+ const agentListToText = {type: 'variable', suggest: 'Agent', then: {
'': 0,
',': {type: 'operator', suggest: true, then: {'': 1}},
':': {type: 'operator', suggest: true, then: {'': textToEnd}},
}};
+ const agentList2ToText = {type: 'variable', suggest: 'Agent', then: {
+ '': 0,
+ ',': {type: 'operator', suggest: true, then: {'': agentListToText}},
+ ':': CM_ERROR,
+ }};
+ const singleAgentToText = {type: 'variable', suggest: 'Agent', then: {
+ '': 0,
+ ',': CM_ERROR,
+ ':': {type: 'operator', suggest: true, then: {'': textToEnd}},
+ }};
const agentToOptText = {type: 'variable', suggest: 'Agent', then: {
'': 0,
':': {type: 'operator', suggest: true, then: {
@@ -46,12 +56,12 @@ define(['core/ArrayUtilities'], (array) => {
suggest: [side + ' of ', side + ': '],
then: {
'of': {type: 'keyword', suggest: true, then: {
- '': agentListToEnd,
+ '': agentListToText,
}},
':': {type: 'operator', suggest: true, then: {
'': textToEnd,
}},
- '': agentListToEnd,
+ '': agentListToText,
},
};
}
@@ -122,7 +132,17 @@ define(['core/ArrayUtilities'], (array) => {
'': textToEnd,
}},
'theme': {type: 'keyword', suggest: true, then: {
- '': textToEnd,
+ '': {
+ type: 'string',
+ suggest: {
+ global: 'themes',
+ suffix: '\n',
+ },
+ then: {
+ '': 0,
+ '\n': end,
+ },
+ },
}},
'terminators': {type: 'keyword', suggest: true, then: {
'none': {type: 'keyword', suggest: true, then: {}},
@@ -166,17 +186,17 @@ define(['core/ArrayUtilities'], (array) => {
}},
'note': {type: 'keyword', suggest: true, then: {
'over': {type: 'keyword', suggest: true, then: {
- '': agentListToEnd,
+ '': agentListToText,
}},
'left': makeSideNote('left'),
'right': makeSideNote('right'),
'between': {type: 'keyword', suggest: true, then: {
- '': agentListToEnd,
+ '': agentList2ToText,
}},
}},
'state': {type: 'keyword', suggest: 'state over ', then: {
'over': {type: 'keyword', suggest: true, then: {
- '': agentListToEnd,
+ '': singleAgentToText,
}},
}},
'text': {type: 'keyword', suggest: true, then: {
@@ -204,6 +224,9 @@ define(['core/ArrayUtilities'], (array) => {
}
function cmGetVarSuggestions(state, previous, current) {
+ if(typeof current.suggest === 'object' && current.suggest.global) {
+ return [current.suggest];
+ }
if(
typeof current.suggest !== 'string' ||
previous.suggest === current.suggest
diff --git a/scripts/sequence/Renderer.js b/scripts/sequence/Renderer.js
index ba24a34..6c0f160 100644
--- a/scripts/sequence/Renderer.js
+++ b/scripts/sequence/Renderer.js
@@ -589,6 +589,12 @@ define([
this.sizer.detach();
}
+ getThemeNames() {
+ return (Array.from(this.themes.keys())
+ .filter((name) => (name !== ''))
+ );
+ }
+
getAgentX(name) {
return this.agentInfos.get(name).x;
}
diff --git a/scripts/sequence/components/Connect.js b/scripts/sequence/components/Connect.js
index 3ed83c6..340e5eb 100644
--- a/scripts/sequence/components/Connect.js
+++ b/scripts/sequence/components/Connect.js
@@ -24,12 +24,17 @@ define([
function getArrowShort(theme) {
const arrow = theme.connect.arrow;
- const h = arrow.height / 2;
- const w = arrow.width;
+ const join = arrow.attrs['stroke-linejoin'] || 'miter';
const t = arrow.attrs['stroke-width'] * 0.5;
const lineStroke = theme.agentLineAttrs['stroke-width'] * 0.5;
- const arrowDistance = t * Math.sqrt((w * w) / (h * h) + 1);
- return lineStroke + arrowDistance;
+ if(join === 'round') {
+ return lineStroke + t;
+ } else {
+ const h = arrow.height / 2;
+ const w = arrow.width;
+ const arrowDistance = t * Math.sqrt((w * w) / (h * h) + 1);
+ return lineStroke + arrowDistance;
+ }
}
class Connect extends BaseComponent {
diff --git a/scripts/sequence/themes/Chunky.js b/scripts/sequence/themes/Chunky.js
index af6154a..80a39a0 100644
--- a/scripts/sequence/themes/Chunky.js
+++ b/scripts/sequence/themes/Chunky.js
@@ -4,7 +4,7 @@ define(['core/ArrayUtilities', 'svg/SVGShapes'], (array, SVGShapes) => {
const LINE_HEIGHT = 1.3;
const SETTINGS = {
- titleMargin: 10,
+ titleMargin: 12,
outerMargin: 5,
agentMargin: 8,
actionMargin: 5,
@@ -40,6 +40,7 @@ define(['core/ArrayUtilities', 'svg/SVGShapes'], (array, SVGShapes) => {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 3,
+ 'stroke-linecap': 'round',
},
},
bar: {
@@ -48,6 +49,8 @@ define(['core/ArrayUtilities', 'svg/SVGShapes'], (array, SVGShapes) => {
'stroke': '#000000',
'stroke-width': 3,
'height': 4,
+ 'rx': 2,
+ 'ry': 2,
},
},
fade: {
@@ -60,7 +63,7 @@ define(['core/ArrayUtilities', 'svg/SVGShapes'], (array, SVGShapes) => {
},
connect: {
- loopbackRadius: 6,
+ loopbackRadius: 8,
lineAttrs: {
'solid': {
'fill': 'none',
@@ -71,7 +74,7 @@ define(['core/ArrayUtilities', 'svg/SVGShapes'], (array, SVGShapes) => {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 3,
- 'stroke-dasharray': '4, 2',
+ 'stroke-dasharray': '10, 4',
},
},
arrow: {
@@ -79,13 +82,14 @@ define(['core/ArrayUtilities', 'svg/SVGShapes'], (array, SVGShapes) => {
height: 12,
attrs: {
'fill': '#000000',
- 'stroke-width': 0,
- 'stroke-linejoin': 'miter',
+ 'stroke': '#000000',
+ 'stroke-width': 3,
+ 'stroke-linejoin': 'round',
},
},
label: {
padding: 6,
- margin: {top: 2, bottom: 1},
+ margin: {top: 2, bottom: 3},
attrs: {
'font-family': 'sans-serif',
'font-size': 8,
@@ -100,10 +104,10 @@ define(['core/ArrayUtilities', 'svg/SVGShapes'], (array, SVGShapes) => {
},
mask: {
padding: {
- top: 0,
- left: 3,
- right: 3,
- bottom: 1,
+ top: 1,
+ left: 5,
+ right: 5,
+ bottom: 3,
},
},
},
@@ -116,28 +120,28 @@ define(['core/ArrayUtilities', 'svg/SVGShapes'], (array, SVGShapes) => {
boxAttrs: {
'fill': 'none',
'stroke': '#000000',
- 'stroke-width': 1.5,
- 'rx': 2,
- 'ry': 2,
+ 'stroke-width': 4,
+ 'rx': 5,
+ 'ry': 5,
},
section: {
padding: {
top: 3,
- bottom: 2,
+ bottom: 4,
},
mode: {
padding: {
- top: 1,
- left: 3,
- right: 3,
- bottom: 0,
+ top: 2,
+ left: 5,
+ right: 5,
+ bottom: 1,
},
boxAttrs: {
'fill': '#FFFFFF',
'stroke': '#000000',
- 'stroke-width': 3,
- 'rx': 2,
- 'ry': 2,
+ 'stroke-width': 2,
+ 'rx': 3,
+ 'ry': 3,
},
labelAttrs: {
'font-family': 'sans-serif',
@@ -149,7 +153,7 @@ define(['core/ArrayUtilities', 'svg/SVGShapes'], (array, SVGShapes) => {
},
label: {
padding: {
- top: 1,
+ top: 2,
left: 5,
right: 3,
bottom: 0,
@@ -165,8 +169,8 @@ define(['core/ArrayUtilities', 'svg/SVGShapes'], (array, SVGShapes) => {
separator: {
attrs: {
'stroke': '#000000',
- 'stroke-width': 1.5,
- 'stroke-dasharray': '4, 2',
+ 'stroke-width': 2,
+ 'stroke-dasharray': '5, 3',
},
},
},
@@ -187,12 +191,13 @@ define(['core/ArrayUtilities', 'svg/SVGShapes'], (array, SVGShapes) => {
},
'note': {
margin: {top: 0, left: 5, right: 5, bottom: 0},
- padding: {top: 5, left: 5, right: 10, bottom: 5},
+ padding: {top: 3, left: 3, right: 10, bottom: 3},
overlap: {left: 10, right: 10},
boxRenderer: SVGShapes.renderNote.bind(null, {
'fill': '#FFFFFF',
'stroke': '#000000',
- 'stroke-width': 1,
+ 'stroke-width': 2,
+ 'stroke-linejoin': 'round',
}, {
'fill': 'none',
'stroke': '#000000',
@@ -206,12 +211,12 @@ define(['core/ArrayUtilities', 'svg/SVGShapes'], (array, SVGShapes) => {
},
'state': {
margin: {top: 0, left: 5, right: 5, bottom: 0},
- padding: {top: 7, left: 7, right: 7, bottom: 7},
+ padding: {top: 5, left: 7, right: 7, bottom: 5},
overlap: {left: 10, right: 10},
boxRenderer: SVGShapes.renderBox.bind(null, {
'fill': '#FFFFFF',
'stroke': '#000000',
- 'stroke-width': 1,
+ 'stroke-width': 3,
'rx': 10,
'ry': 10,
}),
@@ -225,6 +230,7 @@ define(['core/ArrayUtilities', 'svg/SVGShapes'], (array, SVGShapes) => {
titleAttrs: {
'font-family': 'sans-serif',
+ 'font-weight': 'bolder',
'font-size': 20,
'line-height': LINE_HEIGHT,
'text-anchor': 'middle',