diff --git a/README.md b/README.md
index 25ea9df..228385d 100644
--- a/README.md
+++ b/README.md
@@ -146,6 +146,21 @@ state over Foo: 'Newlines here,
too!'
```
+### Themes
+
+
+
+```
+theme sketch
+
+title Mockup
+
+A thing -> +Another thing: Something happens
+-Another thing --> A thing: With some response
+note right of Another thing: Find out what happens here
+end Another thing
+```
+
### Short-Lived Agents
diff --git a/editor-dev.htm b/editor-dev.htm
index 8e4c067..5cb0129 100644
--- a/editor-dev.htm
+++ b/editor-dev.htm
@@ -5,7 +5,11 @@
base-uri 'self';
default-src 'none';
script-src 'self' https://cdnjs.cloudflare.com https://unpkg.com;
- style-src 'self' https://cdnjs.cloudflare.com;
+ style-src 'self'
+ https://cdnjs.cloudflare.com
+ 'sha256-ru2GY2rXeOf7PQX5LzK3ckNo21FCDUoRc2f3i0QcD1g='
+ ;
+ font-src 'self' data:;
img-src 'self' blob:;
form-action 'none';
">
diff --git a/index.html b/index.html
index 6a28767..be4cfa7 100644
--- a/index.html
+++ b/index.html
@@ -5,7 +5,11 @@
base-uri 'self';
default-src 'none';
script-src 'self' https://cdnjs.cloudflare.com https://unpkg.com;
- style-src 'self' https://cdnjs.cloudflare.com;
+ style-src 'self'
+ https://cdnjs.cloudflare.com
+ 'sha256-ru2GY2rXeOf7PQX5LzK3ckNo21FCDUoRc2f3i0QcD1g='
+ ;
+ font-src 'self' data:;
img-src 'self' blob:;
form-action 'none';
">
diff --git a/lib/sequence-diagram.js b/lib/sequence-diagram.js
index 719ed8b..0c82a62 100644
--- a/lib/sequence-diagram.js
+++ b/lib/sequence-diagram.js
@@ -3630,6 +3630,7 @@ define('sequence/components/AgentCap',[
y,
padding: config.padding,
boxAttrs: config.boxAttrs,
+ boxRenderer: config.boxRenderer,
labelAttrs: config.labelAttrs,
boxLayer: env.shapeLayer,
labelLayer: clickable,
@@ -3670,14 +3671,7 @@ define('sequence/components/AgentCap',[
const config = env.theme.agentCap.cross;
const d = config.size / 2;
- env.shapeLayer.appendChild(svg.make('path', Object.assign({
- 'd': (
- 'M ' + (x - d) + ' ' + y +
- ' L ' + (x + d) + ' ' + (y + d * 2) +
- ' M ' + (x + d) + ' ' + y +
- ' L ' + (x - d) + ' ' + (y + d * 2)
- ),
- }, config.attrs)));
+ env.shapeLayer.appendChild(config.render({x, y: y + d, radius: d}));
env.makeRegion().appendChild(svg.make('rect', {
'x': x - d,
@@ -3713,7 +3707,7 @@ define('sequence/components/AgentCap',[
topShift(agentInfo, env) {
const config = env.theme.agentCap.bar;
- return config.attrs.height / 2;
+ return config.height / 2;
}
render(y, {x, label}, env) {
@@ -3724,25 +3718,27 @@ define('sequence/components/AgentCap',[
configB.padding.left +
configB.padding.right
);
+ const height = config.height;
- env.shapeLayer.appendChild(svg.make('rect', Object.assign({
- 'x': x - width / 2,
- 'y': y,
- 'width': width,
- }, config.attrs)));
+ env.shapeLayer.appendChild(config.render({
+ x: x - width / 2,
+ y,
+ width,
+ height,
+ }));
env.makeRegion().appendChild(svg.make('rect', {
'x': x - width / 2,
'y': y,
'width': width,
- 'height': config.attrs.height,
+ 'height': height,
'fill': 'transparent',
}));
return {
lineTop: 0,
- lineBottom: config.attrs.height,
- height: config.attrs.height,
+ lineBottom: height,
+ height: height,
};
}
}
@@ -4107,10 +4103,10 @@ define('sequence/components/Connect',[
if(!ww || !hh) {
container.appendChild(svg.make('path', Object.assign({
'd': (
- 'M ' + xL1 + ' ' + y1 +
- ' L ' + xR + ' ' + y1 +
- ' A ' + r + ' ' + r + ' 0 0 1 ' + xR + ' ' + y2 +
- ' L ' + xL2 + ' ' + y2
+ 'M' + xL1 + ' ' + y1 +
+ 'L' + xR + ' ' + y1 +
+ 'A' + r + ' ' + r + ' 0 0 1 ' + xR + ' ' + y2 +
+ 'L' + xL2 + ' ' + y2
),
}, attrs)));
return;
@@ -4403,7 +4399,7 @@ define('sequence/components/Note',['./BaseComponent', 'svg/SVGUtilities'], (Base
mode,
label,
}, env) {
- const config = env.theme.note[mode];
+ const config = env.theme.getNote(mode);
const clickable = env.makeRegion();
@@ -4477,7 +4473,7 @@ define('sequence/components/Note',['./BaseComponent', 'svg/SVGUtilities'], (Base
class NoteOver extends NoteComponent {
separation({agentNames, mode, label}, env) {
- const config = env.theme.note[mode];
+ const config = env.theme.getNote(mode);
const width = (
env.textSizer.measure(config.labelAttrs, label).width +
config.padding.left +
@@ -4504,7 +4500,7 @@ define('sequence/components/Note',['./BaseComponent', 'svg/SVGUtilities'], (Base
}
render({agentNames, mode, label}, env) {
- const config = env.theme.note[mode];
+ const config = env.theme.getNote(mode);
const {left, right} = findExtremes(env.agentInfos, agentNames);
const infoL = env.agentInfos.get(left);
@@ -4536,7 +4532,7 @@ define('sequence/components/Note',['./BaseComponent', 'svg/SVGUtilities'], (Base
}
separation({agentNames, mode, label}, env) {
- const config = env.theme.note[mode];
+ const config = env.theme.getNote(mode);
const {left, right} = findExtremes(env.agentInfos, agentNames);
const width = (
env.textSizer.measure(config.labelAttrs, label).width +
@@ -4562,7 +4558,7 @@ define('sequence/components/Note',['./BaseComponent', 'svg/SVGUtilities'], (Base
}
render({agentNames, mode, label}, env) {
- const config = env.theme.note[mode];
+ const config = env.theme.getNote(mode);
const {left, right} = findExtremes(env.agentInfos, agentNames);
if(this.isRight) {
const info = env.agentInfos.get(right);
@@ -4588,7 +4584,7 @@ define('sequence/components/Note',['./BaseComponent', 'svg/SVGUtilities'], (Base
class NoteBetween extends NoteComponent {
separation({agentNames, mode, label}, env) {
- const config = env.theme.note[mode];
+ const config = env.theme.getNote(mode);
const {left, right} = findExtremes(env.agentInfos, agentNames);
const infoL = env.agentInfos.get(left);
const infoR = env.agentInfos.get(right);
@@ -4879,25 +4875,13 @@ define('sequence/Renderer',[
return;
}
- const r = agentInfo.currentRad;
-
- if(r > 0) {
- this.agentLines.appendChild(svg.make('rect', Object.assign({
- 'x': agentInfo.x - r,
- 'y': agentInfo.latestYStart,
- 'width': r * 2,
- 'height': toY - agentInfo.latestYStart,
- 'class': 'agent-' + agentInfo.index + '-line',
- }, this.theme.agentLineAttrs)));
- } else {
- this.agentLines.appendChild(svg.make('line', Object.assign({
- 'x1': agentInfo.x,
- 'y1': agentInfo.latestYStart,
- 'x2': agentInfo.x,
- 'y2': toY,
- 'class': 'agent-' + agentInfo.index + '-line',
- }, this.theme.agentLineAttrs)));
- }
+ this.theme.drawAgentLine(this.agentLines, {
+ x: agentInfo.x,
+ y0: agentInfo.latestYStart,
+ y1: toY,
+ width: agentInfo.currentRad * 2,
+ className: 'agent-' + agentInfo.index + '-line',
+ });
}
addHighlightObject(line, o) {
@@ -5114,6 +5098,9 @@ define('sequence/Renderer',[
this.theme = this.themes.get('');
}
+ this.theme.reset();
+ this.theme.addDefs(this.addDef);
+
this.title.set({
attrs: this.theme.titleAttrs,
text: sequence.meta.title,
@@ -5273,9 +5260,18 @@ define('sequence/Exporter',[],() => {
};
});
-define('sequence/themes/Basic',['core/ArrayUtilities', 'svg/SVGShapes'], (array, SVGShapes) => {
+define('sequence/themes/Basic',[
+ 'core/ArrayUtilities',
+ 'svg/SVGUtilities',
+ 'svg/SVGShapes',
+], (
+ array,
+ svg,
+ SVGShapes
+) => {
'use strict';
+ const FONT = 'sans-serif';
const LINE_HEIGHT = 1.3;
const SETTINGS = {
@@ -5301,7 +5297,7 @@ define('sequence/themes/Basic',['core/ArrayUtilities', 'svg/SVGShapes'], (array,
'stroke-width': 1,
},
labelAttrs: {
- 'font-family': 'sans-serif',
+ 'font-family': FONT,
'font-size': 12,
'line-height': LINE_HEIGHT,
'text-anchor': 'middle',
@@ -5309,18 +5305,32 @@ define('sequence/themes/Basic',['core/ArrayUtilities', 'svg/SVGShapes'], (array,
},
cross: {
size: 20,
- attrs: {
- 'fill': 'none',
- 'stroke': '#000000',
- 'stroke-width': 1,
+ render: ({x, y, radius}) => {
+ return svg.make('path', {
+ 'd': (
+ 'M' + (x - radius) + ' ' + (y - radius) +
+ 'l' + (radius * 2) + ' ' + (radius * 2) +
+ 'm0 ' + (-radius * 2) +
+ 'l' + (-radius * 2) + ' ' + (radius * 2)
+ ),
+ 'fill': 'none',
+ 'stroke': '#000000',
+ 'stroke-width': 1,
+ });
},
},
bar: {
- attrs: {
- 'fill': '#000000',
- 'stroke': '#000000',
- 'stroke-width': 1,
- 'height': 4,
+ height: 4,
+ render: ({x, y, width, height}) => {
+ return svg.make('rect', {
+ 'x': x,
+ 'y': y,
+ 'width': width,
+ 'height': height,
+ 'fill': '#000000',
+ 'stroke': '#000000',
+ 'stroke-width': 1,
+ });
},
},
fade: {
@@ -5381,13 +5391,13 @@ define('sequence/themes/Basic',['core/ArrayUtilities', 'svg/SVGShapes'], (array,
padding: 6,
margin: {top: 2, bottom: 1},
attrs: {
- 'font-family': 'sans-serif',
+ 'font-family': FONT,
'font-size': 8,
'line-height': LINE_HEIGHT,
'text-anchor': 'middle',
},
loopbackAttrs: {
- 'font-family': 'sans-serif',
+ 'font-family': FONT,
'font-size': 8,
'line-height': LINE_HEIGHT,
},
@@ -5447,7 +5457,7 @@ define('sequence/themes/Basic',['core/ArrayUtilities', 'svg/SVGShapes'], (array,
'ry': 2,
},
labelAttrs: {
- 'font-family': 'sans-serif',
+ 'font-family': FONT,
'font-weight': 'bold',
'font-size': 9,
'line-height': LINE_HEIGHT,
@@ -5462,7 +5472,7 @@ define('sequence/themes/Basic',['core/ArrayUtilities', 'svg/SVGShapes'], (array,
bottom: 0,
},
labelAttrs: {
- 'font-family': 'sans-serif',
+ 'font-family': FONT,
'font-size': 8,
'line-height': LINE_HEIGHT,
'text-anchor': 'left',
@@ -5478,60 +5488,8 @@ define('sequence/themes/Basic',['core/ArrayUtilities', 'svg/SVGShapes'], (array,
},
},
- note: {
- '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: SVGShapes.renderBox.bind(null, {
- 'fill': '#FFFFFF',
- }),
- labelAttrs: {
- 'font-family': 'sans-serif',
- 'font-size': 8,
- 'line-height': LINE_HEIGHT,
- },
- },
- '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: SVGShapes.renderNote.bind(null, {
- 'fill': '#FFFFFF',
- 'stroke': '#000000',
- 'stroke-width': 1,
- }, {
- 'fill': 'none',
- 'stroke': '#000000',
- 'stroke-width': 1,
- }),
- labelAttrs: {
- 'font-family': 'sans-serif',
- 'font-size': 8,
- 'line-height': LINE_HEIGHT,
- },
- },
- '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: SVGShapes.renderBox.bind(null, {
- 'fill': '#FFFFFF',
- 'stroke': '#000000',
- 'stroke-width': 1,
- 'rx': 10,
- 'ry': 10,
- }),
- labelAttrs: {
- 'font-family': 'sans-serif',
- 'font-size': 8,
- 'line-height': LINE_HEIGHT,
- },
- },
- },
-
titleAttrs: {
- 'font-family': 'sans-serif',
+ 'font-family': FONT,
'font-size': 20,
'line-height': LINE_HEIGHT,
'text-anchor': 'middle',
@@ -5545,17 +5503,108 @@ define('sequence/themes/Basic',['core/ArrayUtilities', 'svg/SVGShapes'], (array,
},
};
+ const 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: SVGShapes.renderBox.bind(null, {
+ 'fill': '#FFFFFF',
+ }),
+ labelAttrs: {
+ 'font-family': FONT,
+ 'font-size': 8,
+ 'line-height': LINE_HEIGHT,
+ },
+ },
+ '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: SVGShapes.renderNote.bind(null, {
+ 'fill': '#FFFFFF',
+ 'stroke': '#000000',
+ 'stroke-width': 1,
+ }, {
+ 'fill': 'none',
+ 'stroke': '#000000',
+ 'stroke-width': 1,
+ }),
+ labelAttrs: {
+ 'font-family': FONT,
+ 'font-size': 8,
+ 'line-height': LINE_HEIGHT,
+ },
+ },
+ '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: SVGShapes.renderBox.bind(null, {
+ 'fill': '#FFFFFF',
+ 'stroke': '#000000',
+ 'stroke-width': 1,
+ 'rx': 10,
+ 'ry': 10,
+ }),
+ labelAttrs: {
+ 'font-family': FONT,
+ 'font-size': 8,
+ 'line-height': LINE_HEIGHT,
+ },
+ },
+ };
+
return class BasicTheme {
constructor() {
this.name = 'basic';
Object.assign(this, SETTINGS);
}
+
+ reset() {
+ }
+
+ addDefs() {
+ }
+
+ getNote(type) {
+ return NOTES[type];
+ }
+
+ drawAgentLine(container, {x, y0, y1, width, className}) {
+ if(width > 0) {
+ container.appendChild(svg.make('rect', Object.assign({
+ 'x': x - width / 2,
+ 'y': y0,
+ 'width': width,
+ 'height': y1 - y0,
+ 'class': className,
+ }, this.agentLineAttrs)));
+ } else {
+ container.appendChild(svg.make('line', Object.assign({
+ 'x1': x,
+ 'y1': y0,
+ 'x2': x,
+ 'y2': y1,
+ 'class': className,
+ }, this.agentLineAttrs)));
+ }
+ }
};
});
-define('sequence/themes/Chunky',['core/ArrayUtilities', 'svg/SVGShapes'], (array, SVGShapes) => {
+define('sequence/themes/Chunky',[
+ 'core/ArrayUtilities',
+ 'svg/SVGUtilities',
+ 'svg/SVGShapes',
+], (
+ array,
+ svg,
+ SVGShapes
+) => {
'use strict';
+ const FONT = 'sans-serif';
const LINE_HEIGHT = 1.3;
const SETTINGS = {
@@ -5583,7 +5632,7 @@ define('sequence/themes/Chunky',['core/ArrayUtilities', 'svg/SVGShapes'], (array
'ry': 4,
},
labelAttrs: {
- 'font-family': 'sans-serif',
+ 'font-family': FONT,
'font-weight': 'bold',
'font-size': 14,
'line-height': LINE_HEIGHT,
@@ -5592,21 +5641,36 @@ define('sequence/themes/Chunky',['core/ArrayUtilities', 'svg/SVGShapes'], (array
},
cross: {
size: 20,
- attrs: {
- 'fill': 'none',
- 'stroke': '#000000',
- 'stroke-width': 3,
- 'stroke-linecap': 'round',
+ render: ({x, y, radius}) => {
+ return svg.make('path', Object.assign({
+ 'd': (
+ 'M' + (x - radius) + ' ' + (y - radius) +
+ 'l' + (radius * 2) + ' ' + (radius * 2) +
+ 'm0 ' + (-radius * 2) +
+ 'l' + (-radius * 2) + ' ' + (radius * 2)
+ ),
+ }, {
+ 'fill': 'none',
+ 'stroke': '#000000',
+ 'stroke-width': 3,
+ 'stroke-linecap': 'round',
+ }));
},
},
bar: {
- attrs: {
- 'fill': '#000000',
- 'stroke': '#000000',
- 'stroke-width': 3,
- 'height': 4,
- 'rx': 2,
- 'ry': 2,
+ height: 4,
+ render: ({x, y, width, height}) => {
+ return svg.make('rect', {
+ 'x': x,
+ 'y': y,
+ 'width': width,
+ 'height': height,
+ 'fill': '#000000',
+ 'stroke': '#000000',
+ 'stroke-width': 3,
+ 'rx': 2,
+ 'ry': 2,
+ });
},
},
fade: {
@@ -5669,13 +5733,13 @@ define('sequence/themes/Chunky',['core/ArrayUtilities', 'svg/SVGShapes'], (array
padding: 7,
margin: {top: 2, bottom: 3},
attrs: {
- 'font-family': 'sans-serif',
+ 'font-family': FONT,
'font-size': 8,
'line-height': LINE_HEIGHT,
'text-anchor': 'middle',
},
loopbackAttrs: {
- 'font-family': 'sans-serif',
+ 'font-family': FONT,
'font-size': 8,
'line-height': LINE_HEIGHT,
},
@@ -5735,7 +5799,7 @@ define('sequence/themes/Chunky',['core/ArrayUtilities', 'svg/SVGShapes'], (array
'ry': 3,
},
labelAttrs: {
- 'font-family': 'sans-serif',
+ 'font-family': FONT,
'font-weight': 'bold',
'font-size': 9,
'line-height': LINE_HEIGHT,
@@ -5750,7 +5814,7 @@ define('sequence/themes/Chunky',['core/ArrayUtilities', 'svg/SVGShapes'], (array
bottom: 0,
},
labelAttrs: {
- 'font-family': 'sans-serif',
+ 'font-family': FONT,
'font-size': 8,
'line-height': LINE_HEIGHT,
'text-anchor': 'left',
@@ -5766,61 +5830,8 @@ define('sequence/themes/Chunky',['core/ArrayUtilities', 'svg/SVGShapes'], (array
},
},
- note: {
- '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: SVGShapes.renderBox.bind(null, {
- 'fill': '#FFFFFF',
- }),
- labelAttrs: {
- 'font-family': 'sans-serif',
- 'font-size': 8,
- 'line-height': LINE_HEIGHT,
- },
- },
- '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: SVGShapes.renderNote.bind(null, {
- 'fill': '#FFFFFF',
- 'stroke': '#000000',
- 'stroke-width': 2,
- 'stroke-linejoin': 'round',
- }, {
- 'fill': 'none',
- 'stroke': '#000000',
- 'stroke-width': 1,
- }),
- labelAttrs: {
- 'font-family': 'sans-serif',
- 'font-size': 8,
- 'line-height': LINE_HEIGHT,
- },
- },
- '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: SVGShapes.renderBox.bind(null, {
- 'fill': '#FFFFFF',
- 'stroke': '#000000',
- 'stroke-width': 3,
- 'rx': 10,
- 'ry': 10,
- }),
- labelAttrs: {
- 'font-family': 'sans-serif',
- 'font-size': 8,
- 'line-height': LINE_HEIGHT,
- },
- },
- },
-
titleAttrs: {
- 'font-family': 'sans-serif',
+ 'font-family': FONT,
'font-weight': 'bolder',
'font-size': 20,
'line-height': LINE_HEIGHT,
@@ -5835,14 +5846,1097 @@ define('sequence/themes/Chunky',['core/ArrayUtilities', 'svg/SVGShapes'], (array
},
};
+ const 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: SVGShapes.renderBox.bind(null, {
+ 'fill': '#FFFFFF',
+ }),
+ labelAttrs: {
+ 'font-family': FONT,
+ 'font-size': 8,
+ 'line-height': LINE_HEIGHT,
+ },
+ },
+ '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: SVGShapes.renderNote.bind(null, {
+ 'fill': '#FFFFFF',
+ 'stroke': '#000000',
+ 'stroke-width': 2,
+ 'stroke-linejoin': 'round',
+ }, {
+ 'fill': 'none',
+ 'stroke': '#000000',
+ 'stroke-width': 1,
+ }),
+ labelAttrs: {
+ 'font-family': FONT,
+ 'font-size': 8,
+ 'line-height': LINE_HEIGHT,
+ },
+ },
+ '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: SVGShapes.renderBox.bind(null, {
+ 'fill': '#FFFFFF',
+ 'stroke': '#000000',
+ 'stroke-width': 3,
+ 'rx': 10,
+ 'ry': 10,
+ }),
+ labelAttrs: {
+ 'font-family': FONT,
+ 'font-size': 8,
+ 'line-height': LINE_HEIGHT,
+ },
+ },
+ };
+
return class ChunkyTheme {
constructor() {
this.name = 'chunky';
Object.assign(this, SETTINGS);
}
+
+ reset() {
+ }
+
+ addDefs() {
+ }
+
+ getNote(type) {
+ return NOTES[type];
+ }
+
+ drawAgentLine(container, {x, y0, y1, width, className}) {
+ if(width > 0) {
+ container.appendChild(svg.make('rect', Object.assign({
+ 'x': x - width / 2,
+ 'y': y0,
+ 'width': width,
+ 'height': y1 - y0,
+ 'class': className,
+ }, this.agentLineAttrs)));
+ } else {
+ container.appendChild(svg.make('line', Object.assign({
+ 'x1': x,
+ 'y1': y0,
+ 'x2': x,
+ 'y2': y1,
+ 'class': className,
+ }, this.agentLineAttrs)));
+ }
+ }
};
});
+define('sequence/themes/HandleeFontData',[],() => {
+ 'use strict';
+
+ // Handlee font, by Joe Prince
+ // Downloaded from Google Fonts and converted to Base64 for embedding in
+ // generated SVGs
+ // https://fonts.google.com/specimen/Handlee
+
+ /* License
+
+ SIL OPEN FONT LICENSE
+ Version 1.1 - 26 February 2007
+
+ PREAMBLE
+ The goals of the Open Font License (OFL) are to stimulate worldwide
+ development of collaborative font projects, to support the font creation
+ efforts of academic and linguistic communities, and to provide a free and
+ open framework in which fonts may be shared and improved in partnership
+ with others.
+
+ The OFL allows the licensed fonts to be used, studied, modified and
+ redistributed freely as long as they are not sold by themselves. The
+ fonts, including any derivative works, can be bundled, embedded,
+ redistributed and/or sold with any software provided that any reserved
+ names are not used by derivative works. The fonts and derivatives,
+ however, cannot be released under any other type of license. The
+ requirement for fonts to remain under this license does not apply
+ to any document created using the fonts or their derivatives.
+
+ DEFINITIONS
+ "Font Software" refers to the set of files released by the Copyright
+ Holder(s) under this license and clearly marked as such. This may
+ include source files, build scripts and documentation.
+
+ "Reserved Font Name" refers to any names specified as such after the
+ copyright statement(s).
+
+ "Original Version" refers to the collection of Font Software components as
+ distributed by the Copyright Holder(s).
+
+ "Modified Version" refers to any derivative made by adding to, deleting,
+ or substituting — in part or in whole — any of the components of the
+ Original Version, by changing formats or by porting the Font Software to a
+ new environment.
+
+ "Author" refers to any designer, engineer, programmer, technical
+ writer or other person who contributed to the Font Software.
+
+ PERMISSION & CONDITIONS
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of the Font Software, to use, study, copy, merge, embed, modify,
+ redistribute, and sell modified and unmodified copies of the Font
+ Software, subject to the following conditions:
+
+ 1) Neither the Font Software nor any of its individual components,
+ in Original or Modified Versions, may be sold by itself.
+
+ 2) Original or Modified Versions of the Font Software may be bundled,
+ redistributed and/or sold with any software, provided that each copy
+ contains the above copyright notice and this license. These can be
+ included either as stand-alone text files, human-readable headers or
+ in the appropriate machine-readable metadata fields within text or
+ binary files as long as those fields can be easily viewed by the user.
+
+ 3) No Modified Version of the Font Software may use the Reserved Font
+ Name(s) unless explicit written permission is granted by the corresponding
+ Copyright Holder. This restriction only applies to the primary font name as
+ presented to the users.
+
+ 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+ Software shall not be used to promote, endorse or advertise any
+ Modified Version, except to acknowledge the contribution(s) of the
+ Copyright Holder(s) and the Author(s) or with their explicit written
+ permission.
+
+ 5) The Font Software, modified or unmodified, in part or in whole,
+ must be distributed entirely under this license, and must not be
+ distributed under any other license. The requirement for fonts to
+ remain under this license does not apply to any document created
+ using the Font Software.
+
+ TERMINATION
+ This license becomes null and void if any of the above conditions are
+ not met.
+
+ DISCLAIMER
+ THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+ OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+ COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+ DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+ OTHER DEALINGS IN THE FONT SOFTWARE.
+
+ */
+
+ return {
+ name: 'Handlee',
+ woff2: (
+ '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' +
+ 'jjhu2qURx1x+3R5nnI2W/z0OAAA='
+ ),
+ };
+});
+
+define('sequence/themes/Sketch',[
+ 'core/ArrayUtilities',
+ 'svg/SVGUtilities',
+ 'svg/SVGShapes',
+ './HandleeFontData',
+], (
+ array,
+ svg,
+ SVGShapes,
+ Handlee
+) => {
+ 'use strict';
+
+ // TODO:
+ // * arrows
+ // * fade starter/terminator sometimes does not fully cover line
+ // * blocks (if/else/repeat/ref)
+
+ const FONT = Handlee.name;
+ const FONT_FAMILY = '"' + FONT + '",cursive';
+ const LINE_HEIGHT = 1.5;
+
+ const SETTINGS = {
+ titleMargin: 10,
+ outerMargin: 5,
+ agentMargin: 10,
+ actionMargin: 10,
+ minActionMargin: 3,
+ agentLineHighlightRadius: 4,
+
+ agentCap: {
+ box: {
+ padding: {
+ top: 5,
+ left: 10,
+ right: 10,
+ bottom: 5,
+ },
+ arrowBottom: 5 + 12 * 1.3 / 2,
+ labelAttrs: {
+ 'font-family': FONT_FAMILY,
+ 'font-size': 12,
+ 'line-height': LINE_HEIGHT,
+ 'text-anchor': 'middle',
+ },
+ boxRenderer: null,
+ },
+ cross: {
+ size: 15,
+ render: null,
+ },
+ bar: {
+ height: 6,
+ render: null,
+ },
+ fade: {
+ width: 8,
+ height: 6,
+ },
+ none: {
+ height: 10,
+ },
+ },
+
+ connect: {
+ loopbackRadius: 6,
+ lineAttrs: {
+ 'solid': {
+ 'fill': 'none',
+ 'stroke': '#000000',
+ 'stroke-width': 1,
+ },
+ 'dash': {
+ 'fill': 'none',
+ 'stroke': '#000000',
+ 'stroke-width': 1,
+ 'stroke-dasharray': '4, 2',
+ },
+ 'wave': {
+ 'fill': 'none',
+ 'stroke': '#000000',
+ 'stroke-width': 1,
+ 'stroke-linejoin': 'round',
+ 'stroke-linecap': 'round',
+ 'wave-width': 6,
+ 'wave-height': 0.5,
+ },
+ },
+ arrow: {
+ single: {
+ width: 5,
+ height: 10,
+ attrs: {
+ 'fill': '#000000',
+ 'stroke-width': 0,
+ 'stroke-linejoin': 'miter',
+ },
+ },
+ double: {
+ width: 4,
+ height: 6,
+ attrs: {
+ 'fill': 'none',
+ 'stroke': '#000000',
+ 'stroke-width': 1,
+ 'stroke-linejoin': 'miter',
+ },
+ },
+ },
+ label: {
+ padding: 6,
+ margin: {top: 2, bottom: 1},
+ attrs: {
+ 'font-family': FONT_FAMILY,
+ 'font-size': 8,
+ 'line-height': LINE_HEIGHT,
+ 'text-anchor': 'middle',
+ },
+ loopbackAttrs: {
+ 'font-family': FONT_FAMILY,
+ 'font-size': 8,
+ 'line-height': LINE_HEIGHT,
+ },
+ },
+ mask: {
+ padding: {
+ top: 0,
+ left: 3,
+ right: 3,
+ bottom: 1,
+ },
+ },
+ },
+
+ block: {
+ margin: {
+ top: 0,
+ bottom: 0,
+ },
+ modes: {
+ 'ref': {
+ boxAttrs: {
+ 'fill': '#FFFFFF',
+ 'stroke': '#000000',
+ 'stroke-width': 1.5,
+ 'rx': 2,
+ 'ry': 2,
+ },
+ },
+ '': {
+ boxAttrs: {
+ 'fill': 'none',
+ 'stroke': '#000000',
+ 'stroke-width': 1.5,
+ 'rx': 2,
+ 'ry': 2,
+ },
+ },
+ },
+ section: {
+ padding: {
+ top: 3,
+ bottom: 2,
+ },
+ mode: {
+ padding: {
+ top: 1,
+ left: 3,
+ right: 3,
+ bottom: 0,
+ },
+ boxAttrs: {
+ 'fill': '#FFFFFF',
+ 'stroke': '#000000',
+ 'stroke-width': 1,
+ 'rx': 2,
+ 'ry': 2,
+ },
+ labelAttrs: {
+ 'font-family': FONT_FAMILY,
+ 'font-weight': 'bold',
+ 'font-size': 9,
+ 'line-height': LINE_HEIGHT,
+ 'text-anchor': 'left',
+ },
+ },
+ label: {
+ padding: {
+ top: 1,
+ left: 5,
+ right: 3,
+ bottom: 0,
+ },
+ labelAttrs: {
+ 'font-family': FONT_FAMILY,
+ 'font-size': 8,
+ 'line-height': LINE_HEIGHT,
+ 'text-anchor': 'left',
+ },
+ },
+ },
+ separator: {
+ attrs: {
+ 'stroke': '#000000',
+ 'stroke-width': 1.5,
+ 'stroke-dasharray': '4, 2',
+ },
+ },
+ },
+
+ titleAttrs: {
+ 'font-family': FONT_FAMILY,
+ 'font-size': 20,
+ 'line-height': LINE_HEIGHT,
+ 'text-anchor': 'middle',
+ 'class': 'title',
+ },
+
+ agentLineAttrs: {
+ 'fill': 'none',
+ 'stroke': '#000000',
+ 'stroke-width': 1,
+ },
+ };
+
+ const 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: SVGShapes.renderBox.bind(null, {
+ 'fill': '#FFFFFF',
+ }),
+ labelAttrs: {
+ 'font-family': FONT_FAMILY,
+ 'font-size': 8,
+ 'line-height': LINE_HEIGHT,
+ },
+ },
+ '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: null,
+ labelAttrs: {
+ 'font-family': FONT_FAMILY,
+ 'font-size': 8,
+ 'line-height': LINE_HEIGHT,
+ },
+ },
+ '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: null,
+ labelAttrs: {
+ 'font-family': FONT_FAMILY,
+ 'font-size': 8,
+ 'line-height': LINE_HEIGHT,
+ },
+ },
+ };
+
+ class Random {
+ // xorshift+ 64-bit random generator
+ // https://en.wikipedia.org/wiki/Xorshift
+
+ constructor() {
+ this.s = new Uint32Array(4);
+ }
+
+ reset() {
+ // Initial seed: 0x00010203
+ for(let i = 0; i < 4; ++ i) {
+ this.s[i] = i;
+ }
+ }
+
+ nextFloat() {
+ /* jshint -W016 */ // bit-operations are part of the algorithm
+ const range = 0x100000000;
+ let x0 = this.s[0];
+ let x1 = this.s[1];
+ const y0 = this.s[2];
+ const y1 = this.s[3];
+ this.s[0] = y0;
+ this.s[1] = y1;
+ x0 ^= (x0 << 23) | (x1 >>> 9);
+ x1 ^= (x1 << 23);
+ this.s[2] = x0 ^ y0 ^ (x0 >>> 17) ^ (y0 >>> 26);
+ this.s[3] = (
+ x1 ^ y1 ^
+ (x0 << 15 | x1 >>> 17) ^
+ (y0 << 6 | y1 >>> 26)
+ );
+ return (((this.s[3] + y1) >>> 0) % range) / range;
+ }
+ }
+
+ const RIGHT = {};
+ const LEFT = {};
+
+ class SketchTheme {
+ constructor(handedness = RIGHT) {
+ if(handedness === RIGHT) {
+ this.name = 'sketch';
+ this.handedness = 1;
+ } else {
+ this.name = 'sketch left handed';
+ this.handedness = -1;
+ }
+ this.random = new Random();
+ Object.assign(this, SETTINGS);
+ // TODO: these mutate the global, not our copy
+ // (fine for now since we're a singleton for all practical purposes)
+ this.agentCap.cross.render = this.renderCross.bind(this);
+ this.agentCap.bar.render = this.renderBar.bind(this);
+ this.agentCap.box.boxRenderer = this.renderBox.bind(this);
+ NOTES.note.boxRenderer = this.renderNote.bind(this);
+ NOTES.state.boxRenderer = this.renderState.bind(this);
+ }
+
+ reset() {
+ this.random.reset();
+ }
+
+ addDefs(builder) {
+ builder('sketch_font', () => {
+ const style = document.createElement('style');
+ // For some uses, it is fine to load this font externally,
+ // but this fails when exporting as SVG / PNG (svg tags must
+ // have no external dependencies).
+// const url = 'https://fonts.googleapis.com/css?family=' + FONT;
+// style.innerText = '@import url("' + url + '")';
+ style.innerText = (
+ '@font-face{' +
+ 'font-family:"' + Handlee.name + '";' +
+ 'src:url("data:font/woff2;base64,' + Handlee.woff2 + '");' +
+ '}'
+ );
+ return style;
+ });
+ }
+
+ vary(range) {
+ if(!range) {
+ return 0;
+ }
+ // cosine distribution [-pi/2 pi/2] scaled to [-range range]
+ // int(cos(x))dx = sin(x)
+ // from -pi/2: sin(x) - sin(-pi/2) = sin(x) + 1
+ // total: sin(pi/2) + 1 = 2
+ // normalise to area 1: /2
+ // (sin(x) + 1) / 2
+ // inverse: p = (sin(x) + 1) / 2
+ // asin(2p - 1) = x
+ // normalise range
+ // x = asin(2p - 1) * range / (pi/2)
+ const rand = this.random.nextFloat(); // [0 1)
+ return Math.asin(rand * 2 - 1) * 2 * range / Math.PI;
+ }
+
+ lineNodes(p1, p2, {var1 = 1, var2 = 1, move = true}) {
+ const length = Math.sqrt(
+ (p2.x - p1.x) * (p2.x - p1.x) +
+ (p2.y - p1.y) * (p2.y - p1.y)
+ );
+ const rough = Math.min(Math.sqrt(length) * 0.2, 5);
+ const x1 = p1.x + this.vary(var1 * rough);
+ const y1 = p1.y + this.vary(var1 * rough);
+ const x2 = p2.x + this.vary(var2 * rough);
+ const y2 = p2.y + this.vary(var2 * rough);
+
+ // -1 = p1 higher, 1 = p2 higher
+ const upper = Math.max(-1, Math.min(1,
+ (y1 - y2) / (Math.abs(x1 - x2) + 0.001)
+ ));
+ const frac = upper / 6 + 0.5;
+
+ // Line curve is to top / left (simulates right-handed drawing)
+ // or top / right (left-handed)
+ const curveX = (0.5 + this.vary(0.5)) * rough;
+ const curveY = (0.5 + this.vary(0.5)) * rough;
+ const xc = x1 * (1 - frac) + x2 * frac - curveX * this.handedness;
+ const yc = y1 * (1 - frac) + y2 * frac - curveY;
+ const nodes = (
+ (move ? ('M' + x1 + ' ' + y1) : '') +
+ 'C' + xc + ' ' + yc +
+ ',' + x2 + ' ' + y2 +
+ ',' + x2 + ' ' + y2
+ );
+ return {
+ nodes,
+ p1: {x: x1, y: y1},
+ p2: {x: x2, y: y2},
+ };
+ }
+
+ renderLine(p1, p2, {var1 = 1, var2 = 1}) {
+ const line = this.lineNodes(p1, p2, {var1, var2});
+ const shape = svg.make('path', {
+ 'd': line.nodes,
+ 'stroke': 'rgba(0,0,0,0.7)',
+ 'stroke-width': 0.8,
+ 'fill': 'none',
+ });
+ return shape;
+ }
+
+ renderBox({x, y, width, height}, {fill = null} = {}) {
+ const lT = this.lineNodes(
+ {x, y},
+ {x: x + width, y},
+ {}
+ );
+ const lB = this.lineNodes(
+ {x: x + width, y: y + height},
+ {x, y: y + height},
+ {move: false}
+ );
+ const lR = this.lineNodes(
+ lT.p2,
+ lB.p1,
+ {var1: 0, var2: 0, move: false}
+ );
+ const lL = this.lineNodes(
+ lB.p2,
+ lT.p1,
+ {var1: 0, var2: 0.3, move: false}
+ );
+
+ const shape = svg.make('path', {
+ 'd': lT.nodes + lR.nodes + lB.nodes + lL.nodes,
+ 'stroke': 'rgba(0,0,0,0.7)',
+ 'stroke-width': 0.8,
+ 'fill': fill || '#FFFFFF',
+ });
+
+ return shape;
+ }
+
+ renderNote({x, y, width, height}) {
+ const flickSize = 5;
+ const lT = this.lineNodes(
+ {x, y},
+ {x: x + width - flickSize, y},
+ {}
+ );
+ const lF = this.lineNodes(
+ lT.p2,
+ {x: x + width, y: y + flickSize},
+ {move: false, var1: 0}
+ );
+ const lB = this.lineNodes(
+ {x: x + width, y: y + height},
+ {x, y: y + height},
+ {move: false}
+ );
+ const lR = this.lineNodes(
+ lF.p2,
+ lB.p1,
+ {var1: 0, var2: 0, move: false}
+ );
+ const lL = this.lineNodes(
+ lB.p2,
+ lT.p1,
+ {var1: 0, var2: 0.3, move: false}
+ );
+ const lF1 = this.lineNodes(
+ lF.p1,
+ {x: x + width - flickSize, y: y + flickSize},
+ {var1: 0.3}
+ );
+ const lF2 = this.lineNodes(
+ lF1.p2,
+ lF.p2,
+ {var1: 0, move: false}
+ );
+
+ const g = svg.make('g');
+ g.appendChild(svg.make('path', {
+ 'd': (
+ lT.nodes +
+ lF.nodes +
+ lR.nodes +
+ lB.nodes +
+ lL.nodes
+ ),
+ 'stroke': 'rgba(0,0,0,0.7)',
+ 'stroke-width': 0.8,
+ 'fill': '#FFFFFF',
+ }));
+ g.appendChild(svg.make('path', {
+ 'd': lF1.nodes + lF2.nodes,
+ 'stroke': 'rgba(0,0,0,0.7)',
+ 'stroke-width': 0.8,
+ 'fill': 'none',
+ }));
+
+ return g;
+ }
+
+ renderState({x, y, width, height}) {
+ // TODO: rounded corners
+ return this.renderBox({x, y, width, height});
+ }
+
+ renderBar({x, y, width, height}) {
+ return this.renderBox({x, y, width, height}, {fill: '#000000'});
+ }
+
+ renderCross({x, y, radius}) {
+ const r1 = (this.vary(0.2) + 1) * radius;
+ const l1 = this.lineNodes(
+ {x: x - r1, y: y - r1},
+ {x: x + r1, y: y + r1},
+ {}
+ );
+ const r2 = (this.vary(0.2) + 1) * radius;
+ const l2 = this.lineNodes(
+ {x: x + r2, y: y - r2},
+ {x: x - r2, y: y + r2},
+ {}
+ );
+
+ return svg.make('path', {
+ 'd': l1.nodes + l2.nodes,
+ 'stroke': 'rgba(0,0,0,0.7)',
+ 'stroke-width': 0.8,
+ 'fill': 'none',
+ });
+ }
+
+ getNote(type) {
+ return NOTES[type];
+ }
+
+ drawAgentLine(container, {x, y0, y1, width, className}) {
+ if(width > 0) {
+ const shape = this.renderBox({
+ x: x - width / 2,
+ y: y0,
+ width,
+ height: y1 - y0,
+ }, {fill: 'none'});
+ shape.setAttribute('class', className);
+ container.appendChild(shape);
+ } else {
+ const shape = this.renderLine(
+ {x, y: y0},
+ {x, y: y1},
+ {}
+ );
+ shape.setAttribute('class', className);
+ container.appendChild(shape);
+ }
+ }
+ }
+
+ SketchTheme.RIGHT = RIGHT;
+ SketchTheme.LEFT = LEFT;
+
+ return SketchTheme;
+});
+
/* jshint -W072 */ // Allow several required modules
define('sequence/SequenceDiagram',[
'core/EventObject',
@@ -5852,6 +6946,7 @@ define('sequence/SequenceDiagram',[
'./Exporter',
'./themes/Basic',
'./themes/Chunky',
+ './themes/Sketch',
], (
EventObject,
Parser,
@@ -5859,7 +6954,8 @@ define('sequence/SequenceDiagram',[
Renderer,
Exporter,
BasicTheme,
- ChunkyTheme
+ ChunkyTheme,
+ SketchTheme
) => {
/* jshint +W072 */
'use strict';
@@ -5867,6 +6963,8 @@ define('sequence/SequenceDiagram',[
const themes = [
new BasicTheme(),
new ChunkyTheme(),
+ new SketchTheme(SketchTheme.RIGHT),
+ new SketchTheme(SketchTheme.LEFT),
];
const SharedParser = new Parser();
diff --git a/lib/sequence-diagram.min.js b/lib/sequence-diagram.min.js
index ba7ed38..3a18f0d 100644
--- a/lib/sequence-diagram.min.js
+++ b/lib/sequence-diagram.min.js
@@ -1 +1 @@
-!function(){var e,t,n;!function(s){function r(e,t){return x.call(e,t)}function i(e,t){var n,s,r,i,a,o,h,l,g,d,c,u=t&&t.split("/"),p=b.map,m=p&&p["*"]||{};if(e){for(a=(e=e.split("/")).length-1,b.nodeIdCompat&&w.test(e[a])&&(e[a]=e[a].replace(w,"")),"."===e[0].charAt(0)&&u&&(e=u.slice(0,u.length-1).concat(e)),g=0;g0&&(e.splice(g-1,2),g-=2)}e=e.join("/")}if((u||m)&&p){for(g=(n=e.split("/")).length;g>0;g-=1){if(s=n.slice(0,g).join("/"),u)for(d=u.length;d>0;d-=1)if((r=p[u.slice(0,d).join("/")])&&(r=r[s])){i=r,o=g;break}if(i)break;!h&&m&&m[s]&&(h=m[s],l=g)}!i&&h&&(i=h,o=l),i&&(n.splice(0,o,i),e=n.join("/"))}return e}function a(e,t){return function(){var n=y.call(arguments,0);return"string"!=typeof n[0]&&1===n.length&&n.push(null),c.apply(s,n.concat([e,t]))}}function o(e){return function(t){m[e]=t}}function h(e){if(r(f,e)){var t=f[e];delete f[e],k[e]=!0,d.apply(s,t)}if(!r(m,e)&&!r(k,e))throw new Error("No "+e);return m[e]}function l(e){var t,n=e?e.indexOf("!"):-1;return n>-1&&(t=e.substring(0,n),e=e.substring(n+1,e.length)),[t,e]}function g(e){return e?l(e):[]}var d,c,u,p,m={},f={},b={},k={},x=Object.prototype.hasOwnProperty,y=[].slice,w=/\.js$/;u=function(e,t){var n,s=l(e),r=s[0],a=t[1];return e=s[1],r&&(n=h(r=i(r,a))),r?e=n&&n.normalize?n.normalize(e,function(e){return function(t){return i(t,e)}}(a)):i(e,a):(r=(s=l(e=i(e,a)))[0],e=s[1],r&&(n=h(r))),{f:r?r+"!"+e:e,n:e,pr:r,p:n}},p={require:function(e){return a(e)},exports:function(e){var t=m[e];return void 0!==t?t:m[e]={}},module:function(e){return{id:e,uri:"",exports:m[e],config:function(e){return function(){return b&&b.config&&b.config[e]||{}}}(e)}}},d=function(e,t,n,i){var l,d,c,b,x,y,w,A=[],v=typeof n;if(i=i||e,y=g(i),"undefined"===v||"function"===v){for(t=!t.length&&n.length?["require","exports","module"]:t,x=0;x{"use strict";return class{constructor(){this.listeners=new Map,this.forwards=new Set}addEventListener(e,t){const n=this.listeners.get(e);n?n.push(t):this.listeners.set(e,[t])}removeEventListener(e,t){const n=this.listeners.get(e);if(!n)return;const s=n.indexOf(t);-1!==s&&n.splice(s,1)}countEventListeners(e){return(this.listeners.get(e)||[]).length}removeAllEventListeners(e){e?this.listeners.delete(e):this.listeners.clear()}addEventForwarding(e){this.forwards.add(e)}removeEventForwarding(e){this.forwards.delete(e)}removeAllEventForwardings(){this.forwards.clear()}trigger(e,t=[]){(this.listeners.get(e)||[]).forEach(e=>e.apply(null,t)),this.forwards.forEach(n=>n.trigger(e,t))}}}),n("core/ArrayUtilities",[],()=>{"use strict";function e(e,t,n=null){if(null===n)return e.indexOf(t);for(let s=0;s=e.length)return void r.push(s.slice());const i=e[n];if(!Array.isArray(i))return s.push(i),t(e,n+1,s,r),void s.pop();for(let a=0;a{n.push(...t(e))}),n}}}),n("sequence/CodeMirrorMode",["core/ArrayUtilities"],e=>{"use strict";function t(e,t,n,s){return""===t?function(e,t,n){return"object"==typeof n.suggest&&n.suggest.global?[n.suggest]:"string"!=typeof n.suggest||t.suggest===n.suggest?null:e["known"+n.suggest]}(e,n,s):!0===s.suggest?[function(e,t){return Object.keys(t.then).length>0?e+" ":e+"\n"}(t,s)]:Array.isArray(s.suggest)?s.suggest:s.suggest?[s.suggest]:null}function n(n,s){const r=[],i=e.last(s);return Object.keys(i.then).forEach(a=>{let o=i.then[a];"number"==typeof o&&(o=s[s.length-o-1]),e.mergeSets(r,t(n,a,i,o))}),r}function s(t,n,s,{suggest:r,override:i}){n.type&&r!==n.type&&(i&&(n.type=i),e.mergeSets(t["known"+n.type],[n.value+" "]),n.type="",n.value=""),"string"==typeof r&&t["known"+r]&&(n.type=r,n.value&&(n.value+=s.s),n.value+=s.v)}function r(t,r,i){const o={type:"",value:""};let h=i;const l=[h];return t.line.forEach((r,i)=>{i===t.line.length-1&&(t.completions=n(t,l));const g=r.q?"":r.v,d=h.then[g]||h.then[""];"number"==typeof d?l.length-=d:l.push(d||a),h=e.last(l),s(t,o,r,h)}),r&&s(t,o,null,{}),t.nextCompletions=n(t,l),t.valid=Boolean(h.then["\n"])||0===Object.keys(h.then).length,h.type}function i(e){const t=e.baseToken||{};return{value:t.v||"",quoted:t.q||!1}}const a={type:"error line-error",then:{"":0}},o=(()=>{function e(e){return{type:"string",then:Object.assign({"":0},e)}}function t(e){return{type:"variable",suggest:"Agent",then:Object.assign({},e,{"":0,",":{type:"operator",suggest:!0,then:{"":1}}})}}function n(e){return{type:"keyword",suggest:[e+" of ",e+": "],then:{of:{type:"keyword",suggest:!0,then:{"":h}},":":{type:"operator",suggest:!0,then:{"":i}},"":h}}}function s(e){const t={type:"operator",suggest:!0,then:{"+":a,"-":a,"*":a,"!":a,"":e}};return{"+":{type:"operator",suggest:!0,then:{"+":a,"-":a,"*":t,"!":a,"":e}},"-":{type:"operator",suggest:!0,then:{"+":a,"-":a,"*":t,"!":{type:"operator",then:{"+":a,"-":a,"*":a,"!":a,"":e}},"":e}},"*":{type:"operator",suggest:!0,then:{"+":t,"-":t,"*":a,"!":a,"":e}},"!":t,"":e}}const r={type:"",suggest:"\n",then:{}},i=e({"\n":r}),o={type:"variable",suggest:"Agent",then:{"":0,"\n":r,",":{type:"operator",suggest:!0,then:{"":1}},as:{type:"keyword",suggest:!0,then:{"":{type:"variable",suggest:"Agent",then:{"":0,",":{type:"operator",suggest:!0,then:{"":3}},"\n":r}}}}}},h=t({":":{type:"operator",suggest:!0,then:{"":i}}}),l={type:"variable",suggest:"Agent",then:{"":0,",":{type:"operator",suggest:!0,then:{"":h}},":":a}},g={type:"variable",suggest:"Agent",then:{"":0,",":a,":":{type:"operator",suggest:!0,then:{"":i}}}},d={type:"variable",suggest:"Agent",then:{"":0,":":{type:"operator",suggest:!0,then:{"":i,"\n":{type:"",then:{}}}},"\n":r}},c={":":{type:"operator",suggest:!0,then:{"":e({as:{type:"keyword",suggest:!0,then:{"":{type:"variable",suggest:"Agent",then:{"":0,"\n":r}}}}})}}},u={type:"keyword",suggest:!0,then:Object.assign({over:{type:"keyword",suggest:!0,then:{"":t(c)}}},c)},p={title:{type:"keyword",suggest:!0,then:{"":i}},theme:{type:"keyword",suggest:!0,then:{"":{type:"string",suggest:{global:"themes",suffix:"\n"},then:{"":0,"\n":r}}}},headers:{type:"keyword",suggest:!0,then:{none:{type:"keyword",suggest:!0,then:{}},cross:{type:"keyword",suggest:!0,then:{}},box:{type:"keyword",suggest:!0,then:{}},fade:{type:"keyword",suggest:!0,then:{}},bar:{type:"keyword",suggest:!0,then:{}}}},terminators:{type:"keyword",suggest:!0,then:{none:{type:"keyword",suggest:!0,then:{}},cross:{type:"keyword",suggest:!0,then:{}},box:{type:"keyword",suggest:!0,then:{}},fade:{type:"keyword",suggest:!0,then:{}},bar:{type:"keyword",suggest:!0,then:{}}}},define:{type:"keyword",suggest:!0,then:{"":o,as:a}},begin:{type:"keyword",suggest:!0,then:{"":o,reference:u,as:a}},end:{type:"keyword",suggest:!0,then:{"":o,as:a,"\n":r}},if:{type:"keyword",suggest:!0,then:{"":i,":":{type:"operator",suggest:!0,then:{"":i}},"\n":r}},else:{type:"keyword",suggest:["else\n","else if: "],then:{if:{type:"keyword",suggest:"if: ",then:{"":i,":":{type:"operator",suggest:!0,then:{"":i}}}},"\n":r}},repeat:{type:"keyword",suggest:!0,then:{"":i,":":{type:"operator",suggest:!0,then:{"":i}},"\n":r}},note:{type:"keyword",suggest:!0,then:{over:{type:"keyword",suggest:!0,then:{"":h}},left:n("left"),right:n("right"),between:{type:"keyword",suggest:!0,then:{"":l}}}},state:{type:"keyword",suggest:"state over ",then:{over:{type:"keyword",suggest:!0,then:{"":g}}}},text:{type:"keyword",suggest:!0,then:{left:n("left"),right:n("right")}},autolabel:{type:"keyword",suggest:!0,then:{off:{type:"keyword",suggest:!0,then:{}},"":i}},simultaneously:{type:"keyword",suggest:!0,then:{":":{type:"operator",suggest:!0,then:{}},with:{type:"keyword",suggest:!0,then:{"":{type:"variable",suggest:"Label",then:{"":0,":":{type:"operator",suggest:!0,then:{}}}}}}}}};return e=>({type:"error line-error",then:Object.assign({},p,function(e){const t={type:"keyword",suggest:!0,then:s(d)},n={"":0};return e.forEach(e=>n[e]=t),n[":"]={type:"operator",suggest:!0,override:"Label",then:{}},s({type:"variable",suggest:"Agent",then:n})}(e))})})();return class{constructor(e,t){this.tokenDefinitions=e,this.commands=o(t),this.lineComment="#"}startState(){return{currentType:-1,current:"",currentSpace:"",currentQuoted:!1,knownAgent:[],knownLabel:[],beginCompletions:n({},[this.commands]),completions:[],nextCompletions:[],valid:!0,line:[],indent:0}}_matchPattern(e,t,n){return t?(t.lastIndex=0,e.match(t,n)):null}_tokenBegin(e,t){t.currentSpace="";let n="";for(;;){if(e.eol())return!1;t.currentSpace+=n;for(let n=0;n{"use strict";function t(e,t,n){return t.lastIndex=n,t.exec(e)}function n(e){return"n"===e[1]?"\n":e[1]}function s(e,n,s){return s?function(e,n,s){if(s.escape){const r=t(e,s.escape,n);if(r)return{newBlock:null,end:!1,appendSpace:"",appendValue:s.escapeWith(r),skip:r[0].length}}const r=t(e,s.end,n);return r?{newBlock:null,end:!0,appendSpace:"",appendValue:"",skip:r[0].length}:{newBlock:null,end:!1,appendSpace:"",appendValue:e[n],skip:1}}(e,n,s):function(e,n){for(let s=0;s,])/y,end:/(?=[ \t\r\n:+\-~*!<>,])|$/y},{start:/(?=[\-~<>])/y,end:/(?=[^\-~<>])|$/y},{start:/,/y,baseToken:{v:","}},{start:/:/y,baseToken:{v:":"}},{start:/!/y,baseToken:{v:"!"}},{start:/\+/y,baseToken:{v:"+"}},{start:/\*/y,baseToken:{v:"*"}},{start:/\n/y,baseToken:{v:"\n"}}];class a{constructor(e){this.src=e,this.block=null,this.token=null,this.pos={i:0,ln:0,ch:0},this.reset()}isOver(){return this.pos.i>this.src.length}reset(){this.token={s:"",v:"",q:!1,b:null,e:null},this.block=null}beginToken(e){this.block=e.newBlock,Object.assign(this.token,this.block.baseToken),this.token.b=r(this.pos)}endToken(){let e=null;return this.block.omit||(this.token.e=r(this.pos),e=this.token),this.reset(),e}advance(){const e=s(this.src,this.pos.i,this.block);return e.newBlock&&this.beginToken(e),this.token.s+=e.appendSpace,this.token.v+=e.appendValue,function(e,t,n){for(let s=0;s{e.q||"\n"!==e.v?n.push(e):n.length>0&&(t.push(n),n=[])}),n.length>0&&t.push(n),t}}}),n("sequence/LabelPatternParser",[],()=>{"use strict";function e(e){const t=s.exec(e);return t&&t[1]?t[1].length:0}function t(t){if("label"===t)return{token:"label"};const n=t.indexOf(" ");let s=null,r=null;return-1===n?(s=t,r=[]):(s=t.substr(0,n),r=t.substr(n+1).split(",")),"inc"===s?function(t){let n=1,s=1,r=0;return t[0]&&(n=Number(t[0]),r=Math.max(r,e(t[0]))),t[1]&&(s=Number(t[1]),r=Math.max(r,e(t[1]))),{start:n,inc:s,dp:r}}(r):"<"+t+">"}const n=/(.*?)<([^<>]*)>/g,s=/\.([0-9]*)/;return function(e){const s=[];let r=null,i=0;for(n.lastIndex=0;r=n.exec(e);)r[1]&&s.push(r[1]),r[2]&&s.push(t(r[2])),i=n.lastIndex;const a=e.substr(i);return a&&s.push(a),s}}),n("sequence/CodeMirrorHints",["core/ArrayUtilities"],e=>{"use strict";function t(e,t){return{text:e,displayText:"\n"===e?"":e.trim(),className:"\n"===e?"pick-virtual":null,from:r.test(e)?t.squashFrom:t.wordFrom,to:i.test(e)?t.squashTo:t.wordTo}}function n({global:e,prefix:t="",suffix:n=""},s){const r=s[e];return r?r.map(e=>t+e+n):[]}const s=/^([ \t]*)(.*)$/,r=/^[ \t\r\n:,]/,i=/[ \t\r\n]$/;return{getHints:function(r,i){const a=r.getCursor(),o=r.getTokenAt(a);let h=o.string;o.end>a.ch&&(h=h.substr(0,a.ch-o.start));const l=s.exec(h);h=l[2];const g=o.start+l[1].length,d=a.ch>0&&o.state.line.length>0;let c=d?o.state.completions:o.state.beginCompletions;d||(c=c.concat(o.state.knownAgent)),function(t,s={}){for(let r=0;r0&&" "===r[n-1]&&i.squashFrom.ch--," "===r[s]&&i.squashTo.ch++,i}(r,a.line,g,o.end);let p=!1;const m=c.filter(e=>e.startsWith(h)).map(e=>e!==h+" "||i.completeSingle?t(e,u):(p=!0,null)).filter(e=>null!==e);return p&&m.length>0&&m.unshift(t(h+" ",u)),{list:m,from:u.wordFrom,to:u.wordTo}}}}),n("sequence/Parser",["core/ArrayUtilities","./Tokeniser","./LabelPatternParser","./CodeMirrorHints"],(e,t,n,s)=>{"use strict";function r(e,t=null){let n="";return t&&(n=" at line "+(t.b.ln+1)+", character "+t.b.ch),new Error(e+n)}function i(e,t=0,n=null){if(null===n&&(n=e.length),n<=t)return"";let s=e[t].v;for(let r=t+1;r=s)&&(o=s),n>=o)throw r("Missing agent name",function(t,n){if(n{const t=e.combine([[{tok:"",type:0},{tok:"<",type:1},{tok:"<<",type:2}],[{tok:"-",type:"solid"},{tok:"--",type:"dash"},{tok:"~",type:"wave"}],[{tok:"",type:0},{tok:">",type:1},{tok:">>",type:2}]]).filter(e=>0!==e[0].type||0!==e[2].type),n=new Map;return t.forEach(e=>{n.set(e.map(e=>e.tok).join(""),{line:e[1].type,left:e[0].type,right:e[2].type})}),n})(),p={"*":"begin","+":"start","-":"stop","!":"end"},m=["none","box","cross","fade","bar"],f={text:{mode:"text",types:{left:{type:"note left",skip:["of"],min:0,max:null},right:{type:"note right",skip:["of"],min:0,max:null}}},note:{mode:"note",types:{over:{type:"note over",skip:[],min:0,max:null},left:{type:"note left",skip:["of"],min:0,max:null},right:{type:"note right",skip:["of"],min:0,max:null},between:{type:"note between",skip:[],min:2,max:null}}},state:{mode:"state",types:{over:{type:"note over",skip:[],min:1,max:1}}}},b={define:{type:"agent define"},begin:{type:"agent begin",mode:"box"},end:{type:"agent end",mode:"cross"}},k=[(e,t)=>"title"!==a(e[0])?null:(t.title=i(e,1),!0),(e,t)=>"theme"!==a(e[0])?null:(t.theme=i(e,1),!0),(e,t)=>{if("terminators"!==a(e[0]))return null;const n=a(e[1]);if(!n)throw r("Unspecified termination",e[0]);if(-1===m.indexOf(n))throw r('Unknown termination "'+n+'"',e[1]);return t.terminators=n,!0},(e,t)=>{if("headers"!==a(e[0]))return null;const n=a(e[1]);if(!n)throw r("Unspecified header",e[0]);if(-1===m.indexOf(n))throw r('Unknown header "'+n+'"',e[1]);return t.headers=n,!0},e=>{if("autolabel"!==a(e[0]))return null;let t=null;return t="off"===a(e[1])?"
+
+Also thanks to
+the Handlee font
+which is used in the sketch theme.
+
-
+
+ theme sketch
begin User, SequenceDiagram as SD, Parser, Generator, Renderer
User -> +SD: code
diff --git a/readme_images.htm b/readme_images.htm
index a713944..7645766 100644
--- a/readme_images.htm
+++ b/readme_images.htm
@@ -5,7 +5,10 @@
base-uri 'self';
default-src 'none';
script-src 'self' https://cdnjs.cloudflare.com;
- style-src 'self';
+ style-src 'self'
+ 'sha256-ru2GY2rXeOf7PQX5LzK3ckNo21FCDUoRc2f3i0QcD1g='
+ ;
+ font-src 'self' data:;
connect-src 'self';
img-src 'self' blob:;
form-action 'none';
diff --git a/screenshots/Themes.png b/screenshots/Themes.png
new file mode 100644
index 0000000..f5e1d3f
Binary files /dev/null and b/screenshots/Themes.png differ
diff --git a/scripts/editor.js b/scripts/editor.js
index 92e99c2..b746927 100644
--- a/scripts/editor.js
+++ b/scripts/editor.js
@@ -204,6 +204,11 @@
code: 'theme chunky',
preview: 'headers box\ntheme chunky\nA -> B',
},
+ {
+ title: 'Sketch theme',
+ code: 'theme sketch',
+ preview: 'headers box\ntheme sketch\nA -> B',
+ },
{
title: 'Cross terminators',
code: 'terminators cross',
diff --git a/scripts/sequence/Renderer.js b/scripts/sequence/Renderer.js
index 89f5f51..38448d5 100644
--- a/scripts/sequence/Renderer.js
+++ b/scripts/sequence/Renderer.js
@@ -240,25 +240,13 @@ define([
return;
}
- const r = agentInfo.currentRad;
-
- if(r > 0) {
- this.agentLines.appendChild(svg.make('rect', Object.assign({
- 'x': agentInfo.x - r,
- 'y': agentInfo.latestYStart,
- 'width': r * 2,
- 'height': toY - agentInfo.latestYStart,
- 'class': 'agent-' + agentInfo.index + '-line',
- }, this.theme.agentLineAttrs)));
- } else {
- this.agentLines.appendChild(svg.make('line', Object.assign({
- 'x1': agentInfo.x,
- 'y1': agentInfo.latestYStart,
- 'x2': agentInfo.x,
- 'y2': toY,
- 'class': 'agent-' + agentInfo.index + '-line',
- }, this.theme.agentLineAttrs)));
- }
+ this.theme.drawAgentLine(this.agentLines, {
+ x: agentInfo.x,
+ y0: agentInfo.latestYStart,
+ y1: toY,
+ width: agentInfo.currentRad * 2,
+ className: 'agent-' + agentInfo.index + '-line',
+ });
}
addHighlightObject(line, o) {
@@ -475,6 +463,9 @@ define([
this.theme = this.themes.get('');
}
+ this.theme.reset();
+ this.theme.addDefs(this.addDef);
+
this.title.set({
attrs: this.theme.titleAttrs,
text: sequence.meta.title,
diff --git a/scripts/sequence/SequenceDiagram.js b/scripts/sequence/SequenceDiagram.js
index 40970c5..bb39769 100644
--- a/scripts/sequence/SequenceDiagram.js
+++ b/scripts/sequence/SequenceDiagram.js
@@ -7,6 +7,7 @@ define([
'./Exporter',
'./themes/Basic',
'./themes/Chunky',
+ './themes/Sketch',
], (
EventObject,
Parser,
@@ -14,7 +15,8 @@ define([
Renderer,
Exporter,
BasicTheme,
- ChunkyTheme
+ ChunkyTheme,
+ SketchTheme
) => {
/* jshint +W072 */
'use strict';
@@ -22,6 +24,8 @@ define([
const themes = [
new BasicTheme(),
new ChunkyTheme(),
+ new SketchTheme(SketchTheme.RIGHT),
+ new SketchTheme(SketchTheme.LEFT),
];
const SharedParser = new Parser();
diff --git a/scripts/sequence/SequenceDiagram_spec.js b/scripts/sequence/SequenceDiagram_spec.js
index 8020f63..7d07581 100644
--- a/scripts/sequence/SequenceDiagram_spec.js
+++ b/scripts/sequence/SequenceDiagram_spec.js
@@ -41,7 +41,7 @@ defineDescribe('SequenceDiagram', [
});
it('provides default themes', () => {
- expect(SequenceDiagram.themes.length).toEqual(2);
+ expect(SequenceDiagram.themes.length).toBeGreaterThan(1);
});
it('renders empty diagrams without error', () => {
diff --git a/scripts/sequence/components/AgentCap.js b/scripts/sequence/components/AgentCap.js
index 1b7e661..c58934e 100644
--- a/scripts/sequence/components/AgentCap.js
+++ b/scripts/sequence/components/AgentCap.js
@@ -45,6 +45,7 @@ define([
y,
padding: config.padding,
boxAttrs: config.boxAttrs,
+ boxRenderer: config.boxRenderer,
labelAttrs: config.labelAttrs,
boxLayer: env.shapeLayer,
labelLayer: clickable,
@@ -85,14 +86,7 @@ define([
const config = env.theme.agentCap.cross;
const d = config.size / 2;
- env.shapeLayer.appendChild(svg.make('path', Object.assign({
- 'd': (
- 'M ' + (x - d) + ' ' + y +
- ' L ' + (x + d) + ' ' + (y + d * 2) +
- ' M ' + (x + d) + ' ' + y +
- ' L ' + (x - d) + ' ' + (y + d * 2)
- ),
- }, config.attrs)));
+ env.shapeLayer.appendChild(config.render({x, y: y + d, radius: d}));
env.makeRegion().appendChild(svg.make('rect', {
'x': x - d,
@@ -128,7 +122,7 @@ define([
topShift(agentInfo, env) {
const config = env.theme.agentCap.bar;
- return config.attrs.height / 2;
+ return config.height / 2;
}
render(y, {x, label}, env) {
@@ -139,25 +133,27 @@ define([
configB.padding.left +
configB.padding.right
);
+ const height = config.height;
- env.shapeLayer.appendChild(svg.make('rect', Object.assign({
- 'x': x - width / 2,
- 'y': y,
- 'width': width,
- }, config.attrs)));
+ env.shapeLayer.appendChild(config.render({
+ x: x - width / 2,
+ y,
+ width,
+ height,
+ }));
env.makeRegion().appendChild(svg.make('rect', {
'x': x - width / 2,
'y': y,
'width': width,
- 'height': config.attrs.height,
+ 'height': height,
'fill': 'transparent',
}));
return {
lineTop: 0,
- lineBottom: config.attrs.height,
- height: config.attrs.height,
+ lineBottom: height,
+ height: height,
};
}
}
diff --git a/scripts/sequence/components/Connect.js b/scripts/sequence/components/Connect.js
index 5382696..812c467 100644
--- a/scripts/sequence/components/Connect.js
+++ b/scripts/sequence/components/Connect.js
@@ -143,10 +143,10 @@ define([
if(!ww || !hh) {
container.appendChild(svg.make('path', Object.assign({
'd': (
- 'M ' + xL1 + ' ' + y1 +
- ' L ' + xR + ' ' + y1 +
- ' A ' + r + ' ' + r + ' 0 0 1 ' + xR + ' ' + y2 +
- ' L ' + xL2 + ' ' + y2
+ 'M' + xL1 + ' ' + y1 +
+ 'L' + xR + ' ' + y1 +
+ 'A' + r + ' ' + r + ' 0 0 1 ' + xR + ' ' + y2 +
+ 'L' + xL2 + ' ' + y2
),
}, attrs)));
return;
diff --git a/scripts/sequence/components/Note.js b/scripts/sequence/components/Note.js
index 277a27c..ccbc93c 100644
--- a/scripts/sequence/components/Note.js
+++ b/scripts/sequence/components/Note.js
@@ -32,7 +32,7 @@ define(['./BaseComponent', 'svg/SVGUtilities'], (BaseComponent, svg) => {
mode,
label,
}, env) {
- const config = env.theme.note[mode];
+ const config = env.theme.getNote(mode);
const clickable = env.makeRegion();
@@ -106,7 +106,7 @@ define(['./BaseComponent', 'svg/SVGUtilities'], (BaseComponent, svg) => {
class NoteOver extends NoteComponent {
separation({agentNames, mode, label}, env) {
- const config = env.theme.note[mode];
+ const config = env.theme.getNote(mode);
const width = (
env.textSizer.measure(config.labelAttrs, label).width +
config.padding.left +
@@ -133,7 +133,7 @@ define(['./BaseComponent', 'svg/SVGUtilities'], (BaseComponent, svg) => {
}
render({agentNames, mode, label}, env) {
- const config = env.theme.note[mode];
+ const config = env.theme.getNote(mode);
const {left, right} = findExtremes(env.agentInfos, agentNames);
const infoL = env.agentInfos.get(left);
@@ -165,7 +165,7 @@ define(['./BaseComponent', 'svg/SVGUtilities'], (BaseComponent, svg) => {
}
separation({agentNames, mode, label}, env) {
- const config = env.theme.note[mode];
+ const config = env.theme.getNote(mode);
const {left, right} = findExtremes(env.agentInfos, agentNames);
const width = (
env.textSizer.measure(config.labelAttrs, label).width +
@@ -191,7 +191,7 @@ define(['./BaseComponent', 'svg/SVGUtilities'], (BaseComponent, svg) => {
}
render({agentNames, mode, label}, env) {
- const config = env.theme.note[mode];
+ const config = env.theme.getNote(mode);
const {left, right} = findExtremes(env.agentInfos, agentNames);
if(this.isRight) {
const info = env.agentInfos.get(right);
@@ -217,7 +217,7 @@ define(['./BaseComponent', 'svg/SVGUtilities'], (BaseComponent, svg) => {
class NoteBetween extends NoteComponent {
separation({agentNames, mode, label}, env) {
- const config = env.theme.note[mode];
+ const config = env.theme.getNote(mode);
const {left, right} = findExtremes(env.agentInfos, agentNames);
const infoL = env.agentInfos.get(left);
const infoR = env.agentInfos.get(right);
diff --git a/scripts/sequence/themes/Basic.js b/scripts/sequence/themes/Basic.js
index 8ccc18b..6681b36 100644
--- a/scripts/sequence/themes/Basic.js
+++ b/scripts/sequence/themes/Basic.js
@@ -1,6 +1,15 @@
-define(['core/ArrayUtilities', 'svg/SVGShapes'], (array, SVGShapes) => {
+define([
+ 'core/ArrayUtilities',
+ 'svg/SVGUtilities',
+ 'svg/SVGShapes',
+], (
+ array,
+ svg,
+ SVGShapes
+) => {
'use strict';
+ const FONT = 'sans-serif';
const LINE_HEIGHT = 1.3;
const SETTINGS = {
@@ -26,7 +35,7 @@ define(['core/ArrayUtilities', 'svg/SVGShapes'], (array, SVGShapes) => {
'stroke-width': 1,
},
labelAttrs: {
- 'font-family': 'sans-serif',
+ 'font-family': FONT,
'font-size': 12,
'line-height': LINE_HEIGHT,
'text-anchor': 'middle',
@@ -34,18 +43,32 @@ define(['core/ArrayUtilities', 'svg/SVGShapes'], (array, SVGShapes) => {
},
cross: {
size: 20,
- attrs: {
- 'fill': 'none',
- 'stroke': '#000000',
- 'stroke-width': 1,
+ render: ({x, y, radius}) => {
+ return svg.make('path', {
+ 'd': (
+ 'M' + (x - radius) + ' ' + (y - radius) +
+ 'l' + (radius * 2) + ' ' + (radius * 2) +
+ 'm0 ' + (-radius * 2) +
+ 'l' + (-radius * 2) + ' ' + (radius * 2)
+ ),
+ 'fill': 'none',
+ 'stroke': '#000000',
+ 'stroke-width': 1,
+ });
},
},
bar: {
- attrs: {
- 'fill': '#000000',
- 'stroke': '#000000',
- 'stroke-width': 1,
- 'height': 4,
+ height: 4,
+ render: ({x, y, width, height}) => {
+ return svg.make('rect', {
+ 'x': x,
+ 'y': y,
+ 'width': width,
+ 'height': height,
+ 'fill': '#000000',
+ 'stroke': '#000000',
+ 'stroke-width': 1,
+ });
},
},
fade: {
@@ -106,13 +129,13 @@ define(['core/ArrayUtilities', 'svg/SVGShapes'], (array, SVGShapes) => {
padding: 6,
margin: {top: 2, bottom: 1},
attrs: {
- 'font-family': 'sans-serif',
+ 'font-family': FONT,
'font-size': 8,
'line-height': LINE_HEIGHT,
'text-anchor': 'middle',
},
loopbackAttrs: {
- 'font-family': 'sans-serif',
+ 'font-family': FONT,
'font-size': 8,
'line-height': LINE_HEIGHT,
},
@@ -172,7 +195,7 @@ define(['core/ArrayUtilities', 'svg/SVGShapes'], (array, SVGShapes) => {
'ry': 2,
},
labelAttrs: {
- 'font-family': 'sans-serif',
+ 'font-family': FONT,
'font-weight': 'bold',
'font-size': 9,
'line-height': LINE_HEIGHT,
@@ -187,7 +210,7 @@ define(['core/ArrayUtilities', 'svg/SVGShapes'], (array, SVGShapes) => {
bottom: 0,
},
labelAttrs: {
- 'font-family': 'sans-serif',
+ 'font-family': FONT,
'font-size': 8,
'line-height': LINE_HEIGHT,
'text-anchor': 'left',
@@ -203,60 +226,8 @@ define(['core/ArrayUtilities', 'svg/SVGShapes'], (array, SVGShapes) => {
},
},
- note: {
- '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: SVGShapes.renderBox.bind(null, {
- 'fill': '#FFFFFF',
- }),
- labelAttrs: {
- 'font-family': 'sans-serif',
- 'font-size': 8,
- 'line-height': LINE_HEIGHT,
- },
- },
- '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: SVGShapes.renderNote.bind(null, {
- 'fill': '#FFFFFF',
- 'stroke': '#000000',
- 'stroke-width': 1,
- }, {
- 'fill': 'none',
- 'stroke': '#000000',
- 'stroke-width': 1,
- }),
- labelAttrs: {
- 'font-family': 'sans-serif',
- 'font-size': 8,
- 'line-height': LINE_HEIGHT,
- },
- },
- '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: SVGShapes.renderBox.bind(null, {
- 'fill': '#FFFFFF',
- 'stroke': '#000000',
- 'stroke-width': 1,
- 'rx': 10,
- 'ry': 10,
- }),
- labelAttrs: {
- 'font-family': 'sans-serif',
- 'font-size': 8,
- 'line-height': LINE_HEIGHT,
- },
- },
- },
-
titleAttrs: {
- 'font-family': 'sans-serif',
+ 'font-family': FONT,
'font-size': 20,
'line-height': LINE_HEIGHT,
'text-anchor': 'middle',
@@ -270,10 +241,92 @@ define(['core/ArrayUtilities', 'svg/SVGShapes'], (array, SVGShapes) => {
},
};
+ const 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: SVGShapes.renderBox.bind(null, {
+ 'fill': '#FFFFFF',
+ }),
+ labelAttrs: {
+ 'font-family': FONT,
+ 'font-size': 8,
+ 'line-height': LINE_HEIGHT,
+ },
+ },
+ '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: SVGShapes.renderNote.bind(null, {
+ 'fill': '#FFFFFF',
+ 'stroke': '#000000',
+ 'stroke-width': 1,
+ }, {
+ 'fill': 'none',
+ 'stroke': '#000000',
+ 'stroke-width': 1,
+ }),
+ labelAttrs: {
+ 'font-family': FONT,
+ 'font-size': 8,
+ 'line-height': LINE_HEIGHT,
+ },
+ },
+ '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: SVGShapes.renderBox.bind(null, {
+ 'fill': '#FFFFFF',
+ 'stroke': '#000000',
+ 'stroke-width': 1,
+ 'rx': 10,
+ 'ry': 10,
+ }),
+ labelAttrs: {
+ 'font-family': FONT,
+ 'font-size': 8,
+ 'line-height': LINE_HEIGHT,
+ },
+ },
+ };
+
return class BasicTheme {
constructor() {
this.name = 'basic';
Object.assign(this, SETTINGS);
}
+
+ reset() {
+ }
+
+ addDefs() {
+ }
+
+ getNote(type) {
+ return NOTES[type];
+ }
+
+ drawAgentLine(container, {x, y0, y1, width, className}) {
+ if(width > 0) {
+ container.appendChild(svg.make('rect', Object.assign({
+ 'x': x - width / 2,
+ 'y': y0,
+ 'width': width,
+ 'height': y1 - y0,
+ 'class': className,
+ }, this.agentLineAttrs)));
+ } else {
+ container.appendChild(svg.make('line', Object.assign({
+ 'x1': x,
+ 'y1': y0,
+ 'x2': x,
+ 'y2': y1,
+ 'class': className,
+ }, this.agentLineAttrs)));
+ }
+ }
};
});
diff --git a/scripts/sequence/themes/Chunky.js b/scripts/sequence/themes/Chunky.js
index 30c658d..30eaaf5 100644
--- a/scripts/sequence/themes/Chunky.js
+++ b/scripts/sequence/themes/Chunky.js
@@ -1,6 +1,15 @@
-define(['core/ArrayUtilities', 'svg/SVGShapes'], (array, SVGShapes) => {
+define([
+ 'core/ArrayUtilities',
+ 'svg/SVGUtilities',
+ 'svg/SVGShapes',
+], (
+ array,
+ svg,
+ SVGShapes
+) => {
'use strict';
+ const FONT = 'sans-serif';
const LINE_HEIGHT = 1.3;
const SETTINGS = {
@@ -28,7 +37,7 @@ define(['core/ArrayUtilities', 'svg/SVGShapes'], (array, SVGShapes) => {
'ry': 4,
},
labelAttrs: {
- 'font-family': 'sans-serif',
+ 'font-family': FONT,
'font-weight': 'bold',
'font-size': 14,
'line-height': LINE_HEIGHT,
@@ -37,21 +46,36 @@ define(['core/ArrayUtilities', 'svg/SVGShapes'], (array, SVGShapes) => {
},
cross: {
size: 20,
- attrs: {
- 'fill': 'none',
- 'stroke': '#000000',
- 'stroke-width': 3,
- 'stroke-linecap': 'round',
+ render: ({x, y, radius}) => {
+ return svg.make('path', Object.assign({
+ 'd': (
+ 'M' + (x - radius) + ' ' + (y - radius) +
+ 'l' + (radius * 2) + ' ' + (radius * 2) +
+ 'm0 ' + (-radius * 2) +
+ 'l' + (-radius * 2) + ' ' + (radius * 2)
+ ),
+ }, {
+ 'fill': 'none',
+ 'stroke': '#000000',
+ 'stroke-width': 3,
+ 'stroke-linecap': 'round',
+ }));
},
},
bar: {
- attrs: {
- 'fill': '#000000',
- 'stroke': '#000000',
- 'stroke-width': 3,
- 'height': 4,
- 'rx': 2,
- 'ry': 2,
+ height: 4,
+ render: ({x, y, width, height}) => {
+ return svg.make('rect', {
+ 'x': x,
+ 'y': y,
+ 'width': width,
+ 'height': height,
+ 'fill': '#000000',
+ 'stroke': '#000000',
+ 'stroke-width': 3,
+ 'rx': 2,
+ 'ry': 2,
+ });
},
},
fade: {
@@ -114,13 +138,13 @@ define(['core/ArrayUtilities', 'svg/SVGShapes'], (array, SVGShapes) => {
padding: 7,
margin: {top: 2, bottom: 3},
attrs: {
- 'font-family': 'sans-serif',
+ 'font-family': FONT,
'font-size': 8,
'line-height': LINE_HEIGHT,
'text-anchor': 'middle',
},
loopbackAttrs: {
- 'font-family': 'sans-serif',
+ 'font-family': FONT,
'font-size': 8,
'line-height': LINE_HEIGHT,
},
@@ -180,7 +204,7 @@ define(['core/ArrayUtilities', 'svg/SVGShapes'], (array, SVGShapes) => {
'ry': 3,
},
labelAttrs: {
- 'font-family': 'sans-serif',
+ 'font-family': FONT,
'font-weight': 'bold',
'font-size': 9,
'line-height': LINE_HEIGHT,
@@ -195,7 +219,7 @@ define(['core/ArrayUtilities', 'svg/SVGShapes'], (array, SVGShapes) => {
bottom: 0,
},
labelAttrs: {
- 'font-family': 'sans-serif',
+ 'font-family': FONT,
'font-size': 8,
'line-height': LINE_HEIGHT,
'text-anchor': 'left',
@@ -211,61 +235,8 @@ define(['core/ArrayUtilities', 'svg/SVGShapes'], (array, SVGShapes) => {
},
},
- note: {
- '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: SVGShapes.renderBox.bind(null, {
- 'fill': '#FFFFFF',
- }),
- labelAttrs: {
- 'font-family': 'sans-serif',
- 'font-size': 8,
- 'line-height': LINE_HEIGHT,
- },
- },
- '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: SVGShapes.renderNote.bind(null, {
- 'fill': '#FFFFFF',
- 'stroke': '#000000',
- 'stroke-width': 2,
- 'stroke-linejoin': 'round',
- }, {
- 'fill': 'none',
- 'stroke': '#000000',
- 'stroke-width': 1,
- }),
- labelAttrs: {
- 'font-family': 'sans-serif',
- 'font-size': 8,
- 'line-height': LINE_HEIGHT,
- },
- },
- '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: SVGShapes.renderBox.bind(null, {
- 'fill': '#FFFFFF',
- 'stroke': '#000000',
- 'stroke-width': 3,
- 'rx': 10,
- 'ry': 10,
- }),
- labelAttrs: {
- 'font-family': 'sans-serif',
- 'font-size': 8,
- 'line-height': LINE_HEIGHT,
- },
- },
- },
-
titleAttrs: {
- 'font-family': 'sans-serif',
+ 'font-family': FONT,
'font-weight': 'bolder',
'font-size': 20,
'line-height': LINE_HEIGHT,
@@ -280,10 +251,93 @@ define(['core/ArrayUtilities', 'svg/SVGShapes'], (array, SVGShapes) => {
},
};
+ const 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: SVGShapes.renderBox.bind(null, {
+ 'fill': '#FFFFFF',
+ }),
+ labelAttrs: {
+ 'font-family': FONT,
+ 'font-size': 8,
+ 'line-height': LINE_HEIGHT,
+ },
+ },
+ '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: SVGShapes.renderNote.bind(null, {
+ 'fill': '#FFFFFF',
+ 'stroke': '#000000',
+ 'stroke-width': 2,
+ 'stroke-linejoin': 'round',
+ }, {
+ 'fill': 'none',
+ 'stroke': '#000000',
+ 'stroke-width': 1,
+ }),
+ labelAttrs: {
+ 'font-family': FONT,
+ 'font-size': 8,
+ 'line-height': LINE_HEIGHT,
+ },
+ },
+ '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: SVGShapes.renderBox.bind(null, {
+ 'fill': '#FFFFFF',
+ 'stroke': '#000000',
+ 'stroke-width': 3,
+ 'rx': 10,
+ 'ry': 10,
+ }),
+ labelAttrs: {
+ 'font-family': FONT,
+ 'font-size': 8,
+ 'line-height': LINE_HEIGHT,
+ },
+ },
+ };
+
return class ChunkyTheme {
constructor() {
this.name = 'chunky';
Object.assign(this, SETTINGS);
}
+
+ reset() {
+ }
+
+ addDefs() {
+ }
+
+ getNote(type) {
+ return NOTES[type];
+ }
+
+ drawAgentLine(container, {x, y0, y1, width, className}) {
+ if(width > 0) {
+ container.appendChild(svg.make('rect', Object.assign({
+ 'x': x - width / 2,
+ 'y': y0,
+ 'width': width,
+ 'height': y1 - y0,
+ 'class': className,
+ }, this.agentLineAttrs)));
+ } else {
+ container.appendChild(svg.make('line', Object.assign({
+ 'x1': x,
+ 'y1': y0,
+ 'x2': x,
+ 'y2': y1,
+ 'class': className,
+ }, this.agentLineAttrs)));
+ }
+ }
};
});
diff --git a/scripts/sequence/themes/HandleeFontData.js b/scripts/sequence/themes/HandleeFontData.js
new file mode 100644
index 0000000..13b22be
--- /dev/null
+++ b/scripts/sequence/themes/HandleeFontData.js
@@ -0,0 +1,434 @@
+define(() => {
+ 'use strict';
+
+ // Handlee font, by Joe Prince
+ // Downloaded from Google Fonts and converted to Base64 for embedding in
+ // generated SVGs
+ // https://fonts.google.com/specimen/Handlee
+
+ /* License
+
+ SIL OPEN FONT LICENSE
+ Version 1.1 - 26 February 2007
+
+ PREAMBLE
+ The goals of the Open Font License (OFL) are to stimulate worldwide
+ development of collaborative font projects, to support the font creation
+ efforts of academic and linguistic communities, and to provide a free and
+ open framework in which fonts may be shared and improved in partnership
+ with others.
+
+ The OFL allows the licensed fonts to be used, studied, modified and
+ redistributed freely as long as they are not sold by themselves. The
+ fonts, including any derivative works, can be bundled, embedded,
+ redistributed and/or sold with any software provided that any reserved
+ names are not used by derivative works. The fonts and derivatives,
+ however, cannot be released under any other type of license. The
+ requirement for fonts to remain under this license does not apply
+ to any document created using the fonts or their derivatives.
+
+ DEFINITIONS
+ "Font Software" refers to the set of files released by the Copyright
+ Holder(s) under this license and clearly marked as such. This may
+ include source files, build scripts and documentation.
+
+ "Reserved Font Name" refers to any names specified as such after the
+ copyright statement(s).
+
+ "Original Version" refers to the collection of Font Software components as
+ distributed by the Copyright Holder(s).
+
+ "Modified Version" refers to any derivative made by adding to, deleting,
+ or substituting — in part or in whole — any of the components of the
+ Original Version, by changing formats or by porting the Font Software to a
+ new environment.
+
+ "Author" refers to any designer, engineer, programmer, technical
+ writer or other person who contributed to the Font Software.
+
+ PERMISSION & CONDITIONS
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of the Font Software, to use, study, copy, merge, embed, modify,
+ redistribute, and sell modified and unmodified copies of the Font
+ Software, subject to the following conditions:
+
+ 1) Neither the Font Software nor any of its individual components,
+ in Original or Modified Versions, may be sold by itself.
+
+ 2) Original or Modified Versions of the Font Software may be bundled,
+ redistributed and/or sold with any software, provided that each copy
+ contains the above copyright notice and this license. These can be
+ included either as stand-alone text files, human-readable headers or
+ in the appropriate machine-readable metadata fields within text or
+ binary files as long as those fields can be easily viewed by the user.
+
+ 3) No Modified Version of the Font Software may use the Reserved Font
+ Name(s) unless explicit written permission is granted by the corresponding
+ Copyright Holder. This restriction only applies to the primary font name as
+ presented to the users.
+
+ 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+ Software shall not be used to promote, endorse or advertise any
+ Modified Version, except to acknowledge the contribution(s) of the
+ Copyright Holder(s) and the Author(s) or with their explicit written
+ permission.
+
+ 5) The Font Software, modified or unmodified, in part or in whole,
+ must be distributed entirely under this license, and must not be
+ distributed under any other license. The requirement for fonts to
+ remain under this license does not apply to any document created
+ using the Font Software.
+
+ TERMINATION
+ This license becomes null and void if any of the above conditions are
+ not met.
+
+ DISCLAIMER
+ THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+ OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+ COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+ DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+ OTHER DEALINGS IN THE FONT SOFTWARE.
+
+ */
+
+ return {
+ name: 'Handlee',
+ woff2: (
+ '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' +
+ 'jjhu2qURx1x+3R5nnI2W/z0OAAA='
+ ),
+ };
+});
diff --git a/scripts/sequence/themes/Sketch.js b/scripts/sequence/themes/Sketch.js
new file mode 100644
index 0000000..b90148e
--- /dev/null
+++ b/scripts/sequence/themes/Sketch.js
@@ -0,0 +1,564 @@
+define([
+ 'core/ArrayUtilities',
+ 'svg/SVGUtilities',
+ 'svg/SVGShapes',
+ './HandleeFontData',
+], (
+ array,
+ svg,
+ SVGShapes,
+ Handlee
+) => {
+ 'use strict';
+
+ // TODO:
+ // * arrows
+ // * fade starter/terminator sometimes does not fully cover line
+ // * blocks (if/else/repeat/ref)
+
+ const FONT = Handlee.name;
+ const FONT_FAMILY = '"' + FONT + '",cursive';
+ const LINE_HEIGHT = 1.5;
+
+ const SETTINGS = {
+ titleMargin: 10,
+ outerMargin: 5,
+ agentMargin: 10,
+ actionMargin: 10,
+ minActionMargin: 3,
+ agentLineHighlightRadius: 4,
+
+ agentCap: {
+ box: {
+ padding: {
+ top: 5,
+ left: 10,
+ right: 10,
+ bottom: 5,
+ },
+ arrowBottom: 5 + 12 * 1.3 / 2,
+ labelAttrs: {
+ 'font-family': FONT_FAMILY,
+ 'font-size': 12,
+ 'line-height': LINE_HEIGHT,
+ 'text-anchor': 'middle',
+ },
+ boxRenderer: null,
+ },
+ cross: {
+ size: 15,
+ render: null,
+ },
+ bar: {
+ height: 6,
+ render: null,
+ },
+ fade: {
+ width: 8,
+ height: 6,
+ },
+ none: {
+ height: 10,
+ },
+ },
+
+ connect: {
+ loopbackRadius: 6,
+ lineAttrs: {
+ 'solid': {
+ 'fill': 'none',
+ 'stroke': '#000000',
+ 'stroke-width': 1,
+ },
+ 'dash': {
+ 'fill': 'none',
+ 'stroke': '#000000',
+ 'stroke-width': 1,
+ 'stroke-dasharray': '4, 2',
+ },
+ 'wave': {
+ 'fill': 'none',
+ 'stroke': '#000000',
+ 'stroke-width': 1,
+ 'stroke-linejoin': 'round',
+ 'stroke-linecap': 'round',
+ 'wave-width': 6,
+ 'wave-height': 0.5,
+ },
+ },
+ arrow: {
+ single: {
+ width: 5,
+ height: 10,
+ attrs: {
+ 'fill': '#000000',
+ 'stroke-width': 0,
+ 'stroke-linejoin': 'miter',
+ },
+ },
+ double: {
+ width: 4,
+ height: 6,
+ attrs: {
+ 'fill': 'none',
+ 'stroke': '#000000',
+ 'stroke-width': 1,
+ 'stroke-linejoin': 'miter',
+ },
+ },
+ },
+ label: {
+ padding: 6,
+ margin: {top: 2, bottom: 1},
+ attrs: {
+ 'font-family': FONT_FAMILY,
+ 'font-size': 8,
+ 'line-height': LINE_HEIGHT,
+ 'text-anchor': 'middle',
+ },
+ loopbackAttrs: {
+ 'font-family': FONT_FAMILY,
+ 'font-size': 8,
+ 'line-height': LINE_HEIGHT,
+ },
+ },
+ mask: {
+ padding: {
+ top: 0,
+ left: 3,
+ right: 3,
+ bottom: 1,
+ },
+ },
+ },
+
+ block: {
+ margin: {
+ top: 0,
+ bottom: 0,
+ },
+ modes: {
+ 'ref': {
+ boxAttrs: {
+ 'fill': '#FFFFFF',
+ 'stroke': '#000000',
+ 'stroke-width': 1.5,
+ 'rx': 2,
+ 'ry': 2,
+ },
+ },
+ '': {
+ boxAttrs: {
+ 'fill': 'none',
+ 'stroke': '#000000',
+ 'stroke-width': 1.5,
+ 'rx': 2,
+ 'ry': 2,
+ },
+ },
+ },
+ section: {
+ padding: {
+ top: 3,
+ bottom: 2,
+ },
+ mode: {
+ padding: {
+ top: 1,
+ left: 3,
+ right: 3,
+ bottom: 0,
+ },
+ boxAttrs: {
+ 'fill': '#FFFFFF',
+ 'stroke': '#000000',
+ 'stroke-width': 1,
+ 'rx': 2,
+ 'ry': 2,
+ },
+ labelAttrs: {
+ 'font-family': FONT_FAMILY,
+ 'font-weight': 'bold',
+ 'font-size': 9,
+ 'line-height': LINE_HEIGHT,
+ 'text-anchor': 'left',
+ },
+ },
+ label: {
+ padding: {
+ top: 1,
+ left: 5,
+ right: 3,
+ bottom: 0,
+ },
+ labelAttrs: {
+ 'font-family': FONT_FAMILY,
+ 'font-size': 8,
+ 'line-height': LINE_HEIGHT,
+ 'text-anchor': 'left',
+ },
+ },
+ },
+ separator: {
+ attrs: {
+ 'stroke': '#000000',
+ 'stroke-width': 1.5,
+ 'stroke-dasharray': '4, 2',
+ },
+ },
+ },
+
+ titleAttrs: {
+ 'font-family': FONT_FAMILY,
+ 'font-size': 20,
+ 'line-height': LINE_HEIGHT,
+ 'text-anchor': 'middle',
+ 'class': 'title',
+ },
+
+ agentLineAttrs: {
+ 'fill': 'none',
+ 'stroke': '#000000',
+ 'stroke-width': 1,
+ },
+ };
+
+ const 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: SVGShapes.renderBox.bind(null, {
+ 'fill': '#FFFFFF',
+ }),
+ labelAttrs: {
+ 'font-family': FONT_FAMILY,
+ 'font-size': 8,
+ 'line-height': LINE_HEIGHT,
+ },
+ },
+ '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: null,
+ labelAttrs: {
+ 'font-family': FONT_FAMILY,
+ 'font-size': 8,
+ 'line-height': LINE_HEIGHT,
+ },
+ },
+ '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: null,
+ labelAttrs: {
+ 'font-family': FONT_FAMILY,
+ 'font-size': 8,
+ 'line-height': LINE_HEIGHT,
+ },
+ },
+ };
+
+ class Random {
+ // xorshift+ 64-bit random generator
+ // https://en.wikipedia.org/wiki/Xorshift
+
+ constructor() {
+ this.s = new Uint32Array(4);
+ }
+
+ reset() {
+ // Initial seed: 0x00010203
+ for(let i = 0; i < 4; ++ i) {
+ this.s[i] = i;
+ }
+ }
+
+ nextFloat() {
+ /* jshint -W016 */ // bit-operations are part of the algorithm
+ const range = 0x100000000;
+ let x0 = this.s[0];
+ let x1 = this.s[1];
+ const y0 = this.s[2];
+ const y1 = this.s[3];
+ this.s[0] = y0;
+ this.s[1] = y1;
+ x0 ^= (x0 << 23) | (x1 >>> 9);
+ x1 ^= (x1 << 23);
+ this.s[2] = x0 ^ y0 ^ (x0 >>> 17) ^ (y0 >>> 26);
+ this.s[3] = (
+ x1 ^ y1 ^
+ (x0 << 15 | x1 >>> 17) ^
+ (y0 << 6 | y1 >>> 26)
+ );
+ return (((this.s[3] + y1) >>> 0) % range) / range;
+ }
+ }
+
+ const RIGHT = {};
+ const LEFT = {};
+
+ class SketchTheme {
+ constructor(handedness = RIGHT) {
+ if(handedness === RIGHT) {
+ this.name = 'sketch';
+ this.handedness = 1;
+ } else {
+ this.name = 'sketch left handed';
+ this.handedness = -1;
+ }
+ this.random = new Random();
+ Object.assign(this, SETTINGS);
+ // TODO: these mutate the global, not our copy
+ // (fine for now since we're a singleton for all practical purposes)
+ this.agentCap.cross.render = this.renderCross.bind(this);
+ this.agentCap.bar.render = this.renderBar.bind(this);
+ this.agentCap.box.boxRenderer = this.renderBox.bind(this);
+ NOTES.note.boxRenderer = this.renderNote.bind(this);
+ NOTES.state.boxRenderer = this.renderState.bind(this);
+ }
+
+ reset() {
+ this.random.reset();
+ }
+
+ addDefs(builder) {
+ builder('sketch_font', () => {
+ const style = document.createElement('style');
+ // For some uses, it is fine to load this font externally,
+ // but this fails when exporting as SVG / PNG (svg tags must
+ // have no external dependencies).
+// const url = 'https://fonts.googleapis.com/css?family=' + FONT;
+// style.innerText = '@import url("' + url + '")';
+ style.innerText = (
+ '@font-face{' +
+ 'font-family:"' + Handlee.name + '";' +
+ 'src:url("data:font/woff2;base64,' + Handlee.woff2 + '");' +
+ '}'
+ );
+ return style;
+ });
+ }
+
+ vary(range) {
+ if(!range) {
+ return 0;
+ }
+ // cosine distribution [-pi/2 pi/2] scaled to [-range range]
+ // int(cos(x))dx = sin(x)
+ // from -pi/2: sin(x) - sin(-pi/2) = sin(x) + 1
+ // total: sin(pi/2) + 1 = 2
+ // normalise to area 1: /2
+ // (sin(x) + 1) / 2
+ // inverse: p = (sin(x) + 1) / 2
+ // asin(2p - 1) = x
+ // normalise range
+ // x = asin(2p - 1) * range / (pi/2)
+ const rand = this.random.nextFloat(); // [0 1)
+ return Math.asin(rand * 2 - 1) * 2 * range / Math.PI;
+ }
+
+ lineNodes(p1, p2, {var1 = 1, var2 = 1, move = true}) {
+ const length = Math.sqrt(
+ (p2.x - p1.x) * (p2.x - p1.x) +
+ (p2.y - p1.y) * (p2.y - p1.y)
+ );
+ const rough = Math.min(Math.sqrt(length) * 0.2, 5);
+ const x1 = p1.x + this.vary(var1 * rough);
+ const y1 = p1.y + this.vary(var1 * rough);
+ const x2 = p2.x + this.vary(var2 * rough);
+ const y2 = p2.y + this.vary(var2 * rough);
+
+ // -1 = p1 higher, 1 = p2 higher
+ const upper = Math.max(-1, Math.min(1,
+ (y1 - y2) / (Math.abs(x1 - x2) + 0.001)
+ ));
+ const frac = upper / 6 + 0.5;
+
+ // Line curve is to top / left (simulates right-handed drawing)
+ // or top / right (left-handed)
+ const curveX = (0.5 + this.vary(0.5)) * rough;
+ const curveY = (0.5 + this.vary(0.5)) * rough;
+ const xc = x1 * (1 - frac) + x2 * frac - curveX * this.handedness;
+ const yc = y1 * (1 - frac) + y2 * frac - curveY;
+ const nodes = (
+ (move ? ('M' + x1 + ' ' + y1) : '') +
+ 'C' + xc + ' ' + yc +
+ ',' + x2 + ' ' + y2 +
+ ',' + x2 + ' ' + y2
+ );
+ return {
+ nodes,
+ p1: {x: x1, y: y1},
+ p2: {x: x2, y: y2},
+ };
+ }
+
+ renderLine(p1, p2, {var1 = 1, var2 = 1}) {
+ const line = this.lineNodes(p1, p2, {var1, var2});
+ const shape = svg.make('path', {
+ 'd': line.nodes,
+ 'stroke': 'rgba(0,0,0,0.7)',
+ 'stroke-width': 0.8,
+ 'fill': 'none',
+ });
+ return shape;
+ }
+
+ renderBox({x, y, width, height}, {fill = null} = {}) {
+ const lT = this.lineNodes(
+ {x, y},
+ {x: x + width, y},
+ {}
+ );
+ const lB = this.lineNodes(
+ {x: x + width, y: y + height},
+ {x, y: y + height},
+ {move: false}
+ );
+ const lR = this.lineNodes(
+ lT.p2,
+ lB.p1,
+ {var1: 0, var2: 0, move: false}
+ );
+ const lL = this.lineNodes(
+ lB.p2,
+ lT.p1,
+ {var1: 0, var2: 0.3, move: false}
+ );
+
+ const shape = svg.make('path', {
+ 'd': lT.nodes + lR.nodes + lB.nodes + lL.nodes,
+ 'stroke': 'rgba(0,0,0,0.7)',
+ 'stroke-width': 0.8,
+ 'fill': fill || '#FFFFFF',
+ });
+
+ return shape;
+ }
+
+ renderNote({x, y, width, height}) {
+ const flickSize = 5;
+ const lT = this.lineNodes(
+ {x, y},
+ {x: x + width - flickSize, y},
+ {}
+ );
+ const lF = this.lineNodes(
+ lT.p2,
+ {x: x + width, y: y + flickSize},
+ {move: false, var1: 0}
+ );
+ const lB = this.lineNodes(
+ {x: x + width, y: y + height},
+ {x, y: y + height},
+ {move: false}
+ );
+ const lR = this.lineNodes(
+ lF.p2,
+ lB.p1,
+ {var1: 0, var2: 0, move: false}
+ );
+ const lL = this.lineNodes(
+ lB.p2,
+ lT.p1,
+ {var1: 0, var2: 0.3, move: false}
+ );
+ const lF1 = this.lineNodes(
+ lF.p1,
+ {x: x + width - flickSize, y: y + flickSize},
+ {var1: 0.3}
+ );
+ const lF2 = this.lineNodes(
+ lF1.p2,
+ lF.p2,
+ {var1: 0, move: false}
+ );
+
+ const g = svg.make('g');
+ g.appendChild(svg.make('path', {
+ 'd': (
+ lT.nodes +
+ lF.nodes +
+ lR.nodes +
+ lB.nodes +
+ lL.nodes
+ ),
+ 'stroke': 'rgba(0,0,0,0.7)',
+ 'stroke-width': 0.8,
+ 'fill': '#FFFFFF',
+ }));
+ g.appendChild(svg.make('path', {
+ 'd': lF1.nodes + lF2.nodes,
+ 'stroke': 'rgba(0,0,0,0.7)',
+ 'stroke-width': 0.8,
+ 'fill': 'none',
+ }));
+
+ return g;
+ }
+
+ renderState({x, y, width, height}) {
+ // TODO: rounded corners
+ return this.renderBox({x, y, width, height});
+ }
+
+ renderBar({x, y, width, height}) {
+ return this.renderBox({x, y, width, height}, {fill: '#000000'});
+ }
+
+ renderCross({x, y, radius}) {
+ const r1 = (this.vary(0.2) + 1) * radius;
+ const l1 = this.lineNodes(
+ {x: x - r1, y: y - r1},
+ {x: x + r1, y: y + r1},
+ {}
+ );
+ const r2 = (this.vary(0.2) + 1) * radius;
+ const l2 = this.lineNodes(
+ {x: x + r2, y: y - r2},
+ {x: x - r2, y: y + r2},
+ {}
+ );
+
+ return svg.make('path', {
+ 'd': l1.nodes + l2.nodes,
+ 'stroke': 'rgba(0,0,0,0.7)',
+ 'stroke-width': 0.8,
+ 'fill': 'none',
+ });
+ }
+
+ getNote(type) {
+ return NOTES[type];
+ }
+
+ drawAgentLine(container, {x, y0, y1, width, className}) {
+ if(width > 0) {
+ const shape = this.renderBox({
+ x: x - width / 2,
+ y: y0,
+ width,
+ height: y1 - y0,
+ }, {fill: 'none'});
+ shape.setAttribute('class', className);
+ container.appendChild(shape);
+ } else {
+ const shape = this.renderLine(
+ {x, y: y0},
+ {x, y: y1},
+ {}
+ );
+ shape.setAttribute('class', className);
+ container.appendChild(shape);
+ }
+ }
+ }
+
+ SketchTheme.RIGHT = RIGHT;
+ SketchTheme.LEFT = LEFT;
+
+ return SketchTheme;
+});
diff --git a/scripts/sequence/themes/Sketch_spec.js b/scripts/sequence/themes/Sketch_spec.js
new file mode 100644
index 0000000..8766602
--- /dev/null
+++ b/scripts/sequence/themes/Sketch_spec.js
@@ -0,0 +1,18 @@
+defineDescribe('Sketch Theme', ['./Sketch'], (SketchTheme) => {
+ 'use strict';
+
+ const theme = new SketchTheme(SketchTheme.RIGHT);
+ const themeL = new SketchTheme(SketchTheme.LEFT);
+
+ it('has a name', () => {
+ expect(theme.name).toEqual('sketch');
+ });
+
+ it('has a left-handed variant', () => {
+ expect(themeL.name).toEqual('sketch left handed');
+ });
+
+ it('contains settings for the theme', () => {
+ expect(theme.outerMargin).toEqual(5);
+ });
+});
diff --git a/scripts/specs.js b/scripts/specs.js
index 834d297..335ecaf 100644
--- a/scripts/specs.js
+++ b/scripts/specs.js
@@ -13,6 +13,7 @@ define([
'sequence/Renderer_spec',
'sequence/themes/Basic_spec',
'sequence/themes/Chunky_spec',
+ 'sequence/themes/Sketch_spec',
'sequence/components/AgentCap_spec',
'sequence/components/AgentHighlight_spec',
'sequence/components/Block_spec',
diff --git a/styles/library.css b/styles/library.css
index 3302300..bd23317 100644
--- a/styles/library.css
+++ b/styles/library.css
@@ -35,13 +35,6 @@ article > header > .sequence-diagram {
filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.2));
}
-svg.fancy {
- filter:
- sepia(1)
- hue-rotate(140deg)
- drop-shadow(0 5px 5px rgba(0, 64, 128, 0.5));
-}
-
article > h2 {
margin: 35px -20px 10px;
padding: 5px 20px 0;
@@ -56,12 +49,23 @@ article > h3 {
clear: both;
}
+article > h4 {
+ margin: 15px -20px 10px;
+ padding: 5px 20px 0;
+ font: 1.1em 'Vollkorn', serif;
+}
+
article > p {
margin: 20px 0;
padding: 0;
text-align: justify;
}
+article > ul {
+ margin: 10px 0;
+ padding: 0 0 0 30px;
+}
+
a:link, a:visited {
color: #556688;
text-decoration: underline;
diff --git a/test.htm b/test.htm
index 7db7023..e4994d8 100644
--- a/test.htm
+++ b/test.htm
@@ -6,7 +6,11 @@
default-src 'none';
script-src 'self' https://cdnjs.cloudflare.com;
connect-src 'self';
- style-src 'self' https://cdnjs.cloudflare.com;
+ style-src 'self'
+ https://cdnjs.cloudflare.com
+ 'sha256-ru2GY2rXeOf7PQX5LzK3ckNo21FCDUoRc2f3i0QcD1g='
+ ;
+ font-src 'self' data:;
img-src 'self' data: blob:;
form-action 'none';
">