Fix dividers not masking elements [#44]

This commit is contained in:
David Evans 2018-02-03 14:37:24 +00:00
parent ec24433366
commit b34b6ca0ae
21 changed files with 540 additions and 475 deletions

View File

@ -3382,13 +3382,16 @@ define('svg/SVGUtilities',[],() => {
return document.createTextNode(text); return document.createTextNode(text);
} }
function make(type, attrs = {}) { function make(type, attrs = {}, children = []) {
const o = document.createElementNS(NS, type); const o = document.createElementNS(NS, type);
for(let k in attrs) { for(const k in attrs) {
if(attrs.hasOwnProperty(k)) { if(attrs.hasOwnProperty(k)) {
o.setAttribute(k, attrs[k]); o.setAttribute(k, attrs[k]);
} }
} }
for(const c of children) {
o.appendChild(c);
}
return o; return o;
} }
@ -3429,7 +3432,7 @@ define('svg/SVGTextBlock',['./SVGUtilities'], (svg) => {
} }
function merge(state, newState) { function merge(state, newState) {
for(let k in state) { for(const k in state) {
if(state.hasOwnProperty(k)) { if(state.hasOwnProperty(k)) {
if(newState[k] !== null && newState[k] !== undefined) { if(newState[k] !== null && newState[k] !== undefined) {
state[k] = newState[k]; state[k] = newState[k];
@ -3445,8 +3448,7 @@ define('svg/SVGTextBlock',['./SVGUtilities'], (svg) => {
formattedLine.forEach(({text, attrs}) => { formattedLine.forEach(({text, attrs}) => {
const textNode = svg.makeText(text); const textNode = svg.makeText(text);
if(attrs) { if(attrs) {
const span = svg.make('tspan', attrs); const span = svg.make('tspan', attrs, [textNode]);
span.appendChild(textNode);
node.appendChild(span); node.appendChild(span);
} else { } else {
node.appendChild(textNode); node.appendChild(textNode);
@ -3775,14 +3777,14 @@ define('svg/SVGShapes',[
} }
function renderNote(attrs, flickAttrs, position) { function renderNote(attrs, flickAttrs, position) {
const g = svg.make('g');
const x0 = position.x; const x0 = position.x;
const x1 = position.x + position.width; const x1 = position.x + position.width;
const y0 = position.y; const y0 = position.y;
const y1 = position.y + position.height; const y1 = position.y + position.height;
const flick = 7; const flick = 7;
g.appendChild(svg.make('polygon', Object.assign({ return svg.make('g', {}, [
svg.make('polygon', Object.assign({
'points': ( 'points': (
x0 + ' ' + y0 + ' ' + x0 + ' ' + y0 + ' ' +
(x1 - flick) + ' ' + y0 + ' ' + (x1 - flick) + ' ' + y0 + ' ' +
@ -3790,17 +3792,15 @@ define('svg/SVGShapes',[
x1 + ' ' + y1 + ' ' + x1 + ' ' + y1 + ' ' +
x0 + ' ' + y1 x0 + ' ' + y1
), ),
}, attrs))); }, attrs)),
svg.make('polyline', Object.assign({
g.appendChild(svg.make('polyline', Object.assign({
'points': ( 'points': (
(x1 - flick) + ' ' + y0 + ' ' + (x1 - flick) + ' ' + y0 + ' ' +
(x1 - flick) + ' ' + (y0 + flick) + ' ' + (x1 - flick) + ' ' + (y0 + flick) + ' ' +
x1 + ' ' + (y0 + flick) x1 + ' ' + (y0 + flick)
), ),
}, flickAttrs))); }, flickAttrs)),
]);
return g;
} }
function calculateAnchor(x, attrs, padding) { function calculateAnchor(x, attrs, padding) {
@ -3938,8 +3938,6 @@ define('sequence/components/BaseComponent',[],() => {
primaryY, primaryY,
fillLayer, fillLayer,
blockLayer, blockLayer,
shapeLayer,
labelLayer,
theme, theme,
agentInfos, agentInfos,
textSizer, textSizer,
@ -4049,7 +4047,7 @@ define('sequence/components/Block',[
padding: config.section.label.padding, padding: config.section.label.padding,
boxAttrs: {'fill': '#000000'}, boxAttrs: {'fill': '#000000'},
labelAttrs: config.section.label.labelAttrs, labelAttrs: config.section.label.labelAttrs,
boxLayer: env.maskLayer, boxLayer: env.lineMaskLayer,
labelLayer: clickable, labelLayer: clickable,
SVGTextBlockClass: env.SVGTextBlockClass, SVGTextBlockClass: env.SVGTextBlockClass,
}); });
@ -4165,7 +4163,7 @@ define('sequence/components/Block',[
env.fillLayer.appendChild(shapes.fill); env.fillLayer.appendChild(shapes.fill);
} }
if(shapes.mask) { if(shapes.mask) {
env.maskLayer.appendChild(shapes.mask); env.lineMaskLayer.appendChild(shapes.mask);
} }
return env.primaryY + config.margin.bottom + env.theme.actionMargin; return env.primaryY + config.margin.bottom + env.theme.actionMargin;
@ -4243,8 +4241,10 @@ define('sequence/components/Parallel',[
const originalMakeRegion = env.makeRegion; const originalMakeRegion = env.makeRegion;
let bottomY = 0; let bottomY = 0;
stage.stages.forEach((subStage) => { stage.stages.forEach((subStage) => {
env.makeRegion = (o, stageOverride = null) => { env.makeRegion = (options = {}) => {
return originalMakeRegion(o, stageOverride || subStage); return originalMakeRegion(Object.assign({
stageOverride: subStage,
}, options));
}; };
const component = env.components.get(subStage.type); const component = env.components.get(subStage.type);
@ -4341,30 +4341,30 @@ define('sequence/components/AgentCap',[
render(y, {x, formattedLabel}, env) { render(y, {x, formattedLabel}, env) {
const config = env.theme.agentCap.box; const config = env.theme.agentCap.box;
const clickable = env.makeRegion(); const clickable = env.makeRegion();
const {width, height} = SVGShapes.renderBoxedText(formattedLabel, { const text = SVGShapes.renderBoxedText(formattedLabel, {
x, x,
y, y,
padding: config.padding, padding: config.padding,
boxAttrs: config.boxAttrs, boxAttrs: config.boxAttrs,
boxRenderer: config.boxRenderer, boxRenderer: config.boxRenderer,
labelAttrs: config.labelAttrs, labelAttrs: config.labelAttrs,
boxLayer: env.shapeLayer, boxLayer: clickable,
labelLayer: clickable, labelLayer: clickable,
SVGTextBlockClass: env.SVGTextBlockClass, SVGTextBlockClass: env.SVGTextBlockClass,
}); });
clickable.insertBefore(svg.make('rect', { clickable.insertBefore(svg.make('rect', {
'x': x - width / 2, 'x': x - text.width / 2,
'y': y, 'y': y,
'width': width, 'width': text.width,
'height': height, 'height': text.height,
'fill': 'transparent', 'fill': 'transparent',
'class': 'outline', 'class': 'outline',
}), clickable.firstChild); }), text.label.firstLine());
return { return {
lineTop: 0, lineTop: 0,
lineBottom: height, lineBottom: text.height,
height, height: text.height,
}; };
} }
} }
@ -4388,9 +4388,10 @@ define('sequence/components/AgentCap',[
const config = env.theme.agentCap.cross; const config = env.theme.agentCap.cross;
const d = config.size / 2; const d = config.size / 2;
env.shapeLayer.appendChild(config.render({x, y: y + d, radius: d})); const clickable = env.makeRegion();
env.makeRegion().appendChild(svg.make('rect', { clickable.appendChild(config.render({x, y: y + d, radius: d}));
clickable.appendChild(svg.make('rect', {
'x': x - d, 'x': x - d,
'y': y, 'y': y,
'width': d * 2, 'width': d * 2,
@ -4438,14 +4439,14 @@ define('sequence/components/AgentCap',[
); );
const height = barCfg.height; const height = barCfg.height;
env.shapeLayer.appendChild(barCfg.render({ const clickable = env.makeRegion();
clickable.appendChild(barCfg.render({
x: x - width / 2, x: x - width / 2,
y, y,
width, width,
height, height,
})); }));
clickable.appendChild(svg.make('rect', {
env.makeRegion().appendChild(svg.make('rect', {
'x': x - width / 2, 'x': x - width / 2,
'y': y, 'y': y,
'width': width, 'width': width,
@ -4481,24 +4482,24 @@ define('sequence/components/AgentCap',[
const ratio = config.height / (config.height + config.extend); const ratio = config.height / (config.height + config.extend);
const gradID = env.addDef(isBegin ? 'FadeIn' : 'FadeOut', () => { const gradID = env.addDef(isBegin ? 'FadeIn' : 'FadeOut', () => {
const grad = svg.make('linearGradient', { return svg.make('linearGradient', {
'x1': '0%', 'x1': '0%',
'y1': isBegin ? '100%' : '0%', 'y1': isBegin ? '100%' : '0%',
'x2': '0%', 'x2': '0%',
'y2': isBegin ? '0%' : '100%', 'y2': isBegin ? '0%' : '100%',
}); }, [
grad.appendChild(svg.make('stop', { svg.make('stop', {
'offset': '0%', 'offset': '0%',
'stop-color': '#FFFFFF', 'stop-color': '#FFFFFF',
})); }),
grad.appendChild(svg.make('stop', { svg.make('stop', {
'offset': (100 * ratio).toFixed(3) + '%', 'offset': (100 * ratio).toFixed(3) + '%',
'stop-color': '#000000', 'stop-color': '#000000',
})); }),
return grad; ]);
}); });
env.maskLayer.appendChild(svg.make('rect', { env.lineMaskLayer.appendChild(svg.make('rect', {
'x': x - config.width / 2, 'x': x - config.width / 2,
'y': y - (isBegin ? config.extend : 0), 'y': y - (isBegin ? config.extend : 0),
'width': config.width, 'width': config.width,
@ -4849,7 +4850,7 @@ define('sequence/components/Connect',[
array.mergeSets(env.momentaryAgentIDs, agentIDs); array.mergeSets(env.momentaryAgentIDs, agentIDs);
} }
renderRevArrowLine({x1, y1, x2, y2, xR}, options, env) { renderRevArrowLine({x1, y1, x2, y2, xR}, options, env, clickable) {
const config = env.theme.connect; const config = env.theme.connect;
const line = config.line[options.line]; const line = config.line[options.line];
const lArrow = ARROWHEADS[options.left]; const lArrow = ARROWHEADS[options.left];
@ -4865,14 +4866,14 @@ define('sequence/components/Connect',[
xR, xR,
rad: config.loopbackRadius, rad: config.loopbackRadius,
}); });
env.shapeLayer.appendChild(rendered.shape); clickable.appendChild(rendered.shape);
lArrow.render(env.shapeLayer, env.theme, { lArrow.render(clickable, env.theme, {
x: rendered.p1.x - dx1, x: rendered.p1.x - dx1,
y: rendered.p1.y, y: rendered.p1.y,
}, {dx: 1, dy: 0}); }, {dx: 1, dy: 0});
rArrow.render(env.shapeLayer, env.theme, { rArrow.render(clickable, env.theme, {
x: rendered.p2.x - dx2, x: rendered.p2.x - dx2,
y: rendered.p2.y, y: rendered.p2.y,
}, {dx: 1, dy: 0}); }, {dx: 1, dy: 0});
@ -4906,7 +4907,7 @@ define('sequence/components/Connect',[
padding: config.mask.padding, padding: config.mask.padding,
boxAttrs: {'fill': '#000000'}, boxAttrs: {'fill': '#000000'},
labelAttrs: config.label.loopbackAttrs, labelAttrs: config.label.loopbackAttrs,
boxLayer: env.maskLayer, boxLayer: env.lineMaskLayer,
labelLayer: clickable, labelLayer: clickable,
SVGTextBlockClass: env.SVGTextBlockClass, SVGTextBlockClass: env.SVGTextBlockClass,
}); });
@ -4928,7 +4929,7 @@ define('sequence/components/Connect',[
x2: to.x + to.currentMaxRad, x2: to.x + to.currentMaxRad,
y2: env.primaryY, y2: env.primaryY,
xR, xR,
}, options, env); }, options, env, clickable);
const raise = Math.max(height, lArrow.height(env.theme) / 2); const raise = Math.max(height, lArrow.height(env.theme) / 2);
const arrowDip = rArrow.height(env.theme) / 2; const arrowDip = rArrow.height(env.theme) / 2;
@ -4949,7 +4950,7 @@ define('sequence/components/Connect',[
); );
} }
renderArrowLine({x1, y1, x2, y2}, options, env) { renderArrowLine({x1, y1, x2, y2}, options, env, clickable) {
const config = env.theme.connect; const config = env.theme.connect;
const line = config.line[options.line]; const line = config.line[options.line];
const lArrow = ARROWHEADS[options.left]; const lArrow = ARROWHEADS[options.left];
@ -4970,13 +4971,13 @@ define('sequence/components/Connect',[
x2: x2 - d2 * dx, x2: x2 - d2 * dx,
y2: y2 - d2 * dy, y2: y2 - d2 * dy,
}); });
env.shapeLayer.appendChild(rendered.shape); clickable.appendChild(rendered.shape);
const p1 = {x: rendered.p1.x - d1 * dx, y: rendered.p1.y - d1 * dy}; const p1 = {x: rendered.p1.x - d1 * dx, y: rendered.p1.y - d1 * dy};
const p2 = {x: rendered.p2.x + d2 * dx, y: rendered.p2.y + d2 * dy}; const p2 = {x: rendered.p2.x + d2 * dx, y: rendered.p2.y + d2 * dy};
lArrow.render(env.shapeLayer, env.theme, p1, {dx, dy}); lArrow.render(clickable, env.theme, p1, {dx, dy});
rArrow.render(env.shapeLayer, env.theme, p2, {dx: -dx, dy: -dy}); rArrow.render(clickable, env.theme, p2, {dx: -dx, dy: -dy});
return { return {
p1, p1,
@ -4986,20 +4987,20 @@ define('sequence/components/Connect',[
}; };
} }
renderVirtualSources(from, to, renderedLine, env) { renderVirtualSources({from, to, rendered}, env, clickable) {
const config = env.theme.connect.source; const config = env.theme.connect.source;
if(from.isVirtualSource) { if(from.isVirtualSource) {
env.shapeLayer.appendChild(config.render({ clickable.appendChild(config.render({
x: renderedLine.p1.x - config.radius, x: rendered.p1.x - config.radius,
y: renderedLine.p1.y, y: rendered.p1.y,
radius: config.radius, radius: config.radius,
})); }));
} }
if(to.isVirtualSource) { if(to.isVirtualSource) {
env.shapeLayer.appendChild(config.render({ clickable.appendChild(config.render({
x: renderedLine.p2.x + config.radius, x: rendered.p2.x + config.radius,
y: renderedLine.p2.y, y: rendered.p2.y,
radius: config.radius, radius: config.radius,
})); }));
} }
@ -5032,7 +5033,7 @@ define('sequence/components/Connect',[
padding: config.mask.padding, padding: config.mask.padding,
boxAttrs, boxAttrs,
labelAttrs: config.label.attrs, labelAttrs: config.label.attrs,
boxLayer: env.maskLayer, boxLayer: env.lineMaskLayer,
labelLayer, labelLayer,
SVGTextBlockClass: env.SVGTextBlockClass, SVGTextBlockClass: env.SVGTextBlockClass,
}); });
@ -5060,7 +5061,7 @@ define('sequence/components/Connect',[
y1: yBegin, y1: yBegin,
x2, x2,
y2: env.primaryY, y2: env.primaryY,
}, options, env); }, options, env, clickable);
const arrowSpread = Math.max( const arrowSpread = Math.max(
rendered.lArrow.height(env.theme), rendered.lArrow.height(env.theme),
@ -5069,7 +5070,7 @@ define('sequence/components/Connect',[
const lift = Math.max(height, arrowSpread); const lift = Math.max(height, arrowSpread);
this.renderVirtualSources(from, to, rendered, env); this.renderVirtualSources({from, to, rendered}, env, clickable);
clickable.appendChild(svg.make('path', { clickable.appendChild(svg.make('path', {
'd': ( 'd': (
@ -5287,13 +5288,6 @@ define('sequence/components/Note',['./BaseComponent', 'svg/SVGUtilities'], (Base
break; break;
} }
env.shapeLayer.appendChild(config.boxRenderer({
x: x0,
y: env.topY + config.margin.top,
width: x1 - x0,
height: fullH,
}));
clickable.insertBefore(svg.make('rect', { clickable.insertBefore(svg.make('rect', {
'x': x0, 'x': x0,
'y': env.topY + config.margin.top, 'y': env.topY + config.margin.top,
@ -5303,6 +5297,13 @@ define('sequence/components/Note',['./BaseComponent', 'svg/SVGUtilities'], (Base
'class': 'outline', 'class': 'outline',
}), clickable.firstChild); }), clickable.firstChild);
clickable.insertBefore(config.boxRenderer({
x: x0,
y: env.topY + config.margin.top,
width: x1 - x0,
height: fullH,
}), clickable.firstChild);
return ( return (
env.topY + env.topY +
config.margin.top + config.margin.top +
@ -5507,7 +5508,7 @@ define('sequence/components/Divider',[
const left = env.agentInfos.get('['); const left = env.agentInfos.get('[');
const right = env.agentInfos.get(']'); const right = env.agentInfos.get(']');
const clickable = env.makeRegion(); const clickable = env.makeRegion({unmasked: true});
let labelWidth = 0; let labelWidth = 0;
let labelHeight = 0; let labelHeight = 0;
@ -5531,7 +5532,7 @@ define('sequence/components/Divider',[
padding: config.padding, padding: config.padding,
boxAttrs: {'fill': '#000000'}, boxAttrs: {'fill': '#000000'},
labelAttrs: config.labelAttrs, labelAttrs: config.labelAttrs,
boxLayer: env.maskLayer, boxLayer: env.fullMaskLayer,
labelLayer: clickable, labelLayer: clickable,
SVGTextBlockClass: env.SVGTextBlockClass, SVGTextBlockClass: env.SVGTextBlockClass,
}); });
@ -5551,7 +5552,7 @@ define('sequence/components/Divider',[
clickable.insertBefore(shape, clickable.firstChild); clickable.insertBefore(shape, clickable.firstChild);
} }
if(mask) { if(mask) {
env.maskLayer.appendChild(mask); env.fullMaskLayer.appendChild(mask);
} }
clickable.insertBefore(svg.make('rect', { clickable.insertBefore(svg.make('rect', {
'x': left.x - config.extend, 'x': left.x - config.extend,
@ -5683,9 +5684,7 @@ define('sequence/Renderer',[
buildMetadata() { buildMetadata() {
this.metaCode = svg.makeText(); this.metaCode = svg.makeText();
const metadata = svg.make('metadata'); return svg.make('metadata', {}, [this.metaCode]);
metadata.appendChild(this.metaCode);
return metadata;
} }
buildStaticElements() { buildStaticElements() {
@ -5693,26 +5692,37 @@ define('sequence/Renderer',[
this.themeDefs = svg.make('defs'); this.themeDefs = svg.make('defs');
this.defs = svg.make('defs'); this.defs = svg.make('defs');
this.mask = svg.make('mask', { this.fullMask = svg.make('mask', {
'id': this.namespace + 'FullMask',
'maskUnits': 'userSpaceOnUse',
});
this.lineMask = svg.make('mask', {
'id': this.namespace + 'LineMask', 'id': this.namespace + 'LineMask',
'maskUnits': 'userSpaceOnUse', 'maskUnits': 'userSpaceOnUse',
}); });
this.maskReveal = svg.make('rect', {'fill': '#FFFFFF'}); this.fullMaskReveal = svg.make('rect', {'fill': '#FFFFFF'});
this.lineMaskReveal = svg.make('rect', {'fill': '#FFFFFF'});
this.backgroundFills = svg.make('g'); this.backgroundFills = svg.make('g');
this.agentLines = svg.make('g', { this.agentLines = svg.make('g', {
'mask': 'url(#' + this.namespace + 'LineMask)', 'mask': 'url(#' + this.namespace + 'LineMask)',
}); });
this.blocks = svg.make('g'); this.blocks = svg.make('g');
this.actionShapes = svg.make('g'); this.shapes = svg.make('g');
this.actionLabels = svg.make('g'); this.unmaskedShapes = svg.make('g');
this.base.appendChild(this.buildMetadata()); this.base.appendChild(this.buildMetadata());
this.base.appendChild(this.themeDefs); this.base.appendChild(this.themeDefs);
this.base.appendChild(this.defs); this.base.appendChild(this.defs);
this.base.appendChild(this.backgroundFills); this.base.appendChild(this.backgroundFills);
this.base.appendChild(this.agentLines); this.base.appendChild(
this.base.appendChild(this.blocks); svg.make('g', {
this.base.appendChild(this.actionShapes); 'mask': 'url(#' + this.namespace + 'FullMask)',
this.base.appendChild(this.actionLabels); }, [
this.agentLines,
this.blocks,
this.shapes,
])
);
this.base.appendChild(this.unmaskedShapes);
this.title = new this.SVGTextBlockClass(this.base); this.title = new this.SVGTextBlockClass(this.base);
this.sizer = new this.SVGTextBlockClass.SizeTester(this.base); this.sizer = new this.SVGTextBlockClass.SizeTester(this.base);
@ -5893,10 +5903,11 @@ define('sequence/Renderer',[
this.trigger('mouseout'); this.trigger('mouseout');
}; };
const makeRegion = (o, stageOverride = null) => { const makeRegion = ({
if(!o) { stageOverride = null,
o = svg.make('g'); unmasked = false,
} } = {}) => {
const o = svg.make('g');
const targetStage = (stageOverride || stage); const targetStage = (stageOverride || stage);
this.addHighlightObject(targetStage.ln, o); this.addHighlightObject(targetStage.ln, o);
o.setAttribute('class', 'region'); o.setAttribute('class', 'region');
@ -5907,7 +5918,7 @@ define('sequence/Renderer',[
o.addEventListener('click', () => { o.addEventListener('click', () => {
this.trigger('click', [targetStage]); this.trigger('click', [targetStage]);
}); });
this.actionLabels.appendChild(o); (unmasked ? this.unmaskedShapes : this.shapes).appendChild(o);
return o; return o;
}; };
@ -5916,9 +5927,8 @@ define('sequence/Renderer',[
primaryY: topY + topShift, primaryY: topY + topShift,
fillLayer: this.backgroundFills, fillLayer: this.backgroundFills,
blockLayer: this.blocks, blockLayer: this.blocks,
shapeLayer: this.actionShapes, fullMaskLayer: this.fullMask,
labelLayer: this.actionLabels, lineMaskLayer: this.lineMask,
maskLayer: this.mask,
theme: this.theme, theme: this.theme,
agentInfos: this.agentInfos, agentInfos: this.agentInfos,
textSizer: this.sizer, textSizer: this.sizer,
@ -6020,10 +6030,15 @@ define('sequence/Renderer',[
this.width = x1 - x0; this.width = x1 - x0;
this.height = y1 - y0; this.height = y1 - y0;
this.maskReveal.setAttribute('x', x0); this.fullMaskReveal.setAttribute('x', x0);
this.maskReveal.setAttribute('y', y0); this.fullMaskReveal.setAttribute('y', y0);
this.maskReveal.setAttribute('width', this.width); this.fullMaskReveal.setAttribute('width', this.width);
this.maskReveal.setAttribute('height', this.height); this.fullMaskReveal.setAttribute('height', this.height);
this.lineMaskReveal.setAttribute('x', x0);
this.lineMaskReveal.setAttribute('y', y0);
this.lineMaskReveal.setAttribute('width', this.width);
this.lineMaskReveal.setAttribute('height', this.height);
this.base.setAttribute('viewBox', ( this.base.setAttribute('viewBox', (
x0 + ' ' + y0 + ' ' + x0 + ' ' + y0 + ' ' +
@ -6048,14 +6063,17 @@ define('sequence/Renderer',[
this.highlights.clear(); this.highlights.clear();
this.currentHighlight = -1; this.currentHighlight = -1;
svg.empty(this.defs); svg.empty(this.defs);
svg.empty(this.mask); svg.empty(this.fullMask);
svg.empty(this.lineMask);
svg.empty(this.backgroundFills); svg.empty(this.backgroundFills);
svg.empty(this.agentLines); svg.empty(this.agentLines);
svg.empty(this.blocks); svg.empty(this.blocks);
svg.empty(this.actionShapes); svg.empty(this.shapes);
svg.empty(this.actionLabels); svg.empty(this.unmaskedShapes);
this.mask.appendChild(this.maskReveal); this.fullMask.appendChild(this.fullMaskReveal);
this.defs.appendChild(this.mask); this.lineMask.appendChild(this.lineMaskReveal);
this.defs.appendChild(this.fullMask);
this.defs.appendChild(this.lineMask);
this._resetState(); this._resetState();
} }
@ -6515,7 +6533,7 @@ define('sequence/themes/BaseTheme',[
return o; return o;
} }
const r = {}; const r = {};
for(let k in o) { for(const k in o) {
if(o.hasOwnProperty(k)) { if(o.hasOwnProperty(k)) {
r[k] = deepCopy(o[k]); r[k] = deepCopy(o[k]);
} }
@ -6720,21 +6738,22 @@ define('sequence/themes/BaseTheme',[
let shape = null; let shape = null;
const yPos = y + height / 2; const yPos = y + height / 2;
if(labelWidth > 0) { if(labelWidth > 0) {
shape = svg.make('g'); shape = svg.make('g', {}, [
shape.appendChild(svg.make('line', Object.assign({ svg.make('line', Object.assign({
'x1': x, 'x1': x,
'y1': yPos, 'y1': yPos,
'x2': x + (width - labelWidth) / 2, 'x2': x + (width - labelWidth) / 2,
'y2': yPos, 'y2': yPos,
'fill': 'none', 'fill': 'none',
}, lineAttrs))); }, lineAttrs)),
shape.appendChild(svg.make('line', Object.assign({ svg.make('line', Object.assign({
'x1': x + (width + labelWidth) / 2, 'x1': x + (width + labelWidth) / 2,
'y1': yPos, 'y1': yPos,
'x2': x + width, 'x2': x + width,
'y2': yPos, 'y2': yPos,
'fill': 'none', 'fill': 'none',
}, lineAttrs))); }, lineAttrs)),
]);
} else { } else {
shape = svg.make('line', Object.assign({ shape = svg.make('line', Object.assign({
'x1': x, 'x1': x,
@ -6770,40 +6789,38 @@ define('sequence/themes/BaseTheme',[
) => { ) => {
const maskGradID = env.addDef('tear-grad', () => { const maskGradID = env.addDef('tear-grad', () => {
const px = 100 / width; const px = 100 / width;
const grad = svg.make('linearGradient'); return svg.make('linearGradient', {}, [
grad.appendChild(svg.make('stop', { svg.make('stop', {
'offset': (fadeBegin * px) + '%', 'offset': (fadeBegin * px) + '%',
'stop-color': '#000000', 'stop-color': '#000000',
})); }),
grad.appendChild(svg.make('stop', { svg.make('stop', {
'offset': ((fadeBegin + fadeSize) * px) + '%', 'offset': ((fadeBegin + fadeSize) * px) + '%',
'stop-color': '#FFFFFF', 'stop-color': '#FFFFFF',
})); }),
grad.appendChild(svg.make('stop', { svg.make('stop', {
'offset': (100 - (fadeBegin + fadeSize) * px) + '%', 'offset': (100 - (fadeBegin + fadeSize) * px) + '%',
'stop-color': '#FFFFFF', 'stop-color': '#FFFFFF',
})); }),
grad.appendChild(svg.make('stop', { svg.make('stop', {
'offset': (100 - fadeBegin * px) + '%', 'offset': (100 - fadeBegin * px) + '%',
'stop-color': '#000000', 'stop-color': '#000000',
})); }),
return grad; ]);
}); });
const shapeMask = svg.make('mask', { const shapeMask = svg.make('mask', {
'maskUnits': 'userSpaceOnUse', 'maskUnits': 'userSpaceOnUse',
}); }, [
const shapeMaskID = env.addDef(shapeMask); svg.make('rect', {
const shape = svg.make('g', {
'mask': 'url(#' + shapeMaskID + ')',
});
shapeMask.appendChild(svg.make('rect', {
'x': x, 'x': x,
'y': y - 5, 'y': y - 5,
'width': width, 'width': width,
'height': height + 10, 'height': height + 10,
'fill': 'url(#' + maskGradID + ')', 'fill': 'url(#' + maskGradID + ')',
})); }),
]);
const shapeMaskID = env.addDef(shapeMask);
if(labelWidth > 0) { if(labelWidth > 0) {
shapeMask.appendChild(svg.make('rect', { shapeMask.appendChild(svg.make('rect', {
'x': x + (width - labelWidth) / 2, 'x': x + (width - labelWidth) / 2,
@ -6827,10 +6844,15 @@ define('sequence/themes/BaseTheme',[
const pathTop = new SVGShapes.PatternedLine(pattern) const pathTop = new SVGShapes.PatternedLine(pattern)
.move(x, y) .move(x, y)
.line(x + width, y); .line(x + width, y);
shape.appendChild(svg.make('path', Object.assign({
const shape = svg.make('g', {
'mask': 'url(#' + shapeMaskID + ')',
}, [
svg.make('path', Object.assign({
'd': pathTop.asPath(), 'd': pathTop.asPath(),
'fill': 'none', 'fill': 'none',
}, lineAttrs))); }, lineAttrs)),
]);
if(height > 0) { if(height > 0) {
const pathBase = new SVGShapes.PatternedLine(pattern) const pathBase = new SVGShapes.PatternedLine(pattern)
@ -9070,8 +9092,8 @@ define('sequence/themes/Sketch',[
{var1: 0, move: false} {var1: 0, move: false}
); );
const g = svg.make('g'); return svg.make('g', {}, [
g.appendChild(svg.make('path', Object.assign({ svg.make('path', Object.assign({
'd': ( 'd': (
lT.nodes + lT.nodes +
lF.nodes + lF.nodes +
@ -9080,30 +9102,30 @@ define('sequence/themes/Sketch',[
lL.nodes lL.nodes
), ),
'fill': '#FFFFFF', 'fill': '#FFFFFF',
}, PENCIL.normal))); }, PENCIL.normal)),
g.appendChild(svg.make('path', Object.assign({ svg.make('path', Object.assign({
'd': lF1.nodes + lF2.nodes, 'd': lF1.nodes + lF2.nodes,
'fill': 'none', 'fill': 'none',
}, PENCIL.normal))); }, PENCIL.normal)),
]);
return g;
} }
renderLineDivider({x, y, labelWidth, width, height}) { renderLineDivider({x, y, labelWidth, width, height}) {
let shape = null; let shape = null;
const yPos = y + height / 2; const yPos = y + height / 2;
if(labelWidth > 0) { if(labelWidth > 0) {
shape = svg.make('g'); shape = svg.make('g', {}, [
shape.appendChild(this.renderLine( this.renderLine(
{x, y: yPos}, {x, y: yPos},
{x: x + (width - labelWidth) / 2, y: yPos}, {x: x + (width - labelWidth) / 2, y: yPos},
{} {}
)); ),
shape.appendChild(this.renderLine( this.renderLine(
{x: x + (width + labelWidth) / 2, y: yPos}, {x: x + (width + labelWidth) / 2, y: yPos},
{x: x + width, y: yPos}, {x: x + width, y: yPos},
{} {}
)); ),
]);
} else { } else {
shape = this.renderLine( shape = this.renderLine(
{x, y: yPos}, {x, y: yPos},
@ -9307,19 +9329,16 @@ define('sequence/themes/Sketch',[
const line = l1.nodes + l2.nodes; const line = l1.nodes + l2.nodes;
const g = svg.make('g'); return svg.make('g', {}, [
svg.make('path', {
g.appendChild(svg.make('path', {
'd': line + 'L' + x + ' ' + y, 'd': line + 'L' + x + ' ' + y,
'fill': '#FFFFFF', 'fill': '#FFFFFF',
})); }),
svg.make('path', Object.assign({
g.appendChild(svg.make('path', Object.assign({
'd': line, 'd': line,
'fill': '#FFFFFF', 'fill': '#FFFFFF',
}, PENCIL.normal))); }, PENCIL.normal)),
]);
return g;
} }
renderSeparator({x1, y1, x2, y2}) { renderSeparator({x1, y1, x2, y2}) {

File diff suppressed because one or more lines are too long

View File

@ -9,13 +9,16 @@ define(['require'], (require) => {
return document.createTextNode(text); return document.createTextNode(text);
} }
function makeNode(type, attrs = {}) { function makeNode(type, attrs = {}, children = []) {
const o = document.createElement(type); const o = document.createElement(type);
for(let k in attrs) { for(const k in attrs) {
if(attrs.hasOwnProperty(k)) { if(attrs.hasOwnProperty(k)) {
o.setAttribute(k, attrs[k]); o.setAttribute(k, attrs[k]);
} }
} }
for(const c of children) {
o.appendChild(c);
}
return o; return o;
} }
@ -169,12 +172,10 @@ define(['require'], (require) => {
buildOptionsLinks() { buildOptionsLinks() {
const options = makeNode('div', {'class': 'options links'}); const options = makeNode('div', {'class': 'options links'});
this.links.forEach((link) => { this.links.forEach((link) => {
const linkNode = makeNode('a', { options.appendChild(makeNode('a', {
'href': link.href, 'href': link.href,
'target': '_blank', 'target': '_blank',
}); }, [makeText(link.label)]));
linkNode.appendChild(makeText(link.label));
options.appendChild(linkNode);
}); });
return options; return options;
} }
@ -183,8 +184,7 @@ define(['require'], (require) => {
this.downloadPNG = makeNode('a', { this.downloadPNG = makeNode('a', {
'href': '#', 'href': '#',
'download': 'SequenceDiagram.png', 'download': 'SequenceDiagram.png',
}); }, [makeText('Download PNG')]);
this.downloadPNG.appendChild(makeText('Download PNG'));
on(this.downloadPNG, [ on(this.downloadPNG, [
'focus', 'focus',
'mouseover', 'mouseover',
@ -195,14 +195,13 @@ define(['require'], (require) => {
this.downloadSVG = makeNode('a', { this.downloadSVG = makeNode('a', {
'href': '#', 'href': '#',
'download': 'SequenceDiagram.svg', 'download': 'SequenceDiagram.svg',
}); }, [makeText('SVG')]);
this.downloadSVG.appendChild(makeText('SVG'));
on(this.downloadSVG, ['click'], this._downloadSVGClick); on(this.downloadSVG, ['click'], this._downloadSVGClick);
const options = makeNode('div', {'class': 'options downloads'}); return makeNode('div', {'class': 'options downloads'}, [
options.appendChild(this.downloadPNG); this.downloadPNG,
options.appendChild(this.downloadSVG); this.downloadSVG,
return options; ]);
} }
buildEditor(container) { buildEditor(container) {
@ -282,13 +281,12 @@ define(['require'], (require) => {
buildLibrary(container) { buildLibrary(container) {
this.library.forEach((lib) => { this.library.forEach((lib) => {
const hold = makeNode('div', {
'class': 'library-item',
});
const holdInner = makeNode('div', { const holdInner = makeNode('div', {
'title': lib.title || lib.code, 'title': lib.title || lib.code,
}); });
hold.appendChild(holdInner); const hold = makeNode('div', {
'class': 'library-item',
}, [holdInner]);
hold.addEventListener( hold.addEventListener(
'click', 'click',
this.addCodeBlock.bind(this, lib.code) this.addCodeBlock.bind(this, lib.code)
@ -308,26 +306,22 @@ define(['require'], (require) => {
} }
buildErrorReport() { buildErrorReport() {
this.errorMsg = makeNode('div', {'class': 'msg-error'});
this.errorText = makeText(); this.errorText = makeText();
this.errorMsg.appendChild(this.errorText); this.errorMsg = makeNode('div', {'class': 'msg-error'}, [
this.errorText,
]);
return this.errorMsg; return this.errorMsg;
} }
buildViewPane() { buildViewPane() {
const viewPane = makeNode('div', {'class': 'pane-view'}); this.viewPaneInner = makeNode('div', {'class': 'pane-view-inner'});
const viewPaneScroller = makeNode('div', {
'class': 'pane-view-scroller',
});
this.viewPaneInner = makeNode('div', {
'class': 'pane-view-inner',
});
viewPane.appendChild(viewPaneScroller); return makeNode('div', {'class': 'pane-view'}, [
viewPaneScroller.appendChild(this.viewPaneInner); makeNode('div', {'class': 'pane-view-scroller'}, [
viewPane.appendChild(this.buildErrorReport()); this.viewPaneInner,
]),
return viewPane; this.buildErrorReport(),
]);
} }
buildLeftPanes(container) { buildLeftPanes(container) {
@ -336,15 +330,14 @@ define(['require'], (require) => {
let libPane = null; let libPane = null;
if(this.library.length > 0) { if(this.library.length > 0) {
libPane = makeNode('div', {'class': 'pane-library'});
const libPaneScroller = makeNode('div', {
'class': 'pane-library-scroller',
});
const libPaneInner = makeNode('div', { const libPaneInner = makeNode('div', {
'class': 'pane-library-inner', 'class': 'pane-library-inner',
}); });
libPaneScroller.appendChild(libPaneInner); libPane = makeNode('div', {'class': 'pane-library'}, [
libPane.appendChild(libPaneScroller); makeNode('div', {'class': 'pane-library-scroller'}, [
libPaneInner,
]),
]);
container.appendChild(libPane); container.appendChild(libPane);
this.buildLibrary(libPaneInner); this.buildLibrary(libPaneInner);
@ -361,17 +354,17 @@ define(['require'], (require) => {
build(container) { build(container) {
this.container = container; this.container = container;
const hold = makeNode('div', {'class': 'pane-hold'});
const lPane = makeNode('div', {'class': 'pane-side'});
hold.appendChild(lPane);
container.appendChild(hold);
const {codePane} = this.buildLeftPanes(lPane);
const viewPane = this.buildViewPane(); const viewPane = this.buildViewPane();
hold.appendChild(viewPane);
hold.appendChild(this.buildOptionsLinks()); const lPane = makeNode('div', {'class': 'pane-side'});
hold.appendChild(this.buildOptionsDownloads()); const hold = makeNode('div', {'class': 'pane-hold'}, [
lPane,
viewPane,
this.buildOptionsLinks(),
this.buildOptionsDownloads(),
]);
container.appendChild(hold);
makeSplit([lPane, viewPane], { makeSplit([lPane, viewPane], {
direction: 'horizontal', direction: 'horizontal',
@ -380,6 +373,7 @@ define(['require'], (require) => {
minSize: [10, 10], minSize: [10, 10],
}); });
const {codePane} = this.buildLeftPanes(lPane);
this.code = this.buildEditor(codePane); this.code = this.buildEditor(codePane);
this.viewPaneInner.appendChild(this.diagram.dom()); this.viewPaneInner.appendChild(this.diagram.dom());

View File

@ -7,13 +7,16 @@
return document.createTextNode(text); return document.createTextNode(text);
} }
function makeNode(type, attrs = {}) { function makeNode(type, attrs = {}, children = []) {
const o = document.createElement(type); const o = document.createElement(type);
for(let k in attrs) { for(const k in attrs) {
if(attrs.hasOwnProperty(k)) { if(attrs.hasOwnProperty(k)) {
o.setAttribute(k, attrs[k]); o.setAttribute(k, attrs[k]);
} }
} }
for(const c of children) {
o.appendChild(c);
}
return o; return o;
} }
@ -64,36 +67,34 @@
} }
requirejs(['sequence/SequenceDiagram'], (SequenceDiagram) => { requirejs(['sequence/SequenceDiagram'], (SequenceDiagram) => {
const status = makeNode('div', {'class': 'status'});
const statusText = makeText('Loading\u2026'); const statusText = makeText('Loading\u2026');
status.appendChild(statusText); const status = makeNode('div', {'class': 'status'}, [statusText]);
document.body.appendChild(status); document.body.appendChild(status);
function renderSample({file, code, size}) { function renderSample({file, code, size}) {
const hold = makeNode('div', {'class': 'hold'}); const diagram = new SequenceDiagram(code);
const diagram = new SequenceDiagram(code, {container: hold});
const raster = makeNode('img', { const raster = makeNode('img', {
'src': '', 'src': '',
'class': 'raster', 'class': 'raster',
'title': 'new', 'title': 'new',
}); });
hold.appendChild(raster);
hold.appendChild(makeNode('img', {
'src': file,
'class': 'original',
'title': 'original',
}));
const downloadPNG = makeNode('a', { const downloadPNG = makeNode('a', {
'href': '#', 'href': '#',
'download': filename(file), 'download': filename(file),
}); }, [makeText('Download PNG')]);
downloadPNG.appendChild(makeText('Download PNG'));
hold.appendChild(downloadPNG);
document.body.appendChild(hold); document.body.appendChild(makeNode('div', {'class': 'hold'}, [
diagram.dom(),
raster,
makeNode('img', {
'src': file,
'class': 'original',
'title': 'original',
}),
downloadPNG,
]));
diagram.getPNG({resolution: RESOLUTION, size}).then(({url}) => { diagram.getPNG({resolution: RESOLUTION, size}).then(({url}) => {
raster.setAttribute('src', url); raster.setAttribute('src', url);

View File

@ -110,9 +110,7 @@ define([
buildMetadata() { buildMetadata() {
this.metaCode = svg.makeText(); this.metaCode = svg.makeText();
const metadata = svg.make('metadata'); return svg.make('metadata', {}, [this.metaCode]);
metadata.appendChild(this.metaCode);
return metadata;
} }
buildStaticElements() { buildStaticElements() {
@ -120,26 +118,37 @@ define([
this.themeDefs = svg.make('defs'); this.themeDefs = svg.make('defs');
this.defs = svg.make('defs'); this.defs = svg.make('defs');
this.mask = svg.make('mask', { this.fullMask = svg.make('mask', {
'id': this.namespace + 'FullMask',
'maskUnits': 'userSpaceOnUse',
});
this.lineMask = svg.make('mask', {
'id': this.namespace + 'LineMask', 'id': this.namespace + 'LineMask',
'maskUnits': 'userSpaceOnUse', 'maskUnits': 'userSpaceOnUse',
}); });
this.maskReveal = svg.make('rect', {'fill': '#FFFFFF'}); this.fullMaskReveal = svg.make('rect', {'fill': '#FFFFFF'});
this.lineMaskReveal = svg.make('rect', {'fill': '#FFFFFF'});
this.backgroundFills = svg.make('g'); this.backgroundFills = svg.make('g');
this.agentLines = svg.make('g', { this.agentLines = svg.make('g', {
'mask': 'url(#' + this.namespace + 'LineMask)', 'mask': 'url(#' + this.namespace + 'LineMask)',
}); });
this.blocks = svg.make('g'); this.blocks = svg.make('g');
this.actionShapes = svg.make('g'); this.shapes = svg.make('g');
this.actionLabels = svg.make('g'); this.unmaskedShapes = svg.make('g');
this.base.appendChild(this.buildMetadata()); this.base.appendChild(this.buildMetadata());
this.base.appendChild(this.themeDefs); this.base.appendChild(this.themeDefs);
this.base.appendChild(this.defs); this.base.appendChild(this.defs);
this.base.appendChild(this.backgroundFills); this.base.appendChild(this.backgroundFills);
this.base.appendChild(this.agentLines); this.base.appendChild(
this.base.appendChild(this.blocks); svg.make('g', {
this.base.appendChild(this.actionShapes); 'mask': 'url(#' + this.namespace + 'FullMask)',
this.base.appendChild(this.actionLabels); }, [
this.agentLines,
this.blocks,
this.shapes,
])
);
this.base.appendChild(this.unmaskedShapes);
this.title = new this.SVGTextBlockClass(this.base); this.title = new this.SVGTextBlockClass(this.base);
this.sizer = new this.SVGTextBlockClass.SizeTester(this.base); this.sizer = new this.SVGTextBlockClass.SizeTester(this.base);
@ -320,10 +329,11 @@ define([
this.trigger('mouseout'); this.trigger('mouseout');
}; };
const makeRegion = (o, stageOverride = null) => { const makeRegion = ({
if(!o) { stageOverride = null,
o = svg.make('g'); unmasked = false,
} } = {}) => {
const o = svg.make('g');
const targetStage = (stageOverride || stage); const targetStage = (stageOverride || stage);
this.addHighlightObject(targetStage.ln, o); this.addHighlightObject(targetStage.ln, o);
o.setAttribute('class', 'region'); o.setAttribute('class', 'region');
@ -334,7 +344,7 @@ define([
o.addEventListener('click', () => { o.addEventListener('click', () => {
this.trigger('click', [targetStage]); this.trigger('click', [targetStage]);
}); });
this.actionLabels.appendChild(o); (unmasked ? this.unmaskedShapes : this.shapes).appendChild(o);
return o; return o;
}; };
@ -343,9 +353,8 @@ define([
primaryY: topY + topShift, primaryY: topY + topShift,
fillLayer: this.backgroundFills, fillLayer: this.backgroundFills,
blockLayer: this.blocks, blockLayer: this.blocks,
shapeLayer: this.actionShapes, fullMaskLayer: this.fullMask,
labelLayer: this.actionLabels, lineMaskLayer: this.lineMask,
maskLayer: this.mask,
theme: this.theme, theme: this.theme,
agentInfos: this.agentInfos, agentInfos: this.agentInfos,
textSizer: this.sizer, textSizer: this.sizer,
@ -447,10 +456,15 @@ define([
this.width = x1 - x0; this.width = x1 - x0;
this.height = y1 - y0; this.height = y1 - y0;
this.maskReveal.setAttribute('x', x0); this.fullMaskReveal.setAttribute('x', x0);
this.maskReveal.setAttribute('y', y0); this.fullMaskReveal.setAttribute('y', y0);
this.maskReveal.setAttribute('width', this.width); this.fullMaskReveal.setAttribute('width', this.width);
this.maskReveal.setAttribute('height', this.height); this.fullMaskReveal.setAttribute('height', this.height);
this.lineMaskReveal.setAttribute('x', x0);
this.lineMaskReveal.setAttribute('y', y0);
this.lineMaskReveal.setAttribute('width', this.width);
this.lineMaskReveal.setAttribute('height', this.height);
this.base.setAttribute('viewBox', ( this.base.setAttribute('viewBox', (
x0 + ' ' + y0 + ' ' + x0 + ' ' + y0 + ' ' +
@ -475,14 +489,17 @@ define([
this.highlights.clear(); this.highlights.clear();
this.currentHighlight = -1; this.currentHighlight = -1;
svg.empty(this.defs); svg.empty(this.defs);
svg.empty(this.mask); svg.empty(this.fullMask);
svg.empty(this.lineMask);
svg.empty(this.backgroundFills); svg.empty(this.backgroundFills);
svg.empty(this.agentLines); svg.empty(this.agentLines);
svg.empty(this.blocks); svg.empty(this.blocks);
svg.empty(this.actionShapes); svg.empty(this.shapes);
svg.empty(this.actionLabels); svg.empty(this.unmaskedShapes);
this.mask.appendChild(this.maskReveal); this.fullMask.appendChild(this.fullMaskReveal);
this.defs.appendChild(this.mask); this.lineMask.appendChild(this.lineMaskReveal);
this.defs.appendChild(this.fullMask);
this.defs.appendChild(this.lineMask);
this._resetState(); this._resetState();
} }

View File

@ -52,12 +52,18 @@ defineDescribe('SequenceDiagram', [
'<svg viewBox="-5 -5 10 10">' + '<svg viewBox="-5 -5 10 10">' +
'<metadata></metadata>' + '<metadata></metadata>' +
'<defs>' + '<defs>' +
'<mask id="FullMask" maskUnits="userSpaceOnUse">' +
'<rect fill="#FFFFFF" x="-5" y="-5" width="10" height="10">' +
'</rect>' +
'</mask>' +
'<mask id="LineMask" maskUnits="userSpaceOnUse">' + '<mask id="LineMask" maskUnits="userSpaceOnUse">' +
'<rect fill="#FFFFFF" x="-5" y="-5" width="10" height="10">' + '<rect fill="#FFFFFF" x="-5" y="-5" width="10" height="10">' +
'</rect>' + '</rect>' +
'</mask>' + '</mask>' +
'</defs>' + '</defs>' +
'<g mask="url(#FullMask)">' +
'<g mask="url(#LineMask)"></g>' + '<g mask="url(#LineMask)"></g>' +
'</g>' +
'</svg>' '</svg>'
); );
}); });
@ -69,12 +75,18 @@ defineDescribe('SequenceDiagram', [
'<svg viewBox="-11.5 -16 23 21">' + '<svg viewBox="-11.5 -16 23 21">' +
'<metadata>title My title here</metadata>' + '<metadata>title My title here</metadata>' +
'<defs>' + '<defs>' +
'<mask id="FullMask" maskUnits="userSpaceOnUse">' +
'<rect fill="#FFFFFF" x="-11.5" y="-16" width="23" height="21">' +
'</rect>' +
'</mask>' +
'<mask id="LineMask" maskUnits="userSpaceOnUse">' + '<mask id="LineMask" maskUnits="userSpaceOnUse">' +
'<rect fill="#FFFFFF" x="-11.5" y="-16" width="23" height="21">' + '<rect fill="#FFFFFF" x="-11.5" y="-16" width="23" height="21">' +
'</rect>' + '</rect>' +
'</mask>' + '</mask>' +
'</defs>' + '</defs>' +
'<g mask="url(#FullMask)">' +
'<g mask="url(#LineMask)"></g>' + '<g mask="url(#LineMask)"></g>' +
'</g>' +
'<text' + '<text' +
' x="0"' + ' x="0"' +
' font-family="sans-serif"' + ' font-family="sans-serif"' +

View File

@ -40,30 +40,30 @@ define([
render(y, {x, formattedLabel}, env) { render(y, {x, formattedLabel}, env) {
const config = env.theme.agentCap.box; const config = env.theme.agentCap.box;
const clickable = env.makeRegion(); const clickable = env.makeRegion();
const {width, height} = SVGShapes.renderBoxedText(formattedLabel, { const text = SVGShapes.renderBoxedText(formattedLabel, {
x, x,
y, y,
padding: config.padding, padding: config.padding,
boxAttrs: config.boxAttrs, boxAttrs: config.boxAttrs,
boxRenderer: config.boxRenderer, boxRenderer: config.boxRenderer,
labelAttrs: config.labelAttrs, labelAttrs: config.labelAttrs,
boxLayer: env.shapeLayer, boxLayer: clickable,
labelLayer: clickable, labelLayer: clickable,
SVGTextBlockClass: env.SVGTextBlockClass, SVGTextBlockClass: env.SVGTextBlockClass,
}); });
clickable.insertBefore(svg.make('rect', { clickable.insertBefore(svg.make('rect', {
'x': x - width / 2, 'x': x - text.width / 2,
'y': y, 'y': y,
'width': width, 'width': text.width,
'height': height, 'height': text.height,
'fill': 'transparent', 'fill': 'transparent',
'class': 'outline', 'class': 'outline',
}), clickable.firstChild); }), text.label.firstLine());
return { return {
lineTop: 0, lineTop: 0,
lineBottom: height, lineBottom: text.height,
height, height: text.height,
}; };
} }
} }
@ -87,9 +87,10 @@ define([
const config = env.theme.agentCap.cross; const config = env.theme.agentCap.cross;
const d = config.size / 2; const d = config.size / 2;
env.shapeLayer.appendChild(config.render({x, y: y + d, radius: d})); const clickable = env.makeRegion();
env.makeRegion().appendChild(svg.make('rect', { clickable.appendChild(config.render({x, y: y + d, radius: d}));
clickable.appendChild(svg.make('rect', {
'x': x - d, 'x': x - d,
'y': y, 'y': y,
'width': d * 2, 'width': d * 2,
@ -137,14 +138,14 @@ define([
); );
const height = barCfg.height; const height = barCfg.height;
env.shapeLayer.appendChild(barCfg.render({ const clickable = env.makeRegion();
clickable.appendChild(barCfg.render({
x: x - width / 2, x: x - width / 2,
y, y,
width, width,
height, height,
})); }));
clickable.appendChild(svg.make('rect', {
env.makeRegion().appendChild(svg.make('rect', {
'x': x - width / 2, 'x': x - width / 2,
'y': y, 'y': y,
'width': width, 'width': width,
@ -180,24 +181,24 @@ define([
const ratio = config.height / (config.height + config.extend); const ratio = config.height / (config.height + config.extend);
const gradID = env.addDef(isBegin ? 'FadeIn' : 'FadeOut', () => { const gradID = env.addDef(isBegin ? 'FadeIn' : 'FadeOut', () => {
const grad = svg.make('linearGradient', { return svg.make('linearGradient', {
'x1': '0%', 'x1': '0%',
'y1': isBegin ? '100%' : '0%', 'y1': isBegin ? '100%' : '0%',
'x2': '0%', 'x2': '0%',
'y2': isBegin ? '0%' : '100%', 'y2': isBegin ? '0%' : '100%',
}); }, [
grad.appendChild(svg.make('stop', { svg.make('stop', {
'offset': '0%', 'offset': '0%',
'stop-color': '#FFFFFF', 'stop-color': '#FFFFFF',
})); }),
grad.appendChild(svg.make('stop', { svg.make('stop', {
'offset': (100 * ratio).toFixed(3) + '%', 'offset': (100 * ratio).toFixed(3) + '%',
'stop-color': '#000000', 'stop-color': '#000000',
})); }),
return grad; ]);
}); });
env.maskLayer.appendChild(svg.make('rect', { env.lineMaskLayer.appendChild(svg.make('rect', {
'x': x - config.width / 2, 'x': x - config.width / 2,
'y': y - (isBegin ? config.extend : 0), 'y': y - (isBegin ? config.extend : 0),
'width': config.width, 'width': config.width,

View File

@ -50,8 +50,6 @@ define(() => {
primaryY, primaryY,
fillLayer, fillLayer,
blockLayer, blockLayer,
shapeLayer,
labelLayer,
theme, theme,
agentInfos, agentInfos,
textSizer, textSizer,

View File

@ -64,7 +64,7 @@ define([
padding: config.section.label.padding, padding: config.section.label.padding,
boxAttrs: {'fill': '#000000'}, boxAttrs: {'fill': '#000000'},
labelAttrs: config.section.label.labelAttrs, labelAttrs: config.section.label.labelAttrs,
boxLayer: env.maskLayer, boxLayer: env.lineMaskLayer,
labelLayer: clickable, labelLayer: clickable,
SVGTextBlockClass: env.SVGTextBlockClass, SVGTextBlockClass: env.SVGTextBlockClass,
}); });
@ -180,7 +180,7 @@ define([
env.fillLayer.appendChild(shapes.fill); env.fillLayer.appendChild(shapes.fill);
} }
if(shapes.mask) { if(shapes.mask) {
env.maskLayer.appendChild(shapes.mask); env.lineMaskLayer.appendChild(shapes.mask);
} }
return env.primaryY + config.margin.bottom + env.theme.actionMargin; return env.primaryY + config.margin.bottom + env.theme.actionMargin;

View File

@ -167,7 +167,7 @@ define([
array.mergeSets(env.momentaryAgentIDs, agentIDs); array.mergeSets(env.momentaryAgentIDs, agentIDs);
} }
renderRevArrowLine({x1, y1, x2, y2, xR}, options, env) { renderRevArrowLine({x1, y1, x2, y2, xR}, options, env, clickable) {
const config = env.theme.connect; const config = env.theme.connect;
const line = config.line[options.line]; const line = config.line[options.line];
const lArrow = ARROWHEADS[options.left]; const lArrow = ARROWHEADS[options.left];
@ -183,14 +183,14 @@ define([
xR, xR,
rad: config.loopbackRadius, rad: config.loopbackRadius,
}); });
env.shapeLayer.appendChild(rendered.shape); clickable.appendChild(rendered.shape);
lArrow.render(env.shapeLayer, env.theme, { lArrow.render(clickable, env.theme, {
x: rendered.p1.x - dx1, x: rendered.p1.x - dx1,
y: rendered.p1.y, y: rendered.p1.y,
}, {dx: 1, dy: 0}); }, {dx: 1, dy: 0});
rArrow.render(env.shapeLayer, env.theme, { rArrow.render(clickable, env.theme, {
x: rendered.p2.x - dx2, x: rendered.p2.x - dx2,
y: rendered.p2.y, y: rendered.p2.y,
}, {dx: 1, dy: 0}); }, {dx: 1, dy: 0});
@ -224,7 +224,7 @@ define([
padding: config.mask.padding, padding: config.mask.padding,
boxAttrs: {'fill': '#000000'}, boxAttrs: {'fill': '#000000'},
labelAttrs: config.label.loopbackAttrs, labelAttrs: config.label.loopbackAttrs,
boxLayer: env.maskLayer, boxLayer: env.lineMaskLayer,
labelLayer: clickable, labelLayer: clickable,
SVGTextBlockClass: env.SVGTextBlockClass, SVGTextBlockClass: env.SVGTextBlockClass,
}); });
@ -246,7 +246,7 @@ define([
x2: to.x + to.currentMaxRad, x2: to.x + to.currentMaxRad,
y2: env.primaryY, y2: env.primaryY,
xR, xR,
}, options, env); }, options, env, clickable);
const raise = Math.max(height, lArrow.height(env.theme) / 2); const raise = Math.max(height, lArrow.height(env.theme) / 2);
const arrowDip = rArrow.height(env.theme) / 2; const arrowDip = rArrow.height(env.theme) / 2;
@ -267,7 +267,7 @@ define([
); );
} }
renderArrowLine({x1, y1, x2, y2}, options, env) { renderArrowLine({x1, y1, x2, y2}, options, env, clickable) {
const config = env.theme.connect; const config = env.theme.connect;
const line = config.line[options.line]; const line = config.line[options.line];
const lArrow = ARROWHEADS[options.left]; const lArrow = ARROWHEADS[options.left];
@ -288,13 +288,13 @@ define([
x2: x2 - d2 * dx, x2: x2 - d2 * dx,
y2: y2 - d2 * dy, y2: y2 - d2 * dy,
}); });
env.shapeLayer.appendChild(rendered.shape); clickable.appendChild(rendered.shape);
const p1 = {x: rendered.p1.x - d1 * dx, y: rendered.p1.y - d1 * dy}; const p1 = {x: rendered.p1.x - d1 * dx, y: rendered.p1.y - d1 * dy};
const p2 = {x: rendered.p2.x + d2 * dx, y: rendered.p2.y + d2 * dy}; const p2 = {x: rendered.p2.x + d2 * dx, y: rendered.p2.y + d2 * dy};
lArrow.render(env.shapeLayer, env.theme, p1, {dx, dy}); lArrow.render(clickable, env.theme, p1, {dx, dy});
rArrow.render(env.shapeLayer, env.theme, p2, {dx: -dx, dy: -dy}); rArrow.render(clickable, env.theme, p2, {dx: -dx, dy: -dy});
return { return {
p1, p1,
@ -304,20 +304,20 @@ define([
}; };
} }
renderVirtualSources(from, to, renderedLine, env) { renderVirtualSources({from, to, rendered}, env, clickable) {
const config = env.theme.connect.source; const config = env.theme.connect.source;
if(from.isVirtualSource) { if(from.isVirtualSource) {
env.shapeLayer.appendChild(config.render({ clickable.appendChild(config.render({
x: renderedLine.p1.x - config.radius, x: rendered.p1.x - config.radius,
y: renderedLine.p1.y, y: rendered.p1.y,
radius: config.radius, radius: config.radius,
})); }));
} }
if(to.isVirtualSource) { if(to.isVirtualSource) {
env.shapeLayer.appendChild(config.render({ clickable.appendChild(config.render({
x: renderedLine.p2.x + config.radius, x: rendered.p2.x + config.radius,
y: renderedLine.p2.y, y: rendered.p2.y,
radius: config.radius, radius: config.radius,
})); }));
} }
@ -350,7 +350,7 @@ define([
padding: config.mask.padding, padding: config.mask.padding,
boxAttrs, boxAttrs,
labelAttrs: config.label.attrs, labelAttrs: config.label.attrs,
boxLayer: env.maskLayer, boxLayer: env.lineMaskLayer,
labelLayer, labelLayer,
SVGTextBlockClass: env.SVGTextBlockClass, SVGTextBlockClass: env.SVGTextBlockClass,
}); });
@ -378,7 +378,7 @@ define([
y1: yBegin, y1: yBegin,
x2, x2,
y2: env.primaryY, y2: env.primaryY,
}, options, env); }, options, env, clickable);
const arrowSpread = Math.max( const arrowSpread = Math.max(
rendered.lArrow.height(env.theme), rendered.lArrow.height(env.theme),
@ -387,7 +387,7 @@ define([
const lift = Math.max(height, arrowSpread); const lift = Math.max(height, arrowSpread);
this.renderVirtualSources(from, to, rendered, env); this.renderVirtualSources({from, to, rendered}, env, clickable);
clickable.appendChild(svg.make('path', { clickable.appendChild(svg.make('path', {
'd': ( 'd': (

View File

@ -30,7 +30,7 @@ define([
const left = env.agentInfos.get('['); const left = env.agentInfos.get('[');
const right = env.agentInfos.get(']'); const right = env.agentInfos.get(']');
const clickable = env.makeRegion(); const clickable = env.makeRegion({unmasked: true});
let labelWidth = 0; let labelWidth = 0;
let labelHeight = 0; let labelHeight = 0;
@ -54,7 +54,7 @@ define([
padding: config.padding, padding: config.padding,
boxAttrs: {'fill': '#000000'}, boxAttrs: {'fill': '#000000'},
labelAttrs: config.labelAttrs, labelAttrs: config.labelAttrs,
boxLayer: env.maskLayer, boxLayer: env.fullMaskLayer,
labelLayer: clickable, labelLayer: clickable,
SVGTextBlockClass: env.SVGTextBlockClass, SVGTextBlockClass: env.SVGTextBlockClass,
}); });
@ -74,7 +74,7 @@ define([
clickable.insertBefore(shape, clickable.firstChild); clickable.insertBefore(shape, clickable.firstChild);
} }
if(mask) { if(mask) {
env.maskLayer.appendChild(mask); env.fullMaskLayer.appendChild(mask);
} }
clickable.insertBefore(svg.make('rect', { clickable.insertBefore(svg.make('rect', {
'x': left.x - config.extend, 'x': left.x - config.extend,

View File

@ -79,13 +79,6 @@ define(['./BaseComponent', 'svg/SVGUtilities'], (BaseComponent, svg) => {
break; break;
} }
env.shapeLayer.appendChild(config.boxRenderer({
x: x0,
y: env.topY + config.margin.top,
width: x1 - x0,
height: fullH,
}));
clickable.insertBefore(svg.make('rect', { clickable.insertBefore(svg.make('rect', {
'x': x0, 'x': x0,
'y': env.topY + config.margin.top, 'y': env.topY + config.margin.top,
@ -95,6 +88,13 @@ define(['./BaseComponent', 'svg/SVGUtilities'], (BaseComponent, svg) => {
'class': 'outline', 'class': 'outline',
}), clickable.firstChild); }), clickable.firstChild);
clickable.insertBefore(config.boxRenderer({
x: x0,
y: env.topY + config.margin.top,
width: x1 - x0,
height: fullH,
}), clickable.firstChild);
return ( return (
env.topY + env.topY +
config.margin.top + config.margin.top +

View File

@ -58,8 +58,10 @@ define([
const originalMakeRegion = env.makeRegion; const originalMakeRegion = env.makeRegion;
let bottomY = 0; let bottomY = 0;
stage.stages.forEach((subStage) => { stage.stages.forEach((subStage) => {
env.makeRegion = (o, stageOverride = null) => { env.makeRegion = (options = {}) => {
return originalMakeRegion(o, stageOverride || subStage); return originalMakeRegion(Object.assign({
stageOverride: subStage,
}, options));
}; };
const component = env.components.get(subStage.type); const component = env.components.get(subStage.type);

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -2,6 +2,7 @@ define([], [
'Connect.svg', 'Connect.svg',
'SelfConnect.svg', 'SelfConnect.svg',
'Divider.svg', 'Divider.svg',
'DividerMasking.svg',
'Asynchronous.svg', 'Asynchronous.svg',
'Block.svg', 'Block.svg',
'Reference.svg', 'Reference.svg',

View File

@ -12,7 +12,7 @@ define([
return o; return o;
} }
const r = {}; const r = {};
for(let k in o) { for(const k in o) {
if(o.hasOwnProperty(k)) { if(o.hasOwnProperty(k)) {
r[k] = deepCopy(o[k]); r[k] = deepCopy(o[k]);
} }
@ -217,21 +217,22 @@ define([
let shape = null; let shape = null;
const yPos = y + height / 2; const yPos = y + height / 2;
if(labelWidth > 0) { if(labelWidth > 0) {
shape = svg.make('g'); shape = svg.make('g', {}, [
shape.appendChild(svg.make('line', Object.assign({ svg.make('line', Object.assign({
'x1': x, 'x1': x,
'y1': yPos, 'y1': yPos,
'x2': x + (width - labelWidth) / 2, 'x2': x + (width - labelWidth) / 2,
'y2': yPos, 'y2': yPos,
'fill': 'none', 'fill': 'none',
}, lineAttrs))); }, lineAttrs)),
shape.appendChild(svg.make('line', Object.assign({ svg.make('line', Object.assign({
'x1': x + (width + labelWidth) / 2, 'x1': x + (width + labelWidth) / 2,
'y1': yPos, 'y1': yPos,
'x2': x + width, 'x2': x + width,
'y2': yPos, 'y2': yPos,
'fill': 'none', 'fill': 'none',
}, lineAttrs))); }, lineAttrs)),
]);
} else { } else {
shape = svg.make('line', Object.assign({ shape = svg.make('line', Object.assign({
'x1': x, 'x1': x,
@ -267,40 +268,38 @@ define([
) => { ) => {
const maskGradID = env.addDef('tear-grad', () => { const maskGradID = env.addDef('tear-grad', () => {
const px = 100 / width; const px = 100 / width;
const grad = svg.make('linearGradient'); return svg.make('linearGradient', {}, [
grad.appendChild(svg.make('stop', { svg.make('stop', {
'offset': (fadeBegin * px) + '%', 'offset': (fadeBegin * px) + '%',
'stop-color': '#000000', 'stop-color': '#000000',
})); }),
grad.appendChild(svg.make('stop', { svg.make('stop', {
'offset': ((fadeBegin + fadeSize) * px) + '%', 'offset': ((fadeBegin + fadeSize) * px) + '%',
'stop-color': '#FFFFFF', 'stop-color': '#FFFFFF',
})); }),
grad.appendChild(svg.make('stop', { svg.make('stop', {
'offset': (100 - (fadeBegin + fadeSize) * px) + '%', 'offset': (100 - (fadeBegin + fadeSize) * px) + '%',
'stop-color': '#FFFFFF', 'stop-color': '#FFFFFF',
})); }),
grad.appendChild(svg.make('stop', { svg.make('stop', {
'offset': (100 - fadeBegin * px) + '%', 'offset': (100 - fadeBegin * px) + '%',
'stop-color': '#000000', 'stop-color': '#000000',
})); }),
return grad; ]);
}); });
const shapeMask = svg.make('mask', { const shapeMask = svg.make('mask', {
'maskUnits': 'userSpaceOnUse', 'maskUnits': 'userSpaceOnUse',
}); }, [
const shapeMaskID = env.addDef(shapeMask); svg.make('rect', {
const shape = svg.make('g', {
'mask': 'url(#' + shapeMaskID + ')',
});
shapeMask.appendChild(svg.make('rect', {
'x': x, 'x': x,
'y': y - 5, 'y': y - 5,
'width': width, 'width': width,
'height': height + 10, 'height': height + 10,
'fill': 'url(#' + maskGradID + ')', 'fill': 'url(#' + maskGradID + ')',
})); }),
]);
const shapeMaskID = env.addDef(shapeMask);
if(labelWidth > 0) { if(labelWidth > 0) {
shapeMask.appendChild(svg.make('rect', { shapeMask.appendChild(svg.make('rect', {
'x': x + (width - labelWidth) / 2, 'x': x + (width - labelWidth) / 2,
@ -324,10 +323,15 @@ define([
const pathTop = new SVGShapes.PatternedLine(pattern) const pathTop = new SVGShapes.PatternedLine(pattern)
.move(x, y) .move(x, y)
.line(x + width, y); .line(x + width, y);
shape.appendChild(svg.make('path', Object.assign({
const shape = svg.make('g', {
'mask': 'url(#' + shapeMaskID + ')',
}, [
svg.make('path', Object.assign({
'd': pathTop.asPath(), 'd': pathTop.asPath(),
'fill': 'none', 'fill': 'none',
}, lineAttrs))); }, lineAttrs)),
]);
if(height > 0) { if(height > 0) {
const pathBase = new SVGShapes.PatternedLine(pattern) const pathBase = new SVGShapes.PatternedLine(pattern)

View File

@ -616,8 +616,8 @@ define([
{var1: 0, move: false} {var1: 0, move: false}
); );
const g = svg.make('g'); return svg.make('g', {}, [
g.appendChild(svg.make('path', Object.assign({ svg.make('path', Object.assign({
'd': ( 'd': (
lT.nodes + lT.nodes +
lF.nodes + lF.nodes +
@ -626,30 +626,30 @@ define([
lL.nodes lL.nodes
), ),
'fill': '#FFFFFF', 'fill': '#FFFFFF',
}, PENCIL.normal))); }, PENCIL.normal)),
g.appendChild(svg.make('path', Object.assign({ svg.make('path', Object.assign({
'd': lF1.nodes + lF2.nodes, 'd': lF1.nodes + lF2.nodes,
'fill': 'none', 'fill': 'none',
}, PENCIL.normal))); }, PENCIL.normal)),
]);
return g;
} }
renderLineDivider({x, y, labelWidth, width, height}) { renderLineDivider({x, y, labelWidth, width, height}) {
let shape = null; let shape = null;
const yPos = y + height / 2; const yPos = y + height / 2;
if(labelWidth > 0) { if(labelWidth > 0) {
shape = svg.make('g'); shape = svg.make('g', {}, [
shape.appendChild(this.renderLine( this.renderLine(
{x, y: yPos}, {x, y: yPos},
{x: x + (width - labelWidth) / 2, y: yPos}, {x: x + (width - labelWidth) / 2, y: yPos},
{} {}
)); ),
shape.appendChild(this.renderLine( this.renderLine(
{x: x + (width + labelWidth) / 2, y: yPos}, {x: x + (width + labelWidth) / 2, y: yPos},
{x: x + width, y: yPos}, {x: x + width, y: yPos},
{} {}
)); ),
]);
} else { } else {
shape = this.renderLine( shape = this.renderLine(
{x, y: yPos}, {x, y: yPos},
@ -853,19 +853,16 @@ define([
const line = l1.nodes + l2.nodes; const line = l1.nodes + l2.nodes;
const g = svg.make('g'); return svg.make('g', {}, [
svg.make('path', {
g.appendChild(svg.make('path', {
'd': line + 'L' + x + ' ' + y, 'd': line + 'L' + x + ' ' + y,
'fill': '#FFFFFF', 'fill': '#FFFFFF',
})); }),
svg.make('path', Object.assign({
g.appendChild(svg.make('path', Object.assign({
'd': line, 'd': line,
'fill': '#FFFFFF', 'fill': '#FFFFFF',
}, PENCIL.normal))); }, PENCIL.normal)),
]);
return g;
} }
renderSeparator({x1, y1, x2, y2}) { renderSeparator({x1, y1, x2, y2}) {

View File

@ -5,7 +5,7 @@ define(['svg/SVGUtilities'], (svg) => {
// 1x1 px squares for repeatable renders in all browsers // 1x1 px squares for repeatable renders in all browsers
function merge(state, newState) { function merge(state, newState) {
for(let k in state) { for(const k in state) {
if(state.hasOwnProperty(k)) { if(state.hasOwnProperty(k)) {
if(newState[k] !== null && newState[k] !== undefined) { if(newState[k] !== null && newState[k] !== undefined) {
state[k] = newState[k]; state[k] = newState[k];
@ -38,9 +38,8 @@ define(['svg/SVGUtilities'], (svg) => {
}, this.state.attrs); }, this.state.attrs);
while(this.nodes.length < count) { while(this.nodes.length < count) {
const element = svg.make('text', attrs);
const text = svg.makeText(); const text = svg.makeText();
element.appendChild(text); const element = svg.make('text', attrs, [text]);
this.container.appendChild(element); this.container.appendChild(element);
this.nodes.push({element, text}); this.nodes.push({element, text});
} }

View File

@ -18,14 +18,14 @@ define([
} }
function renderNote(attrs, flickAttrs, position) { function renderNote(attrs, flickAttrs, position) {
const g = svg.make('g');
const x0 = position.x; const x0 = position.x;
const x1 = position.x + position.width; const x1 = position.x + position.width;
const y0 = position.y; const y0 = position.y;
const y1 = position.y + position.height; const y1 = position.y + position.height;
const flick = 7; const flick = 7;
g.appendChild(svg.make('polygon', Object.assign({ return svg.make('g', {}, [
svg.make('polygon', Object.assign({
'points': ( 'points': (
x0 + ' ' + y0 + ' ' + x0 + ' ' + y0 + ' ' +
(x1 - flick) + ' ' + y0 + ' ' + (x1 - flick) + ' ' + y0 + ' ' +
@ -33,17 +33,15 @@ define([
x1 + ' ' + y1 + ' ' + x1 + ' ' + y1 + ' ' +
x0 + ' ' + y1 x0 + ' ' + y1
), ),
}, attrs))); }, attrs)),
svg.make('polyline', Object.assign({
g.appendChild(svg.make('polyline', Object.assign({
'points': ( 'points': (
(x1 - flick) + ' ' + y0 + ' ' + (x1 - flick) + ' ' + y0 + ' ' +
(x1 - flick) + ' ' + (y0 + flick) + ' ' + (x1 - flick) + ' ' + (y0 + flick) + ' ' +
x1 + ' ' + (y0 + flick) x1 + ' ' + (y0 + flick)
), ),
}, flickAttrs))); }, flickAttrs)),
]);
return g;
} }
function calculateAnchor(x, attrs, padding) { function calculateAnchor(x, attrs, padding) {

View File

@ -14,7 +14,7 @@ define(['./SVGUtilities'], (svg) => {
} }
function merge(state, newState) { function merge(state, newState) {
for(let k in state) { for(const k in state) {
if(state.hasOwnProperty(k)) { if(state.hasOwnProperty(k)) {
if(newState[k] !== null && newState[k] !== undefined) { if(newState[k] !== null && newState[k] !== undefined) {
state[k] = newState[k]; state[k] = newState[k];
@ -30,8 +30,7 @@ define(['./SVGUtilities'], (svg) => {
formattedLine.forEach(({text, attrs}) => { formattedLine.forEach(({text, attrs}) => {
const textNode = svg.makeText(text); const textNode = svg.makeText(text);
if(attrs) { if(attrs) {
const span = svg.make('tspan', attrs); const span = svg.make('tspan', attrs, [textNode]);
span.appendChild(textNode);
node.appendChild(span); node.appendChild(span);
} else { } else {
node.appendChild(textNode); node.appendChild(textNode);

View File

@ -7,13 +7,16 @@ define(() => {
return document.createTextNode(text); return document.createTextNode(text);
} }
function make(type, attrs = {}) { function make(type, attrs = {}, children = []) {
const o = document.createElementNS(NS, type); const o = document.createElementNS(NS, type);
for(let k in attrs) { for(const k in attrs) {
if(attrs.hasOwnProperty(k)) { if(attrs.hasOwnProperty(k)) {
o.setAttribute(k, attrs[k]); o.setAttribute(k, attrs[k]);
} }
} }
for(const c of children) {
o.appendChild(c);
}
return o; return o;
} }