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);
}
function make(type, attrs = {}) {
function make(type, attrs = {}, children = []) {
const o = document.createElementNS(NS, type);
for(let k in attrs) {
for(const k in attrs) {
if(attrs.hasOwnProperty(k)) {
o.setAttribute(k, attrs[k]);
}
}
for(const c of children) {
o.appendChild(c);
}
return o;
}
@ -3429,7 +3432,7 @@ define('svg/SVGTextBlock',['./SVGUtilities'], (svg) => {
}
function merge(state, newState) {
for(let k in state) {
for(const k in state) {
if(state.hasOwnProperty(k)) {
if(newState[k] !== null && newState[k] !== undefined) {
state[k] = newState[k];
@ -3445,8 +3448,7 @@ define('svg/SVGTextBlock',['./SVGUtilities'], (svg) => {
formattedLine.forEach(({text, attrs}) => {
const textNode = svg.makeText(text);
if(attrs) {
const span = svg.make('tspan', attrs);
span.appendChild(textNode);
const span = svg.make('tspan', attrs, [textNode]);
node.appendChild(span);
} else {
node.appendChild(textNode);
@ -3775,14 +3777,14 @@ define('svg/SVGShapes',[
}
function renderNote(attrs, flickAttrs, position) {
const g = svg.make('g');
const x0 = position.x;
const x1 = position.x + position.width;
const y0 = position.y;
const y1 = position.y + position.height;
const flick = 7;
g.appendChild(svg.make('polygon', Object.assign({
return svg.make('g', {}, [
svg.make('polygon', Object.assign({
'points': (
x0 + ' ' + y0 + ' ' +
(x1 - flick) + ' ' + y0 + ' ' +
@ -3790,17 +3792,15 @@ define('svg/SVGShapes',[
x1 + ' ' + y1 + ' ' +
x0 + ' ' + y1
),
}, attrs)));
g.appendChild(svg.make('polyline', Object.assign({
}, attrs)),
svg.make('polyline', Object.assign({
'points': (
(x1 - flick) + ' ' + y0 + ' ' +
(x1 - flick) + ' ' + (y0 + flick) + ' ' +
x1 + ' ' + (y0 + flick)
),
}, flickAttrs)));
return g;
}, flickAttrs)),
]);
}
function calculateAnchor(x, attrs, padding) {
@ -3938,8 +3938,6 @@ define('sequence/components/BaseComponent',[],() => {
primaryY,
fillLayer,
blockLayer,
shapeLayer,
labelLayer,
theme,
agentInfos,
textSizer,
@ -4049,7 +4047,7 @@ define('sequence/components/Block',[
padding: config.section.label.padding,
boxAttrs: {'fill': '#000000'},
labelAttrs: config.section.label.labelAttrs,
boxLayer: env.maskLayer,
boxLayer: env.lineMaskLayer,
labelLayer: clickable,
SVGTextBlockClass: env.SVGTextBlockClass,
});
@ -4165,7 +4163,7 @@ define('sequence/components/Block',[
env.fillLayer.appendChild(shapes.fill);
}
if(shapes.mask) {
env.maskLayer.appendChild(shapes.mask);
env.lineMaskLayer.appendChild(shapes.mask);
}
return env.primaryY + config.margin.bottom + env.theme.actionMargin;
@ -4243,8 +4241,10 @@ define('sequence/components/Parallel',[
const originalMakeRegion = env.makeRegion;
let bottomY = 0;
stage.stages.forEach((subStage) => {
env.makeRegion = (o, stageOverride = null) => {
return originalMakeRegion(o, stageOverride || subStage);
env.makeRegion = (options = {}) => {
return originalMakeRegion(Object.assign({
stageOverride: subStage,
}, options));
};
const component = env.components.get(subStage.type);
@ -4341,30 +4341,30 @@ define('sequence/components/AgentCap',[
render(y, {x, formattedLabel}, env) {
const config = env.theme.agentCap.box;
const clickable = env.makeRegion();
const {width, height} = SVGShapes.renderBoxedText(formattedLabel, {
const text = SVGShapes.renderBoxedText(formattedLabel, {
x,
y,
padding: config.padding,
boxAttrs: config.boxAttrs,
boxRenderer: config.boxRenderer,
labelAttrs: config.labelAttrs,
boxLayer: env.shapeLayer,
boxLayer: clickable,
labelLayer: clickable,
SVGTextBlockClass: env.SVGTextBlockClass,
});
clickable.insertBefore(svg.make('rect', {
'x': x - width / 2,
'x': x - text.width / 2,
'y': y,
'width': width,
'height': height,
'width': text.width,
'height': text.height,
'fill': 'transparent',
'class': 'outline',
}), clickable.firstChild);
}), text.label.firstLine());
return {
lineTop: 0,
lineBottom: height,
height,
lineBottom: text.height,
height: text.height,
};
}
}
@ -4388,9 +4388,10 @@ define('sequence/components/AgentCap',[
const config = env.theme.agentCap.cross;
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,
'y': y,
'width': d * 2,
@ -4438,14 +4439,14 @@ define('sequence/components/AgentCap',[
);
const height = barCfg.height;
env.shapeLayer.appendChild(barCfg.render({
const clickable = env.makeRegion();
clickable.appendChild(barCfg.render({
x: x - width / 2,
y,
width,
height,
}));
env.makeRegion().appendChild(svg.make('rect', {
clickable.appendChild(svg.make('rect', {
'x': x - width / 2,
'y': y,
'width': width,
@ -4481,24 +4482,24 @@ define('sequence/components/AgentCap',[
const ratio = config.height / (config.height + config.extend);
const gradID = env.addDef(isBegin ? 'FadeIn' : 'FadeOut', () => {
const grad = svg.make('linearGradient', {
return svg.make('linearGradient', {
'x1': '0%',
'y1': isBegin ? '100%' : '0%',
'x2': '0%',
'y2': isBegin ? '0%' : '100%',
});
grad.appendChild(svg.make('stop', {
}, [
svg.make('stop', {
'offset': '0%',
'stop-color': '#FFFFFF',
}));
grad.appendChild(svg.make('stop', {
}),
svg.make('stop', {
'offset': (100 * ratio).toFixed(3) + '%',
'stop-color': '#000000',
}));
return grad;
}),
]);
});
env.maskLayer.appendChild(svg.make('rect', {
env.lineMaskLayer.appendChild(svg.make('rect', {
'x': x - config.width / 2,
'y': y - (isBegin ? config.extend : 0),
'width': config.width,
@ -4849,7 +4850,7 @@ define('sequence/components/Connect',[
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 line = config.line[options.line];
const lArrow = ARROWHEADS[options.left];
@ -4865,14 +4866,14 @@ define('sequence/components/Connect',[
xR,
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,
y: rendered.p1.y,
}, {dx: 1, dy: 0});
rArrow.render(env.shapeLayer, env.theme, {
rArrow.render(clickable, env.theme, {
x: rendered.p2.x - dx2,
y: rendered.p2.y,
}, {dx: 1, dy: 0});
@ -4906,7 +4907,7 @@ define('sequence/components/Connect',[
padding: config.mask.padding,
boxAttrs: {'fill': '#000000'},
labelAttrs: config.label.loopbackAttrs,
boxLayer: env.maskLayer,
boxLayer: env.lineMaskLayer,
labelLayer: clickable,
SVGTextBlockClass: env.SVGTextBlockClass,
});
@ -4928,7 +4929,7 @@ define('sequence/components/Connect',[
x2: to.x + to.currentMaxRad,
y2: env.primaryY,
xR,
}, options, env);
}, options, env, clickable);
const raise = Math.max(height, lArrow.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 line = config.line[options.line];
const lArrow = ARROWHEADS[options.left];
@ -4970,13 +4971,13 @@ define('sequence/components/Connect',[
x2: x2 - d2 * dx,
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 p2 = {x: rendered.p2.x + d2 * dx, y: rendered.p2.y + d2 * dy};
lArrow.render(env.shapeLayer, env.theme, p1, {dx, dy});
rArrow.render(env.shapeLayer, env.theme, p2, {dx: -dx, dy: -dy});
lArrow.render(clickable, env.theme, p1, {dx, dy});
rArrow.render(clickable, env.theme, p2, {dx: -dx, dy: -dy});
return {
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;
if(from.isVirtualSource) {
env.shapeLayer.appendChild(config.render({
x: renderedLine.p1.x - config.radius,
y: renderedLine.p1.y,
clickable.appendChild(config.render({
x: rendered.p1.x - config.radius,
y: rendered.p1.y,
radius: config.radius,
}));
}
if(to.isVirtualSource) {
env.shapeLayer.appendChild(config.render({
x: renderedLine.p2.x + config.radius,
y: renderedLine.p2.y,
clickable.appendChild(config.render({
x: rendered.p2.x + config.radius,
y: rendered.p2.y,
radius: config.radius,
}));
}
@ -5032,7 +5033,7 @@ define('sequence/components/Connect',[
padding: config.mask.padding,
boxAttrs,
labelAttrs: config.label.attrs,
boxLayer: env.maskLayer,
boxLayer: env.lineMaskLayer,
labelLayer,
SVGTextBlockClass: env.SVGTextBlockClass,
});
@ -5060,7 +5061,7 @@ define('sequence/components/Connect',[
y1: yBegin,
x2,
y2: env.primaryY,
}, options, env);
}, options, env, clickable);
const arrowSpread = Math.max(
rendered.lArrow.height(env.theme),
@ -5069,7 +5070,7 @@ define('sequence/components/Connect',[
const lift = Math.max(height, arrowSpread);
this.renderVirtualSources(from, to, rendered, env);
this.renderVirtualSources({from, to, rendered}, env, clickable);
clickable.appendChild(svg.make('path', {
'd': (
@ -5287,13 +5288,6 @@ define('sequence/components/Note',['./BaseComponent', 'svg/SVGUtilities'], (Base
break;
}
env.shapeLayer.appendChild(config.boxRenderer({
x: x0,
y: env.topY + config.margin.top,
width: x1 - x0,
height: fullH,
}));
clickable.insertBefore(svg.make('rect', {
'x': x0,
'y': env.topY + config.margin.top,
@ -5303,6 +5297,13 @@ define('sequence/components/Note',['./BaseComponent', 'svg/SVGUtilities'], (Base
'class': 'outline',
}), clickable.firstChild);
clickable.insertBefore(config.boxRenderer({
x: x0,
y: env.topY + config.margin.top,
width: x1 - x0,
height: fullH,
}), clickable.firstChild);
return (
env.topY +
config.margin.top +
@ -5507,7 +5508,7 @@ define('sequence/components/Divider',[
const left = env.agentInfos.get('[');
const right = env.agentInfos.get(']');
const clickable = env.makeRegion();
const clickable = env.makeRegion({unmasked: true});
let labelWidth = 0;
let labelHeight = 0;
@ -5531,7 +5532,7 @@ define('sequence/components/Divider',[
padding: config.padding,
boxAttrs: {'fill': '#000000'},
labelAttrs: config.labelAttrs,
boxLayer: env.maskLayer,
boxLayer: env.fullMaskLayer,
labelLayer: clickable,
SVGTextBlockClass: env.SVGTextBlockClass,
});
@ -5551,7 +5552,7 @@ define('sequence/components/Divider',[
clickable.insertBefore(shape, clickable.firstChild);
}
if(mask) {
env.maskLayer.appendChild(mask);
env.fullMaskLayer.appendChild(mask);
}
clickable.insertBefore(svg.make('rect', {
'x': left.x - config.extend,
@ -5683,9 +5684,7 @@ define('sequence/Renderer',[
buildMetadata() {
this.metaCode = svg.makeText();
const metadata = svg.make('metadata');
metadata.appendChild(this.metaCode);
return metadata;
return svg.make('metadata', {}, [this.metaCode]);
}
buildStaticElements() {
@ -5693,26 +5692,37 @@ define('sequence/Renderer',[
this.themeDefs = 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',
'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.agentLines = svg.make('g', {
'mask': 'url(#' + this.namespace + 'LineMask)',
});
this.blocks = svg.make('g');
this.actionShapes = svg.make('g');
this.actionLabels = svg.make('g');
this.shapes = svg.make('g');
this.unmaskedShapes = svg.make('g');
this.base.appendChild(this.buildMetadata());
this.base.appendChild(this.themeDefs);
this.base.appendChild(this.defs);
this.base.appendChild(this.backgroundFills);
this.base.appendChild(this.agentLines);
this.base.appendChild(this.blocks);
this.base.appendChild(this.actionShapes);
this.base.appendChild(this.actionLabels);
this.base.appendChild(
svg.make('g', {
'mask': 'url(#' + this.namespace + 'FullMask)',
}, [
this.agentLines,
this.blocks,
this.shapes,
])
);
this.base.appendChild(this.unmaskedShapes);
this.title = new this.SVGTextBlockClass(this.base);
this.sizer = new this.SVGTextBlockClass.SizeTester(this.base);
@ -5893,10 +5903,11 @@ define('sequence/Renderer',[
this.trigger('mouseout');
};
const makeRegion = (o, stageOverride = null) => {
if(!o) {
o = svg.make('g');
}
const makeRegion = ({
stageOverride = null,
unmasked = false,
} = {}) => {
const o = svg.make('g');
const targetStage = (stageOverride || stage);
this.addHighlightObject(targetStage.ln, o);
o.setAttribute('class', 'region');
@ -5907,7 +5918,7 @@ define('sequence/Renderer',[
o.addEventListener('click', () => {
this.trigger('click', [targetStage]);
});
this.actionLabels.appendChild(o);
(unmasked ? this.unmaskedShapes : this.shapes).appendChild(o);
return o;
};
@ -5916,9 +5927,8 @@ define('sequence/Renderer',[
primaryY: topY + topShift,
fillLayer: this.backgroundFills,
blockLayer: this.blocks,
shapeLayer: this.actionShapes,
labelLayer: this.actionLabels,
maskLayer: this.mask,
fullMaskLayer: this.fullMask,
lineMaskLayer: this.lineMask,
theme: this.theme,
agentInfos: this.agentInfos,
textSizer: this.sizer,
@ -6020,10 +6030,15 @@ define('sequence/Renderer',[
this.width = x1 - x0;
this.height = y1 - y0;
this.maskReveal.setAttribute('x', x0);
this.maskReveal.setAttribute('y', y0);
this.maskReveal.setAttribute('width', this.width);
this.maskReveal.setAttribute('height', this.height);
this.fullMaskReveal.setAttribute('x', x0);
this.fullMaskReveal.setAttribute('y', y0);
this.fullMaskReveal.setAttribute('width', this.width);
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', (
x0 + ' ' + y0 + ' ' +
@ -6048,14 +6063,17 @@ define('sequence/Renderer',[
this.highlights.clear();
this.currentHighlight = -1;
svg.empty(this.defs);
svg.empty(this.mask);
svg.empty(this.fullMask);
svg.empty(this.lineMask);
svg.empty(this.backgroundFills);
svg.empty(this.agentLines);
svg.empty(this.blocks);
svg.empty(this.actionShapes);
svg.empty(this.actionLabels);
this.mask.appendChild(this.maskReveal);
this.defs.appendChild(this.mask);
svg.empty(this.shapes);
svg.empty(this.unmaskedShapes);
this.fullMask.appendChild(this.fullMaskReveal);
this.lineMask.appendChild(this.lineMaskReveal);
this.defs.appendChild(this.fullMask);
this.defs.appendChild(this.lineMask);
this._resetState();
}
@ -6515,7 +6533,7 @@ define('sequence/themes/BaseTheme',[
return o;
}
const r = {};
for(let k in o) {
for(const k in o) {
if(o.hasOwnProperty(k)) {
r[k] = deepCopy(o[k]);
}
@ -6720,21 +6738,22 @@ define('sequence/themes/BaseTheme',[
let shape = null;
const yPos = y + height / 2;
if(labelWidth > 0) {
shape = svg.make('g');
shape.appendChild(svg.make('line', Object.assign({
shape = svg.make('g', {}, [
svg.make('line', Object.assign({
'x1': x,
'y1': yPos,
'x2': x + (width - labelWidth) / 2,
'y2': yPos,
'fill': 'none',
}, lineAttrs)));
shape.appendChild(svg.make('line', Object.assign({
}, lineAttrs)),
svg.make('line', Object.assign({
'x1': x + (width + labelWidth) / 2,
'y1': yPos,
'x2': x + width,
'y2': yPos,
'fill': 'none',
}, lineAttrs)));
}, lineAttrs)),
]);
} else {
shape = svg.make('line', Object.assign({
'x1': x,
@ -6770,40 +6789,38 @@ define('sequence/themes/BaseTheme',[
) => {
const maskGradID = env.addDef('tear-grad', () => {
const px = 100 / width;
const grad = svg.make('linearGradient');
grad.appendChild(svg.make('stop', {
return svg.make('linearGradient', {}, [
svg.make('stop', {
'offset': (fadeBegin * px) + '%',
'stop-color': '#000000',
}));
grad.appendChild(svg.make('stop', {
}),
svg.make('stop', {
'offset': ((fadeBegin + fadeSize) * px) + '%',
'stop-color': '#FFFFFF',
}));
grad.appendChild(svg.make('stop', {
}),
svg.make('stop', {
'offset': (100 - (fadeBegin + fadeSize) * px) + '%',
'stop-color': '#FFFFFF',
}));
grad.appendChild(svg.make('stop', {
}),
svg.make('stop', {
'offset': (100 - fadeBegin * px) + '%',
'stop-color': '#000000',
}));
return grad;
}),
]);
});
const shapeMask = svg.make('mask', {
'maskUnits': 'userSpaceOnUse',
});
const shapeMaskID = env.addDef(shapeMask);
const shape = svg.make('g', {
'mask': 'url(#' + shapeMaskID + ')',
});
shapeMask.appendChild(svg.make('rect', {
}, [
svg.make('rect', {
'x': x,
'y': y - 5,
'width': width,
'height': height + 10,
'fill': 'url(#' + maskGradID + ')',
}));
}),
]);
const shapeMaskID = env.addDef(shapeMask);
if(labelWidth > 0) {
shapeMask.appendChild(svg.make('rect', {
'x': x + (width - labelWidth) / 2,
@ -6827,10 +6844,15 @@ define('sequence/themes/BaseTheme',[
const pathTop = new SVGShapes.PatternedLine(pattern)
.move(x, 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(),
'fill': 'none',
}, lineAttrs)));
}, lineAttrs)),
]);
if(height > 0) {
const pathBase = new SVGShapes.PatternedLine(pattern)
@ -9070,8 +9092,8 @@ define('sequence/themes/Sketch',[
{var1: 0, move: false}
);
const g = svg.make('g');
g.appendChild(svg.make('path', Object.assign({
return svg.make('g', {}, [
svg.make('path', Object.assign({
'd': (
lT.nodes +
lF.nodes +
@ -9080,30 +9102,30 @@ define('sequence/themes/Sketch',[
lL.nodes
),
'fill': '#FFFFFF',
}, PENCIL.normal)));
g.appendChild(svg.make('path', Object.assign({
}, PENCIL.normal)),
svg.make('path', Object.assign({
'd': lF1.nodes + lF2.nodes,
'fill': 'none',
}, PENCIL.normal)));
return g;
}, PENCIL.normal)),
]);
}
renderLineDivider({x, y, labelWidth, width, height}) {
let shape = null;
const yPos = y + height / 2;
if(labelWidth > 0) {
shape = svg.make('g');
shape.appendChild(this.renderLine(
shape = svg.make('g', {}, [
this.renderLine(
{x, 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, y: yPos},
{}
));
),
]);
} else {
shape = this.renderLine(
{x, y: yPos},
@ -9307,19 +9329,16 @@ define('sequence/themes/Sketch',[
const line = l1.nodes + l2.nodes;
const g = svg.make('g');
g.appendChild(svg.make('path', {
return svg.make('g', {}, [
svg.make('path', {
'd': line + 'L' + x + ' ' + y,
'fill': '#FFFFFF',
}));
g.appendChild(svg.make('path', Object.assign({
}),
svg.make('path', Object.assign({
'd': line,
'fill': '#FFFFFF',
}, PENCIL.normal)));
return g;
}, PENCIL.normal)),
]);
}
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);
}
function makeNode(type, attrs = {}) {
function makeNode(type, attrs = {}, children = []) {
const o = document.createElement(type);
for(let k in attrs) {
for(const k in attrs) {
if(attrs.hasOwnProperty(k)) {
o.setAttribute(k, attrs[k]);
}
}
for(const c of children) {
o.appendChild(c);
}
return o;
}
@ -169,12 +172,10 @@ define(['require'], (require) => {
buildOptionsLinks() {
const options = makeNode('div', {'class': 'options links'});
this.links.forEach((link) => {
const linkNode = makeNode('a', {
options.appendChild(makeNode('a', {
'href': link.href,
'target': '_blank',
});
linkNode.appendChild(makeText(link.label));
options.appendChild(linkNode);
}, [makeText(link.label)]));
});
return options;
}
@ -183,8 +184,7 @@ define(['require'], (require) => {
this.downloadPNG = makeNode('a', {
'href': '#',
'download': 'SequenceDiagram.png',
});
this.downloadPNG.appendChild(makeText('Download PNG'));
}, [makeText('Download PNG')]);
on(this.downloadPNG, [
'focus',
'mouseover',
@ -195,14 +195,13 @@ define(['require'], (require) => {
this.downloadSVG = makeNode('a', {
'href': '#',
'download': 'SequenceDiagram.svg',
});
this.downloadSVG.appendChild(makeText('SVG'));
}, [makeText('SVG')]);
on(this.downloadSVG, ['click'], this._downloadSVGClick);
const options = makeNode('div', {'class': 'options downloads'});
options.appendChild(this.downloadPNG);
options.appendChild(this.downloadSVG);
return options;
return makeNode('div', {'class': 'options downloads'}, [
this.downloadPNG,
this.downloadSVG,
]);
}
buildEditor(container) {
@ -282,13 +281,12 @@ define(['require'], (require) => {
buildLibrary(container) {
this.library.forEach((lib) => {
const hold = makeNode('div', {
'class': 'library-item',
});
const holdInner = makeNode('div', {
'title': lib.title || lib.code,
});
hold.appendChild(holdInner);
const hold = makeNode('div', {
'class': 'library-item',
}, [holdInner]);
hold.addEventListener(
'click',
this.addCodeBlock.bind(this, lib.code)
@ -308,26 +306,22 @@ define(['require'], (require) => {
}
buildErrorReport() {
this.errorMsg = makeNode('div', {'class': 'msg-error'});
this.errorText = makeText();
this.errorMsg.appendChild(this.errorText);
this.errorMsg = makeNode('div', {'class': 'msg-error'}, [
this.errorText,
]);
return this.errorMsg;
}
buildViewPane() {
const viewPane = makeNode('div', {'class': 'pane-view'});
const viewPaneScroller = makeNode('div', {
'class': 'pane-view-scroller',
});
this.viewPaneInner = makeNode('div', {
'class': 'pane-view-inner',
});
this.viewPaneInner = makeNode('div', {'class': 'pane-view-inner'});
viewPane.appendChild(viewPaneScroller);
viewPaneScroller.appendChild(this.viewPaneInner);
viewPane.appendChild(this.buildErrorReport());
return viewPane;
return makeNode('div', {'class': 'pane-view'}, [
makeNode('div', {'class': 'pane-view-scroller'}, [
this.viewPaneInner,
]),
this.buildErrorReport(),
]);
}
buildLeftPanes(container) {
@ -336,15 +330,14 @@ define(['require'], (require) => {
let libPane = null;
if(this.library.length > 0) {
libPane = makeNode('div', {'class': 'pane-library'});
const libPaneScroller = makeNode('div', {
'class': 'pane-library-scroller',
});
const libPaneInner = makeNode('div', {
'class': 'pane-library-inner',
});
libPaneScroller.appendChild(libPaneInner);
libPane.appendChild(libPaneScroller);
libPane = makeNode('div', {'class': 'pane-library'}, [
makeNode('div', {'class': 'pane-library-scroller'}, [
libPaneInner,
]),
]);
container.appendChild(libPane);
this.buildLibrary(libPaneInner);
@ -361,17 +354,17 @@ define(['require'], (require) => {
build(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();
hold.appendChild(viewPane);
hold.appendChild(this.buildOptionsLinks());
hold.appendChild(this.buildOptionsDownloads());
const lPane = makeNode('div', {'class': 'pane-side'});
const hold = makeNode('div', {'class': 'pane-hold'}, [
lPane,
viewPane,
this.buildOptionsLinks(),
this.buildOptionsDownloads(),
]);
container.appendChild(hold);
makeSplit([lPane, viewPane], {
direction: 'horizontal',
@ -380,6 +373,7 @@ define(['require'], (require) => {
minSize: [10, 10],
});
const {codePane} = this.buildLeftPanes(lPane);
this.code = this.buildEditor(codePane);
this.viewPaneInner.appendChild(this.diagram.dom());

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -58,8 +58,10 @@ define([
const originalMakeRegion = env.makeRegion;
let bottomY = 0;
stage.stages.forEach((subStage) => {
env.makeRegion = (o, stageOverride = null) => {
return originalMakeRegion(o, stageOverride || subStage);
env.makeRegion = (options = {}) => {
return originalMakeRegion(Object.assign({
stageOverride: subStage,
}, options));
};
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',
'SelfConnect.svg',
'Divider.svg',
'DividerMasking.svg',
'Asynchronous.svg',
'Block.svg',
'Reference.svg',

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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