Select code in editor when elements are clicked [#16]
This commit is contained in:
parent
b562120c33
commit
25ffd6a904
|
@ -0,0 +1,61 @@
|
|||
define(() => {
|
||||
'use strict';
|
||||
|
||||
return class EventObject {
|
||||
constructor() {
|
||||
this.listeners = new Map();
|
||||
this.forwards = new Set();
|
||||
}
|
||||
|
||||
addEventListener(type, callback) {
|
||||
const l = this.listeners.get(type);
|
||||
if(l) {
|
||||
l.push(callback);
|
||||
} else {
|
||||
this.listeners.set(type, [callback]);
|
||||
}
|
||||
}
|
||||
|
||||
removeEventListener(type, fn) {
|
||||
const l = this.listeners.get(type);
|
||||
if(!l) {
|
||||
return;
|
||||
}
|
||||
const i = l.indexOf(fn);
|
||||
if(i !== -1) {
|
||||
l.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
countEventListeners(type) {
|
||||
return (this.listeners.get(type) || []).length;
|
||||
}
|
||||
|
||||
removeAllEventListeners(type) {
|
||||
if(type) {
|
||||
this.listeners.delete(type);
|
||||
} else {
|
||||
this.listeners.clear();
|
||||
}
|
||||
}
|
||||
|
||||
addEventForwarding(target) {
|
||||
this.forwards.add(target);
|
||||
}
|
||||
|
||||
removeEventForwarding(target) {
|
||||
this.forwards.delete(target);
|
||||
}
|
||||
|
||||
removeAllEventForwardings() {
|
||||
this.forwards.clear();
|
||||
}
|
||||
|
||||
trigger(type, params = []) {
|
||||
(this.listeners.get(type) || []).forEach(
|
||||
(listener) => listener.apply(null, params)
|
||||
);
|
||||
this.forwards.forEach((fwd) => fwd.trigger(type, params));
|
||||
}
|
||||
};
|
||||
});
|
|
@ -0,0 +1,188 @@
|
|||
defineDescribe('EventObject', ['./EventObject'], (EventObject) => {
|
||||
'use strict';
|
||||
|
||||
let o = null;
|
||||
|
||||
beforeEach(() => {
|
||||
o = new EventObject();
|
||||
});
|
||||
|
||||
describe('trigger', () => {
|
||||
it('invokes registered listeners', () => {
|
||||
let triggered = 0;
|
||||
o.addEventListener('foo', () => {
|
||||
++ triggered;
|
||||
});
|
||||
|
||||
o.trigger('foo');
|
||||
|
||||
expect(triggered).toEqual(1);
|
||||
});
|
||||
|
||||
it('invokes with the given parameters', () => {
|
||||
let capturedParam1 = null;
|
||||
let capturedParam2 = null;
|
||||
o.addEventListener('foo', (param1, param2) => {
|
||||
capturedParam1 = param1;
|
||||
capturedParam2 = param2;
|
||||
});
|
||||
|
||||
o.trigger('foo', ['a', 'b']);
|
||||
|
||||
expect(capturedParam1).toEqual('a');
|
||||
expect(capturedParam2).toEqual('b');
|
||||
});
|
||||
|
||||
it('only invokes relevant callbacks', () => {
|
||||
let triggered = 0;
|
||||
o.addEventListener('foo', () => {
|
||||
++ triggered;
|
||||
});
|
||||
|
||||
o.trigger('bar');
|
||||
|
||||
expect(triggered).toEqual(0);
|
||||
});
|
||||
|
||||
it('forwards to registered objects', () => {
|
||||
let capturedType = null;
|
||||
o.addEventForwarding({trigger: (type) => {
|
||||
capturedType = type;
|
||||
}});
|
||||
|
||||
o.trigger('bar');
|
||||
|
||||
expect(capturedType).toEqual('bar');
|
||||
});
|
||||
|
||||
it('forwards with the given parameters', () => {
|
||||
let capturedParams = null;
|
||||
o.addEventForwarding({trigger: (type, params) => {
|
||||
capturedParams = params;
|
||||
}});
|
||||
|
||||
o.trigger('bar', ['a', 'b']);
|
||||
|
||||
expect(capturedParams[0]).toEqual('a');
|
||||
expect(capturedParams[1]).toEqual('b');
|
||||
});
|
||||
});
|
||||
|
||||
describe('countEventListeners', () => {
|
||||
it('returns the number of event listeners of a given type', () => {
|
||||
o.addEventListener('foo', () => {});
|
||||
o.addEventListener('foo', () => {});
|
||||
expect(o.countEventListeners('foo')).toEqual(2);
|
||||
});
|
||||
|
||||
it('does not count unrequested types', () => {
|
||||
o.addEventListener('foo', () => {});
|
||||
o.addEventListener('foo', () => {});
|
||||
o.addEventListener('bar', () => {});
|
||||
expect(o.countEventListeners('bar')).toEqual(1);
|
||||
});
|
||||
|
||||
it('returns 0 for events which have no listeners', () => {
|
||||
expect(o.countEventListeners('foo')).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeEventListener', () => {
|
||||
it('removes the requested listener', () => {
|
||||
let triggered = 0;
|
||||
const fn = () => {
|
||||
++ triggered;
|
||||
};
|
||||
|
||||
o.addEventListener('foo', fn);
|
||||
o.trigger('foo');
|
||||
expect(triggered).toEqual(1);
|
||||
|
||||
triggered = 0;
|
||||
o.removeEventListener('foo', fn);
|
||||
o.trigger('foo');
|
||||
expect(triggered).toEqual(0);
|
||||
});
|
||||
|
||||
it('leaves other listeners', () => {
|
||||
let triggered = 0;
|
||||
const fn1 = () => {
|
||||
};
|
||||
const fn2 = () => {
|
||||
++ triggered;
|
||||
};
|
||||
|
||||
o.addEventListener('foo', fn1);
|
||||
o.addEventListener('foo', fn2);
|
||||
o.removeEventListener('foo', fn1);
|
||||
o.trigger('foo');
|
||||
expect(triggered).toEqual(1);
|
||||
});
|
||||
|
||||
it('leaves other listener types', () => {
|
||||
let triggered = 0;
|
||||
const fn = () => {
|
||||
++ triggered;
|
||||
};
|
||||
|
||||
o.addEventListener('foo', fn);
|
||||
o.addEventListener('bar', fn);
|
||||
o.removeEventListener('foo', fn);
|
||||
o.trigger('bar');
|
||||
expect(triggered).toEqual(1);
|
||||
});
|
||||
|
||||
it('silently ignores non-existent listeners', () => {
|
||||
expect(() => o.removeEventListener('foo', () => {})).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeAllEventListeners', () => {
|
||||
it('removes all listeners for the requested type', () => {
|
||||
let triggered = 0;
|
||||
const fn = () => {
|
||||
++ triggered;
|
||||
};
|
||||
|
||||
o.addEventListener('foo', fn);
|
||||
o.trigger('foo');
|
||||
expect(triggered).toEqual(1);
|
||||
|
||||
triggered = 0;
|
||||
o.removeAllEventListeners('foo');
|
||||
o.trigger('foo');
|
||||
expect(triggered).toEqual(0);
|
||||
});
|
||||
|
||||
it('leaves other listener types', () => {
|
||||
let triggered = 0;
|
||||
const fn = () => {
|
||||
++ triggered;
|
||||
};
|
||||
|
||||
o.addEventListener('foo', fn);
|
||||
o.addEventListener('bar', fn);
|
||||
o.removeAllEventListeners('foo');
|
||||
o.trigger('bar');
|
||||
expect(triggered).toEqual(1);
|
||||
});
|
||||
|
||||
it('silently ignores non-existent types', () => {
|
||||
expect(() => o.removeAllEventListeners('foo')).not.toThrow();
|
||||
});
|
||||
|
||||
it('removes all listener types when given no argument', () => {
|
||||
let triggered = 0;
|
||||
const fn = () => {
|
||||
++ triggered;
|
||||
};
|
||||
|
||||
o.addEventListener('foo', fn);
|
||||
o.addEventListener('bar', fn);
|
||||
o.removeAllEventListeners();
|
||||
o.trigger('foo');
|
||||
o.trigger('bar');
|
||||
expect(triggered).toEqual(0);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -51,6 +51,8 @@ define([
|
|||
this.pngDirty = true;
|
||||
this.updatingPNG = false;
|
||||
|
||||
this.marker = null;
|
||||
|
||||
this._downloadSVGClick = this._downloadSVGClick.bind(this);
|
||||
this._downloadPNGClick = this._downloadPNGClick.bind(this);
|
||||
this._downloadPNGFocus = this._downloadPNGFocus.bind(this);
|
||||
|
@ -147,6 +149,50 @@ define([
|
|||
return code;
|
||||
}
|
||||
|
||||
registerListeners() {
|
||||
this.code.on('change', () => this.update(false));
|
||||
|
||||
this.renderer.addEventListener('mouseover', (element) => {
|
||||
if(this.marker) {
|
||||
this.marker.clear();
|
||||
}
|
||||
if(element.ln !== undefined) {
|
||||
this.marker = this.code.markText(
|
||||
{line: element.ln, ch: 0},
|
||||
{line: element.ln + 1, ch: 0},
|
||||
{
|
||||
className: 'hover',
|
||||
inclusiveLeft: false,
|
||||
inclusiveRight: false,
|
||||
clearOnEnter: true,
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
this.renderer.addEventListener('mouseout', () => {
|
||||
if(this.marker) {
|
||||
this.marker.clear();
|
||||
this.marker = null;
|
||||
}
|
||||
});
|
||||
|
||||
this.renderer.addEventListener('click', (element) => {
|
||||
if(this.marker) {
|
||||
this.marker.clear();
|
||||
this.marker = null;
|
||||
}
|
||||
if(element.ln !== undefined) {
|
||||
this.code.setSelection(
|
||||
{line: element.ln, ch: 0},
|
||||
{line: element.ln + 1, ch: 0},
|
||||
{origin: '+focus', bias: -1}
|
||||
);
|
||||
this.code.focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
build(container) {
|
||||
const codePane = makeNode('div', {'class': 'pane-code'});
|
||||
const viewPane = makeNode('div', {'class': 'pane-view'});
|
||||
|
@ -172,7 +218,7 @@ define([
|
|||
this.code = this.buildEditor(codePane);
|
||||
this.viewPaneInner.appendChild(this.renderer.svg());
|
||||
|
||||
this.code.on('change', () => this.update(false));
|
||||
this.registerListeners();
|
||||
this.update();
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ defineDescribe('Interface', ['./Interface'], (Interface) => {
|
|||
'render',
|
||||
'svg',
|
||||
'getThemeNames',
|
||||
'addEventListener',
|
||||
]);
|
||||
renderer.svg.and.returnValue(document.createElement('svg'));
|
||||
container = jasmine.createSpyObj('container', ['appendChild']);
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
BasicTheme,
|
||||
ChunkyTheme
|
||||
) => {
|
||||
/* jshint +W072 */
|
||||
const defaultCode = (
|
||||
'title Labyrinth\n' +
|
||||
'\n' +
|
||||
|
|
|
@ -79,6 +79,7 @@
|
|||
ChunkyTheme,
|
||||
Exporter
|
||||
) => {
|
||||
/* jshint +W072 */
|
||||
const parser = new Parser();
|
||||
const generator = new Generator();
|
||||
const themes = [
|
||||
|
|
|
@ -230,6 +230,9 @@ define(['core/ArrayUtilities'], (array) => {
|
|||
if(!stage) {
|
||||
return;
|
||||
}
|
||||
if(stage.ln === undefined) {
|
||||
stage.ln = this.latestLine;
|
||||
}
|
||||
this.currentSection.stages.push(stage);
|
||||
if(isVisible) {
|
||||
this.currentNest.hasContent = true;
|
||||
|
@ -244,6 +247,11 @@ define(['core/ArrayUtilities'], (array) => {
|
|||
if(viableStages.length === 1) {
|
||||
return this.addStage(viableStages[0]);
|
||||
}
|
||||
viableStages.forEach((stage) => {
|
||||
if(stage.ln === undefined) {
|
||||
stage.ln = this.latestLine;
|
||||
}
|
||||
});
|
||||
return this.addStage({
|
||||
type: 'parallel',
|
||||
stages: viableStages,
|
||||
|
@ -503,6 +511,7 @@ define(['core/ArrayUtilities'], (array) => {
|
|||
}
|
||||
|
||||
handleStage(stage) {
|
||||
this.latestLine = stage.ln;
|
||||
try {
|
||||
this.stageHandlers[stage.type](stage);
|
||||
} catch(e) {
|
||||
|
|
|
@ -26,26 +26,29 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
|||
return {type: 'block end'};
|
||||
},
|
||||
|
||||
defineAgents: (agentNames) => {
|
||||
defineAgents: (agentNames, {ln = 0} = {}) => {
|
||||
return {
|
||||
type: 'agent define',
|
||||
agents: makeParsedAgents(agentNames),
|
||||
ln,
|
||||
};
|
||||
},
|
||||
|
||||
beginAgents: (agentNames, {mode = 'box'} = {}) => {
|
||||
beginAgents: (agentNames, {mode = 'box', ln = 0} = {}) => {
|
||||
return {
|
||||
type: 'agent begin',
|
||||
agents: makeParsedAgents(agentNames),
|
||||
mode,
|
||||
ln,
|
||||
};
|
||||
},
|
||||
|
||||
endAgents: (agentNames, {mode = 'cross'} = {}) => {
|
||||
endAgents: (agentNames, {mode = 'cross', ln = 0} = {}) => {
|
||||
return {
|
||||
type: 'agent end',
|
||||
agents: makeParsedAgents(agentNames),
|
||||
mode,
|
||||
ln,
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -54,6 +57,7 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
|||
line = '',
|
||||
left = 0,
|
||||
right = 0,
|
||||
ln = 0,
|
||||
} = {}) => {
|
||||
return {
|
||||
type: 'connect',
|
||||
|
@ -64,18 +68,21 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
|||
left,
|
||||
right,
|
||||
},
|
||||
ln,
|
||||
};
|
||||
},
|
||||
|
||||
note: (type, agentNames, {
|
||||
mode = '',
|
||||
label = '',
|
||||
ln = 0,
|
||||
} = {}) => {
|
||||
return {
|
||||
type,
|
||||
agents: makeParsedAgents(agentNames),
|
||||
mode,
|
||||
label,
|
||||
ln,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
@ -83,21 +90,25 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
|||
const GENERATED = {
|
||||
beginAgents: (agentNames, {
|
||||
mode = jasmine.anything(),
|
||||
ln = jasmine.anything(),
|
||||
} = {}) => {
|
||||
return {
|
||||
type: 'agent begin',
|
||||
agentNames,
|
||||
mode,
|
||||
ln,
|
||||
};
|
||||
},
|
||||
|
||||
endAgents: (agentNames, {
|
||||
mode = jasmine.anything(),
|
||||
ln = jasmine.anything(),
|
||||
} = {}) => {
|
||||
return {
|
||||
type: 'agent end',
|
||||
agentNames,
|
||||
mode,
|
||||
ln,
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -106,6 +117,7 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
|||
line = jasmine.anything(),
|
||||
left = jasmine.anything(),
|
||||
right = jasmine.anything(),
|
||||
ln = jasmine.anything(),
|
||||
} = {}) => {
|
||||
return {
|
||||
type: 'connect',
|
||||
|
@ -116,33 +128,42 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
|||
left,
|
||||
right,
|
||||
},
|
||||
ln,
|
||||
};
|
||||
},
|
||||
|
||||
highlight: (agentNames, highlighted) => {
|
||||
highlight: (agentNames, highlighted, {
|
||||
ln = jasmine.anything(),
|
||||
} = {}) => {
|
||||
return {
|
||||
type: 'agent highlight',
|
||||
agentNames,
|
||||
highlighted,
|
||||
ln,
|
||||
};
|
||||
},
|
||||
|
||||
note: (type, agentNames, {
|
||||
mode = jasmine.anything(),
|
||||
label = jasmine.anything(),
|
||||
ln = jasmine.anything(),
|
||||
} = {}) => {
|
||||
return {
|
||||
type,
|
||||
agentNames,
|
||||
mode,
|
||||
label,
|
||||
ln,
|
||||
};
|
||||
},
|
||||
|
||||
parallel: (stages) => {
|
||||
parallel: (stages, {
|
||||
ln = jasmine.anything(),
|
||||
} = {}) => {
|
||||
return {
|
||||
type: 'parallel',
|
||||
stages,
|
||||
ln,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
@ -172,14 +193,14 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
|||
|
||||
it('passes marks and async through', () => {
|
||||
const sequence = generator.generate({stages: [
|
||||
{type: 'mark', name: 'foo'},
|
||||
{type: 'async', target: 'foo'},
|
||||
{type: 'async', target: ''},
|
||||
{type: 'mark', name: 'foo', ln: 0},
|
||||
{type: 'async', target: 'foo', ln: 1},
|
||||
{type: 'async', target: '', ln: 2},
|
||||
]});
|
||||
expect(sequence.stages).toEqual([
|
||||
{type: 'mark', name: 'foo'},
|
||||
{type: 'async', target: 'foo'},
|
||||
{type: 'async', target: ''},
|
||||
{type: 'mark', name: 'foo', ln: 0},
|
||||
{type: 'async', target: 'foo', ln: 1},
|
||||
{type: 'async', target: '', ln: 2},
|
||||
]);
|
||||
});
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
/* jshint -W072 */ // Allow several required modules
|
||||
define([
|
||||
'core/ArrayUtilities',
|
||||
'core/EventObject',
|
||||
'svg/SVGUtilities',
|
||||
'svg/SVGShapes',
|
||||
'./components/BaseComponent',
|
||||
|
@ -10,10 +12,12 @@ define([
|
|||
'./components/Note',
|
||||
], (
|
||||
array,
|
||||
EventObject,
|
||||
svg,
|
||||
SVGShapes,
|
||||
BaseComponent
|
||||
) => {
|
||||
/* jshint +W072 */
|
||||
'use strict';
|
||||
|
||||
function traverse(stages, callbacks) {
|
||||
|
@ -77,13 +81,23 @@ define([
|
|||
|
||||
let globalNamespace = 0;
|
||||
|
||||
return class Renderer {
|
||||
function parseNamespace(namespace) {
|
||||
if(namespace === null) {
|
||||
namespace = 'R' + globalNamespace;
|
||||
++ globalNamespace;
|
||||
}
|
||||
return namespace;
|
||||
}
|
||||
|
||||
return class Renderer extends EventObject {
|
||||
constructor({
|
||||
themes = [],
|
||||
namespace = null,
|
||||
components = null,
|
||||
SVGTextBlockClass = SVGShapes.TextBlock,
|
||||
} = {}) {
|
||||
super();
|
||||
|
||||
if(components === null) {
|
||||
components = BaseComponent.getComponents();
|
||||
}
|
||||
|
@ -111,11 +125,7 @@ define([
|
|||
this.height = 0;
|
||||
this.themes = makeThemes(themes);
|
||||
this.theme = null;
|
||||
this.namespace = namespace;
|
||||
if(namespace === null) {
|
||||
this.namespace = 'R' + globalNamespace;
|
||||
++ globalNamespace;
|
||||
}
|
||||
this.namespace = parseNamespace(namespace);
|
||||
this.components = components;
|
||||
this.SVGTextBlockClass = SVGTextBlockClass;
|
||||
this.knownDefs = new Set();
|
||||
|
@ -442,6 +452,29 @@ define([
|
|||
};
|
||||
let bottomY = topY;
|
||||
stages.forEach((stage) => {
|
||||
const eventOver = () => {
|
||||
this.trigger('mouseover', [stage]);
|
||||
};
|
||||
|
||||
const eventOut = () => {
|
||||
this.trigger('mouseout');
|
||||
};
|
||||
|
||||
const eventClick = () => {
|
||||
this.trigger('click', [stage]);
|
||||
};
|
||||
|
||||
env.makeRegion = (o) => {
|
||||
if(!o) {
|
||||
o = svg.make('g');
|
||||
}
|
||||
o.addEventListener('mouseenter', eventOver);
|
||||
o.addEventListener('mouseleave', eventOut);
|
||||
o.addEventListener('click', eventClick);
|
||||
this.actionLabels.appendChild(o);
|
||||
return o;
|
||||
};
|
||||
|
||||
const component = this.components.get(stage.type);
|
||||
const baseY = component.render(stage, env);
|
||||
if(baseY !== undefined) {
|
||||
|
|
|
@ -39,16 +39,24 @@ define([
|
|||
|
||||
render(y, {x, label}, env) {
|
||||
const config = env.theme.agentCap.box;
|
||||
const {height} = SVGShapes.renderBoxedText(label, {
|
||||
const clickable = env.makeRegion();
|
||||
const {width, height} = SVGShapes.renderBoxedText(label, {
|
||||
x,
|
||||
y,
|
||||
padding: config.padding,
|
||||
boxAttrs: config.boxAttrs,
|
||||
labelAttrs: config.labelAttrs,
|
||||
boxLayer: env.shapeLayer,
|
||||
labelLayer: env.labelLayer,
|
||||
labelLayer: clickable,
|
||||
SVGTextBlockClass: env.SVGTextBlockClass,
|
||||
});
|
||||
clickable.insertBefore(svg.make('rect', {
|
||||
'x': x - width / 2,
|
||||
'y': y,
|
||||
'width': width,
|
||||
'height': height,
|
||||
'fill': 'transparent',
|
||||
}), clickable.firstChild);
|
||||
|
||||
return {
|
||||
lineTop: 0,
|
||||
|
|
|
@ -47,6 +47,7 @@ define(() => {
|
|||
textSizer,
|
||||
SVGTextBlockClass,
|
||||
addDef,
|
||||
makeRegion,
|
||||
state,
|
||||
}*/) {
|
||||
}
|
||||
|
|
|
@ -135,6 +135,7 @@ define([
|
|||
}
|
||||
|
||||
renderSelfConnect({label, agentNames, options}, env) {
|
||||
/* jshint -W071 */ // TODO: find appropriate abstractions
|
||||
const config = env.theme.connect;
|
||||
const from = env.agentInfos.get(agentNames[0]);
|
||||
|
||||
|
@ -155,6 +156,8 @@ define([
|
|||
(label ? config.label.padding : 0)
|
||||
);
|
||||
|
||||
const clickable = env.makeRegion();
|
||||
|
||||
const renderedText = SVGShapes.renderBoxedText(label, {
|
||||
x: x0 - config.mask.padding.left,
|
||||
y: y0 - height + config.label.margin.top,
|
||||
|
@ -162,7 +165,7 @@ define([
|
|||
boxAttrs: {'fill': '#000000'},
|
||||
labelAttrs: config.label.loopbackAttrs,
|
||||
boxLayer: env.maskLayer,
|
||||
labelLayer: env.labelLayer,
|
||||
labelLayer: clickable,
|
||||
SVGTextBlockClass: env.SVGTextBlockClass,
|
||||
});
|
||||
const labelW = (label ? (
|
||||
|
@ -190,7 +193,17 @@ define([
|
|||
lArrow.render(env.shapeLayer, env.theme, {x: lineX, y: y0, dir: 1});
|
||||
rArrow.render(env.shapeLayer, env.theme, {x: lineX, y: y1, dir: 1});
|
||||
|
||||
return y1 + rArrow.height(env.theme) / 2 + env.theme.actionMargin;
|
||||
const arrowDip = rArrow.height(env.theme) / 2;
|
||||
|
||||
clickable.insertBefore(svg.make('rect', {
|
||||
'x': lineX,
|
||||
'y': y0 - height,
|
||||
'width': x1 + r - lineX,
|
||||
'height': height + r * 2 + arrowDip,
|
||||
'fill': 'transparent',
|
||||
}), clickable.firstChild);
|
||||
|
||||
return y1 + arrowDip + env.theme.actionMargin;
|
||||
}
|
||||
|
||||
renderSimpleConnect({label, agentNames, options}, env) {
|
||||
|
@ -211,7 +224,9 @@ define([
|
|||
|
||||
const x0 = from.x + from.currentMaxRad * dir;
|
||||
const x1 = to.x - to.currentMaxRad * dir;
|
||||
let y = env.primaryY;
|
||||
const y = env.primaryY;
|
||||
|
||||
const clickable = env.makeRegion();
|
||||
|
||||
SVGShapes.renderBoxedText(label, {
|
||||
x: (x0 + x1) / 2,
|
||||
|
@ -220,7 +235,7 @@ define([
|
|||
boxAttrs: {'fill': '#000000'},
|
||||
labelAttrs: config.label.attrs,
|
||||
boxLayer: env.maskLayer,
|
||||
labelLayer: env.labelLayer,
|
||||
labelLayer: clickable,
|
||||
SVGTextBlockClass: env.SVGTextBlockClass,
|
||||
});
|
||||
|
||||
|
@ -235,14 +250,20 @@ define([
|
|||
lArrow.render(env.shapeLayer, env.theme, {x: x0, y, dir});
|
||||
rArrow.render(env.shapeLayer, env.theme, {x: x1, y, dir: -dir});
|
||||
|
||||
return (
|
||||
y +
|
||||
Math.max(
|
||||
lArrow.height(env.theme),
|
||||
rArrow.height(env.theme)
|
||||
) / 2 +
|
||||
env.theme.actionMargin
|
||||
);
|
||||
const arrowDip = Math.max(
|
||||
lArrow.height(env.theme),
|
||||
rArrow.height(env.theme)
|
||||
) / 2;
|
||||
|
||||
clickable.insertBefore(svg.make('rect', {
|
||||
'x': Math.min(x0, x1),
|
||||
'y': y - height,
|
||||
'width': Math.abs(x1 - x0),
|
||||
'height': height + arrowDip,
|
||||
'fill': 'transparent',
|
||||
}), clickable.firstChild);
|
||||
|
||||
return y + arrowDip + env.theme.actionMargin;
|
||||
}
|
||||
|
||||
renderPre({label, agentNames, options}, env) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
define(['./BaseComponent'], (BaseComponent) => {
|
||||
define(['./BaseComponent', 'svg/SVGUtilities'], (BaseComponent, svg) => {
|
||||
'use strict';
|
||||
|
||||
function findExtremes(agentInfos, agentNames) {
|
||||
|
@ -34,8 +34,10 @@ define(['./BaseComponent'], (BaseComponent) => {
|
|||
}, env) {
|
||||
const config = env.theme.note[mode];
|
||||
|
||||
const clickable = env.makeRegion();
|
||||
|
||||
const y = env.topY + config.margin.top + config.padding.top;
|
||||
const labelNode = new env.SVGTextBlockClass(env.labelLayer, {
|
||||
const labelNode = new env.SVGTextBlockClass(clickable, {
|
||||
attrs: config.labelAttrs,
|
||||
text: label,
|
||||
y,
|
||||
|
@ -84,6 +86,14 @@ define(['./BaseComponent'], (BaseComponent) => {
|
|||
height: fullH,
|
||||
}));
|
||||
|
||||
clickable.insertBefore(svg.make('rect', {
|
||||
'x': x0,
|
||||
'y': env.topY + config.margin.top,
|
||||
'width': x1 - x0,
|
||||
'height': fullH,
|
||||
'fill': 'transparent',
|
||||
}), clickable.firstChild);
|
||||
|
||||
return (
|
||||
env.topY +
|
||||
config.margin.top +
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* jshint -W072 */
|
||||
/* jshint -W072 */ // Allow several required modules
|
||||
defineDescribe('Sequence Integration', [
|
||||
'./Parser',
|
||||
'./Generator',
|
||||
|
@ -12,6 +12,7 @@ defineDescribe('Sequence Integration', [
|
|||
BasicTheme,
|
||||
SVGTextBlock
|
||||
) => {
|
||||
/* jshint +W072 */
|
||||
'use strict';
|
||||
|
||||
let parser = null;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
define([
|
||||
'core/ArrayUtilities_spec',
|
||||
'core/EventObject_spec',
|
||||
'svg/SVGUtilities_spec',
|
||||
'svg/SVGTextBlock_spec',
|
||||
'svg/SVGShapes_spec',
|
||||
|
|
|
@ -34,6 +34,10 @@ html, body {
|
|||
background: rgba(255, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.hover {
|
||||
background: #FFFF00;
|
||||
}
|
||||
|
||||
.pick-virtual {
|
||||
color: #777777;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue