From eaf4a956d4afb513a1a567f33d77dbcd36599a42 Mon Sep 17 00:00:00 2001
From: David Evans
Date: Fri, 5 Jan 2018 23:19:54 +0000
Subject: [PATCH] Add beginnings of sketch theme [#18]
---
README.md | 15 +
editor-dev.htm | 6 +-
index.html | 6 +-
lib/sequence-diagram.js | 1472 +++++++++++++++++---
lib/sequence-diagram.min.js | 2 +-
library.htm | 36 +-
readme_images.htm | 5 +-
screenshots/Themes.png | Bin 0 -> 30824 bytes
scripts/editor.js | 5 +
scripts/sequence/Renderer.js | 29 +-
scripts/sequence/SequenceDiagram.js | 6 +-
scripts/sequence/SequenceDiagram_spec.js | 2 +-
scripts/sequence/components/AgentCap.js | 30 +-
scripts/sequence/components/Connect.js | 8 +-
scripts/sequence/components/Note.js | 12 +-
scripts/sequence/themes/Basic.js | 189 ++-
scripts/sequence/themes/Chunky.js | 198 ++-
scripts/sequence/themes/HandleeFontData.js | 434 ++++++
scripts/sequence/themes/Sketch.js | 564 ++++++++
scripts/sequence/themes/Sketch_spec.js | 18 +
scripts/specs.js | 1 +
styles/library.css | 18 +-
test.htm | 6 +-
23 files changed, 2672 insertions(+), 390 deletions(-)
create mode 100644 screenshots/Themes.png
create mode 100644 scripts/sequence/themes/HandleeFontData.js
create mode 100644 scripts/sequence/themes/Sketch.js
create mode 100644 scripts/sequence/themes/Sketch_spec.js
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 0000000000000000000000000000000000000000..f5e1d3f482d7c791ac90106338d2e79b2afc883c
GIT binary patch
literal 30824
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
literal 0
HcmV?d00001
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';
">