Revize a4090428
Přidáno uživatelem Pavel Fidranský před více než 6 roky(ů)
sources/src/main/webapp/css/components/sidebar.css | ||
---|---|---|
194 | 194 |
border: 3px solid red; |
195 | 195 |
} |
196 | 196 |
|
197 |
.sidebar .node--highlighted-required .vertex-name, |
|
198 |
.sidebar .node--highlighted-required .group-name { |
|
197 |
.sidebar .node--highlighted-as-required .vertex-name,
|
|
198 |
.sidebar .node--highlighted-as-required .group-name {
|
|
199 | 199 |
background: red; |
200 | 200 |
} |
201 | 201 |
|
202 |
.sidebar .node--highlighted-provided .vertex-name, |
|
203 |
.sidebar .node--highlighted-provided .group-name { |
|
202 |
.sidebar .node--highlighted-as-provided .vertex-name,
|
|
203 |
.sidebar .node--highlighted-as-provided .group-name {
|
|
204 | 204 |
background: #5896FF; |
205 | 205 |
} |
206 | 206 |
|
207 |
.sidebar .node--highlighted-required.node--highlighted-provided .vertex-name,
|
|
208 |
.sidebar .node--highlighted-required.node--highlighted-provided .group-name {
|
|
207 |
.sidebar .node--highlighted-as-required.node--highlighted-as-provided .vertex-name,
|
|
208 |
.sidebar .node--highlighted-as-required.node--highlighted-as-provided .group-name {
|
|
209 | 209 |
background: linear-gradient(to right, red, #5896FF); |
210 | 210 |
} |
211 | 211 |
|
212 |
.sidebar .node--highlighted-required .provided-counter .outer-floater, |
|
213 |
.sidebar .node--highlighted-required-neighbours .required-counter .outer-floater, |
|
214 |
.sidebar .node--highlighted-provided .required-counter .outer-floater, |
|
215 |
.sidebar .node--highlighted-provided-neighbours .provided-counter .outer-floater, |
|
216 |
.sidebar .node--highlighted-required .provided-counter .outer-port, |
|
217 |
.sidebar .node--highlighted-required-neighbours .required-counter .outer-port, |
|
218 |
.sidebar .node--highlighted-provided .required-counter .outer-port, |
|
219 |
.sidebar .node--highlighted-provided-neighbours .provided-counter .outer-port { |
|
212 |
.sidebar .node--highlighted-as-required .provided-counter .outer-floater,
|
|
213 |
.sidebar .node--highlighted-as-required-neighbours .required-counter .outer-floater,
|
|
214 |
.sidebar .node--highlighted-as-provided .required-counter .outer-floater,
|
|
215 |
.sidebar .node--highlighted-as-provided-neighbours .provided-counter .outer-floater,
|
|
216 |
.sidebar .node--highlighted-as-required .provided-counter .outer-port,
|
|
217 |
.sidebar .node--highlighted-as-required-neighbours .required-counter .outer-port,
|
|
218 |
.sidebar .node--highlighted-as-provided .required-counter .outer-port,
|
|
219 |
.sidebar .node--highlighted-as-provided-neighbours .provided-counter .outer-port {
|
|
220 | 220 |
display: block; |
221 | 221 |
} |
222 | 222 |
|
sources/src/main/webapp/css/components/viewport.css | ||
---|---|---|
20 | 20 |
stroke: red; |
21 | 21 |
} |
22 | 22 |
|
23 |
.viewport .edge--highlighted-required { |
|
23 |
.viewport .edge--highlighted-as-required {
|
|
24 | 24 |
stroke: red; |
25 | 25 |
stroke-width: 2; |
26 | 26 |
} |
27 | 27 |
|
28 |
.viewport .edge--highlighted-provided { |
|
28 |
.viewport .edge--highlighted-as-provided {
|
|
29 | 29 |
stroke: #5896FF; |
30 | 30 |
stroke-width: 2; |
31 | 31 |
} |
... | ... | |
60 | 60 |
stroke-width: 3; |
61 | 61 |
} |
62 | 62 |
|
63 |
.viewport .node--highlighted-required > rect { |
|
63 |
.viewport .node--highlighted-as-required > rect {
|
|
64 | 64 |
fill: red; |
65 | 65 |
} |
66 | 66 |
|
67 |
.viewport .node--highlighted-provided > rect { |
|
67 |
.viewport .node--highlighted-as-provided > rect {
|
|
68 | 68 |
fill: #5896FF; |
69 | 69 |
} |
70 | 70 |
|
71 |
.viewport .node--highlighted-archetype > rect { |
|
71 |
.viewport .node--highlighted-as-archetype > rect {
|
|
72 | 72 |
fill: yellowgreen; |
73 | 73 |
} |
74 | 74 |
|
75 |
.viewport .node--highlighted-required.node--highlighted-provided > rect {
|
|
76 |
fill: url(#node--highlighted-required-provided); |
|
75 |
.viewport .node--highlighted-as-required.node--highlighted-as-provided > rect {
|
|
76 |
fill: url(#node--highlighted-as-required-provided);
|
|
77 | 77 |
} |
78 | 78 |
|
79 | 79 |
.viewport .node--dragged > rect { |
sources/src/main/webapp/js/components/edge.js | ||
---|---|---|
1 | 1 |
/** |
2 | 2 |
* Class representing an edge of a graph in viewport. |
3 |
* @constructor |
|
4 |
* @param {object} props Properties of the edge. |
|
5 | 3 |
*/ |
6 |
function Edge(props) { |
|
7 |
/** @prop {integer} id Unique identifier of the edge. */ |
|
8 |
this.id = props.id; |
|
4 |
class Edge { |
|
5 |
/** |
|
6 |
* @constructor |
|
7 |
* @param {object} props Properties of the edge. |
|
8 |
*/ |
|
9 |
constructor(props) { |
|
10 |
/** @prop {integer} id Unique identifier of the edge. */ |
|
11 |
this.id = props.id; |
|
12 |
/** @prop {array} subedgeInfo */ |
|
13 |
this.subedgeInfo = props.subedgeInfo; |
|
9 | 14 |
|
10 |
var rootElement; |
|
15 |
this._isHidden = false; |
|
16 |
this._isDimmed = false; |
|
17 |
this._isHighlighted = false; |
|
18 |
this._isHighlightedAsRequired = false; |
|
19 |
this._isHighlightedAsProvided = false; |
|
11 | 20 |
|
12 |
var hidden = false; |
|
13 |
var dimmed = false; |
|
14 |
var highlighted = false; |
|
15 |
var highlightedRequired = false; |
|
16 |
var highlightedProvided = false; |
|
21 |
this._fromNode = null; |
|
22 |
this._toNode = null; |
|
17 | 23 |
|
18 |
var fromNode = null; |
|
19 |
var toNode = null; |
|
24 |
this._start = null; |
|
25 |
this._end = null; |
|
26 |
} |
|
20 | 27 |
|
21 |
var start = new Coordinates(0, 0); |
|
22 |
var end = new Coordinates(0, 0); |
|
23 |
|
|
24 | 28 |
/** |
25 | 29 |
* Sets origin vertex of the edge without moving the starting point. |
26 | 30 |
* @param {Vertex} node Vertex that this edge is going from. |
27 | 31 |
*/ |
28 |
this.setFrom = function(node) {
|
|
32 |
set from(node) {
|
|
29 | 33 |
if (!(node instanceof Vertex)) { |
30 |
throw new TypeError(node.toString() + 'is not instance of Vertex');
|
|
34 |
throw new TypeError(node.toString() + ' is not an instance of Vertex');
|
|
31 | 35 |
} |
32 | 36 |
|
33 |
fromNode = node; |
|
37 |
this._fromNode = node; |
|
38 |
this._start = node.center; |
|
39 |
} |
|
34 | 40 |
|
35 |
setStart(node.getCenter()); |
|
36 |
}; |
|
37 |
|
|
38 | 41 |
/** |
39 |
* Returns origin vertex of the edge.
|
|
42 |
* @returns {Vertex} origin vertex of the edge.
|
|
40 | 43 |
*/ |
41 |
this.getFrom = function() {
|
|
42 |
return fromNode; |
|
43 |
};
|
|
44 |
get from() {
|
|
45 |
return this._fromNode;
|
|
46 |
} |
|
44 | 47 |
|
45 | 48 |
/** |
46 | 49 |
* Sets target vertex of the edge without moving the ending point. |
47 | 50 |
* @param {Vertex} node Vertex that this edge is going to. |
48 | 51 |
*/ |
49 |
this.setTo = function(node) {
|
|
52 |
set to(node) {
|
|
50 | 53 |
if (!(node instanceof Vertex)) { |
51 |
throw new TypeError(node.toString() + 'is not instance of Vertex');
|
|
54 |
throw new TypeError(node.toString() + ' is not an instance of Vertex');
|
|
52 | 55 |
} |
53 | 56 |
|
54 |
toNode = node; |
|
55 |
|
|
56 |
setEnd(node.getCenter()); |
|
57 |
}; |
|
57 |
this._toNode = node; |
|
58 |
this._end = node.center; |
|
59 |
} |
|
58 | 60 |
|
59 | 61 |
/** |
60 |
* Returns target vertex of the edge.
|
|
62 |
* @returns {Vertex} target vertex of the edge
|
|
61 | 63 |
*/ |
62 |
this.getTo = function() {
|
|
63 |
return toNode; |
|
64 |
};
|
|
64 |
get to() {
|
|
65 |
return this._toNode;
|
|
66 |
} |
|
65 | 67 |
|
66 | 68 |
/** |
67 | 69 |
* Moves starting point of the edge to new coordinates and rotates the lollipop. |
68 | 70 |
* @param {Coordinates} coords New starting coordinates of the edge. |
69 | 71 |
*/ |
70 |
this.moveStart = function(coords) { |
|
71 |
setStart(coords); |
|
72 |
|
|
73 |
var lines = rootElement.querySelectorAll('.line'); |
|
74 |
lines.forEach(function(line) { |
|
75 |
line.setAttribute('x1', start.x); |
|
76 |
line.setAttribute('y1', start.y); |
|
72 |
set start(coords) { |
|
73 |
if (!(coords instanceof Coordinates)) { |
|
74 |
throw new TypeError(coords.toString() + ' is not an instance of Coordinates'); |
|
75 |
} |
|
76 |
|
|
77 |
this._start = coords; |
|
78 |
|
|
79 |
this._lineElements.forEach(line => { |
|
80 |
line.setAttribute('x1', this._start.x); |
|
81 |
line.setAttribute('y1', this._start.y); |
|
77 | 82 |
}); |
78 |
|
|
79 |
// arrow position and rotation |
|
80 |
var position = getArrowPosition.call(this); |
|
81 |
var rotation = getArrowRotation.call(this); |
|
82 | 83 |
|
83 |
var arrow = rootElement.querySelector('.arrow'); |
|
84 |
arrow.setAttribute('transform', `rotate(${rotation}, ${position.x},${position.y}) translate(${position.x},${position.y})`); |
|
85 |
}; |
|
86 |
|
|
84 |
// icon position and rotation |
|
85 |
let position = this._iconPosition; |
|
86 |
let rotation = this._iconRotation; |
|
87 |
|
|
88 |
this._iconElement.setAttribute('transform', `rotate(${rotation}, ${position.x},${position.y}) translate(${position.x},${position.y})`); |
|
89 |
} |
|
90 |
|
|
87 | 91 |
/** |
88 | 92 |
* Moved ending point of the edge to new coordinates and rotates the arrow. |
89 | 93 |
* @param {Coordinates} coords New ending coordinates of the edge. |
90 | 94 |
*/ |
91 |
this.moveEnd = function(coords) { |
|
92 |
setEnd(coords); |
|
93 |
|
|
94 |
var lines = rootElement.querySelectorAll('.line'); |
|
95 |
lines.forEach(function(line) { |
|
96 |
line.setAttribute('x2', end.x); |
|
97 |
line.setAttribute('y2', end.y); |
|
95 |
set end(coords) { |
|
96 |
if (!(coords instanceof Coordinates)) { |
|
97 |
throw new TypeError(coords.toString() + ' is not an instance of Coordinates'); |
|
98 |
} |
|
99 |
|
|
100 |
this._end = coords; |
|
101 |
|
|
102 |
this._lineElements.forEach(line => { |
|
103 |
line.setAttribute('x2', this._end.x); |
|
104 |
line.setAttribute('y2', this._end.y); |
|
98 | 105 |
}); |
99 | 106 |
|
100 |
// arrow position and rotation
|
|
101 |
var position = getArrowPosition.call(this);
|
|
102 |
var rotation = getArrowRotation.call(this);
|
|
107 |
// icon position and rotation
|
|
108 |
let position = this._iconPosition;
|
|
109 |
let rotation = this._iconRotation;
|
|
103 | 110 |
|
104 |
var arrow = rootElement.querySelector('.arrow'); |
|
105 |
arrow.setAttribute('transform', `rotate(${rotation}, ${position.x},${position.y}) translate(${position.x},${position.y})`); |
|
106 |
}; |
|
111 |
this._iconElement.setAttribute('transform', `rotate(${rotation}, ${position.x},${position.y}) translate(${position.x},${position.y})`); |
|
112 |
} |
|
107 | 113 |
|
108 | 114 |
/** |
109 | 115 |
* Toggles visibility of the edge. |
110 | 116 |
* @param {boolean} newValue True to hide the edge, false to display it. |
111 | 117 |
*/ |
112 |
this.setHidden = function(newValue) {
|
|
113 |
hidden = newValue;
|
|
118 |
set isHidden(newValue) {
|
|
119 |
this._isHidden = newValue;
|
|
114 | 120 |
|
115 | 121 |
if (newValue) { |
116 |
rootElement.classList.add('hidden'); |
|
122 |
this._rootElement.classList.add('hidden');
|
|
117 | 123 |
} else { |
118 |
rootElement.classList.remove('hidden'); |
|
124 |
this._rootElement.classList.remove('hidden');
|
|
119 | 125 |
} |
120 |
};
|
|
126 |
} |
|
121 | 127 |
|
122 | 128 |
/** |
123 | 129 |
* Toggles transparency of the edge. |
124 | 130 |
* @param {boolean} newValue True to set the edge semitransparent, false to display it normally. |
125 | 131 |
*/ |
126 |
this.setDimmed = function(newValue) {
|
|
127 |
dimmed = newValue;
|
|
132 |
set isDimmed(newValue) {
|
|
133 |
this._isDimmed = newValue;
|
|
128 | 134 |
|
129 | 135 |
if (newValue) { |
130 |
rootElement.classList.add('edge--dimmed'); |
|
136 |
this._rootElement.classList.add('edge--dimmed');
|
|
131 | 137 |
} else { |
132 |
rootElement.classList.remove('edge--dimmed'); |
|
138 |
this._rootElement.classList.remove('edge--dimmed');
|
|
133 | 139 |
} |
134 |
};
|
|
140 |
} |
|
135 | 141 |
|
136 |
/**
|
|
137 |
* @returns true if the edge is currently highlighted (in any way), otherwise false
|
|
138 |
*/
|
|
139 |
this.isHighlighted = function () {
|
|
140 |
return highlighted;
|
|
141 |
};
|
|
142 |
/**
|
|
143 |
* @returns true if the edge is currently highlighted (in any way), otherwise false
|
|
144 |
*/
|
|
145 |
get isHighlighted() {
|
|
146 |
return this._isHighlighted;
|
|
147 |
}
|
|
142 | 148 |
|
143 | 149 |
/** |
144 | 150 |
* Toggles highlighting of the edge. |
145 | 151 |
* @param {boolean} newValue True to highlight the edge, false to unhighlight. |
146 | 152 |
*/ |
147 |
this.setHighlighted = function(newValue) {
|
|
148 |
highlighted = newValue;
|
|
153 |
set isHighlighted(newValue) {
|
|
154 |
this._isHighlighted = newValue;
|
|
149 | 155 |
|
150 | 156 |
if (newValue) { |
151 |
rootElement.classList.add('edge--highlighted'); |
|
157 |
this._rootElement.classList.add('edge--highlighted');
|
|
152 | 158 |
} else { |
153 |
rootElement.classList.remove('edge--highlighted'); |
|
159 |
this._rootElement.classList.remove('edge--highlighted');
|
|
154 | 160 |
} |
155 |
};
|
|
161 |
} |
|
156 | 162 |
|
157 | 163 |
/** |
158 | 164 |
* Toggles highlighting of the edge between vertex and its requirement. |
159 | 165 |
* @param {boolean} newValue True to highlight the edge as required, false to unhighlight. |
160 | 166 |
*/ |
161 |
this.setHighlightedRequired = function(newValue) {
|
|
162 |
highlightedRequired = newValue;
|
|
167 |
set isHighlightedAsRequired(newValue) {
|
|
168 |
this._isHighlightedAsRequired = newValue;
|
|
163 | 169 |
|
164 | 170 |
if (newValue) { |
165 |
rootElement.classList.add('edge--highlighted-required');
|
|
166 |
rootElement.classList.remove('edge--highlighted-provided');
|
|
171 |
this._rootElement.classList.add('edge--highlighted-as-required');
|
|
172 |
this._rootElement.classList.remove('edge--highlighted-as-provided');
|
|
167 | 173 |
|
168 | 174 |
} else { |
169 |
rootElement.classList.remove('edge--highlighted-required');
|
|
175 |
this._rootElement.classList.remove('edge--highlighted-as-required');
|
|
170 | 176 |
} |
171 |
};
|
|
177 |
} |
|
172 | 178 |
|
173 | 179 |
/** |
174 | 180 |
* Toggles highlighting of the edge between vertex and its dependent. |
175 | 181 |
* @param {boolean} newValue True to highlight the edge as provided, false to unhighlight. |
176 | 182 |
*/ |
177 |
this.setHighlightedProvided = function(newValue) {
|
|
178 |
highlightedProvided = newValue;
|
|
183 |
set isHighlightedAsProvided(newValue) {
|
|
184 |
this._isHighlightedAsProvided = newValue;
|
|
179 | 185 |
|
180 | 186 |
if (newValue) { |
181 |
rootElement.classList.remove('edge--highlighted-required');
|
|
182 |
rootElement.classList.add('edge--highlighted-provided');
|
|
187 |
this._rootElement.classList.remove('edge--highlighted-as-required');
|
|
188 |
this._rootElement.classList.add('edge--highlighted-as-provided');
|
|
183 | 189 |
|
184 | 190 |
} else { |
185 |
rootElement.classList.remove('edge--highlighted-provided');
|
|
191 |
this._rootElement.classList.remove('edge--highlighted-as-provided');
|
|
186 | 192 |
} |
187 |
};
|
|
188 |
|
|
193 |
} |
|
194 |
|
|
189 | 195 |
/** |
190 | 196 |
* Creates a new DOM element representing the edge in memory. Binds user interactions to local handler functions. |
191 | 197 |
* @returns {Element} SVG DOM element. |
192 | 198 |
*/ |
193 |
this.render = function() { |
|
194 |
rootElement = DOM.createSvgElement('g', { |
|
195 |
'class': 'edge', |
|
196 |
'data-id': props.id, |
|
197 |
'data-from': props.from, |
|
198 |
'data-to': props.to, |
|
199 |
}); |
|
199 |
render() { |
|
200 |
// icon |
|
201 |
let position = this._iconPosition; |
|
202 |
let rotation = this._iconRotation; |
|
200 | 203 |
|
201 |
rootElement.appendChild(DOM.createSvgElement('line', { |
|
202 |
'class': 'line', |
|
203 |
'x1': start.x, |
|
204 |
'y1': start.y, |
|
205 |
'x2': end.x, |
|
206 |
'y2': end.y, |
|
207 |
'stroke': 'white', |
|
208 |
'stroke-width': 5, |
|
209 |
})); |
|
210 |
|
|
211 |
rootElement.appendChild(DOM.createSvgElement('line', { |
|
212 |
'class': 'line', |
|
213 |
'x1': start.x, |
|
214 |
'y1': start.y, |
|
215 |
'x2': end.x, |
|
216 |
'y2': end.y, |
|
217 |
})); |
|
218 |
|
|
219 |
// arrow position and rotation |
|
220 |
var position = getArrowPosition.call(this); |
|
221 |
var rotation = getArrowRotation.call(this); |
|
222 |
|
|
223 |
// arrow |
|
224 |
var arrow = DOM.createSvgElement('g', { |
|
225 |
'class': 'arrow', |
|
226 |
'data-edgeId': this.id, |
|
227 |
'transform': `rotate(${rotation}, ${position.x},${position.y}) translate(${position.x},${position.y})`, |
|
228 |
}); |
|
229 |
arrow.appendChild(DOM.createSvgElement('polygon', { |
|
230 |
'points': '0,-10 30,0 0,10', |
|
204 |
this._iconElement = DOM.s('g', { |
|
205 |
class: 'arrow', |
|
206 |
transform: `rotate(${rotation}, ${position.x},${position.y}) translate(${position.x},${position.y})`, |
|
207 |
onClick: this._onEdgeClick.bind(this), |
|
208 |
}, [ |
|
209 |
DOM.s('polygon', { |
|
210 |
points: '0,-10 30,0 0,10', |
|
211 |
}), |
|
212 |
]); |
|
213 |
|
|
214 |
// lines |
|
215 |
this._lineElements = [ |
|
216 |
DOM.s('line', { |
|
217 |
class: 'line', |
|
218 |
x1: this._start.x, |
|
219 |
y1: this._start.y, |
|
220 |
x2: this._end.x, |
|
221 |
y2: this._end.y, |
|
222 |
stroke: 'white', |
|
223 |
'stroke-width': 5, |
|
224 |
}), |
|
225 |
DOM.s('line', { |
|
226 |
class: 'line', |
|
227 |
x1: this._start.x, |
|
228 |
y1: this._start.y, |
|
229 |
x2: this._end.x, |
|
230 |
y2: this._end.y, |
|
231 |
}), |
|
232 |
]; |
|
231 | 233 |
|
232 |
})); |
|
233 |
arrow.addEventListener('click', click.bind(this)); |
|
234 |
rootElement.appendChild(arrow); |
|
234 |
// root |
|
235 |
this._rootElement = DOM.s('g', { |
|
236 |
class: 'edge', |
|
237 |
'data-id': this.id, |
|
238 |
'data-from': this.from.name, |
|
239 |
'data-to': this.to.name, |
|
240 |
}, [].concat(this._lineElements, this._iconElement)); |
|
235 | 241 |
|
236 |
return rootElement; |
|
237 |
};
|
|
242 |
return this._rootElement;
|
|
243 |
} |
|
238 | 244 |
|
239 | 245 |
/** |
240 | 246 |
* Removes the DOM element representing the edge from document. |
241 | 247 |
*/ |
242 |
this.remove = function() {
|
|
243 |
rootElement.remove(); |
|
244 |
};
|
|
248 |
remove() {
|
|
249 |
this._rootElement.remove();
|
|
250 |
} |
|
245 | 251 |
|
246 | 252 |
/** |
247 | 253 |
* Exports the edge to a new, plain JS object. |
248 | 254 |
* @returns {Object} exported edge |
249 | 255 |
*/ |
250 |
this.export = function() {
|
|
256 |
export() {
|
|
251 | 257 |
return { |
252 |
subedgeInfo: props.subedgeInfo,
|
|
253 |
from: this.getFrom().id,
|
|
254 |
to: this.getTo().id,
|
|
258 |
subedgeInfo: this.subedgeInfo,
|
|
259 |
from: this.from.id,
|
|
260 |
to: this.to.id,
|
|
255 | 261 |
text: '', |
256 | 262 |
id: this.id, |
257 | 263 |
}; |
258 |
};
|
|
264 |
} |
|
259 | 265 |
|
260 | 266 |
/** |
261 | 267 |
* Edge click interaction. Highlights the edge and vertices related to it. Reveals edge popover. |
262 | 268 |
* @param {Event} e Click event. |
263 | 269 |
*/ |
264 |
function click(e) {
|
|
265 |
app.viewportComponent.edgePopoverComponent.body = props.subedgeInfo;
|
|
270 |
_onEdgeClick(e) {
|
|
271 |
app.viewportComponent.edgePopoverComponent.body = this.subedgeInfo;
|
|
266 | 272 |
app.viewportComponent.edgePopoverComponent.position = new Coordinates(e.clientX, e.clientY); |
267 | 273 |
app.viewportComponent.edgePopoverComponent.open(); |
268 | 274 |
|
269 | 275 |
// unhighlight other edges |
270 |
app.edgeList.filter(function(edge) {
|
|
276 |
app.edgeList.filter(edge => {
|
|
271 | 277 |
return edge !== this; |
272 |
}, this).forEach(function(edge) {
|
|
273 |
edge.setHighlighted(false);
|
|
274 |
edge.getFrom().setHighlighted(false);
|
|
275 |
edge.getTo().setHighlighted(false);
|
|
278 |
}).forEach(edge => {
|
|
279 |
edge.from.isHighlighted = false;
|
|
280 |
edge.to.isHighlighted = false;
|
|
281 |
edge.isHighlighted = false;
|
|
276 | 282 |
}); |
277 | 283 |
|
278 | 284 |
// highlight this edge |
279 |
this.setHighlighted(!highlighted);
|
|
280 |
this.getFrom().setHighlighted(highlighted);
|
|
281 |
this.getTo().setHighlighted(highlighted);
|
|
285 |
this.from.isHighlighted = !this.isHighlighted;
|
|
286 |
this.to.isHighlighted = !this.isHighlighted;
|
|
287 |
this.isHighlighted = !this.isHighlighted;
|
|
282 | 288 |
} |
283 |
|
|
284 |
/** |
|
285 |
* Sets new coordinates of the starting point of the edge. |
|
286 |
* @param {Coordinates} coords New starting coordinates of the edge. |
|
287 |
*/ |
|
288 |
function setStart(coords) { |
|
289 |
if (!(coords instanceof Coordinates)) { |
|
290 |
throw new TypeError(coords.toString() + 'is not instance of Coordinates'); |
|
291 |
} |
|
292 | 289 |
|
293 |
start = coords; |
|
294 |
} |
|
295 |
|
|
296 | 290 |
/** |
297 |
* Sets new coordinates of the ending point of the edge. |
|
298 |
* @param {Coordinates} coords New ending coordinates of the edge. |
|
291 |
* @returns {Coordinates} Current position of the icon. |
|
299 | 292 |
*/ |
300 |
function setEnd(coords) { |
|
301 |
if (!(coords instanceof Coordinates)) { |
|
302 |
throw new TypeError(coords.toString() + 'is not instance of Coordinates'); |
|
303 |
} |
|
304 |
|
|
305 |
end = coords; |
|
306 |
} |
|
307 |
|
|
308 |
/** |
|
309 |
* @returns {Coordinates} Current position of the arrow. |
|
310 |
*/ |
|
311 |
function getArrowPosition() { |
|
312 |
// arrow is placed at 2/3 of the distance from start to end |
|
293 |
get _iconPosition() { |
|
294 |
// icon is placed at 2/3 of the distance from start to end |
|
313 | 295 |
return new Coordinates( |
314 |
(start.x + 2 * end.x) / 3,
|
|
315 |
(start.y + 2 * end.y) / 3,
|
|
296 |
(this._start.x + 2 * this._end.x) / 3,
|
|
297 |
(this._start.y + 2 * this._end.y) / 3,
|
|
316 | 298 |
); |
317 | 299 |
} |
318 |
|
|
300 |
|
|
319 | 301 |
/** |
320 |
* @returns {float} Current rotation of the arrow in degrees.
|
|
302 |
* @returns {float} Current rotation of the icon in degrees.
|
|
321 | 303 |
*/ |
322 |
function getArrowRotation() {
|
|
323 |
return -1 * Math.atan2(end.x - start.x, end.y - start.y) * 180 / Math.PI + 90;
|
|
304 |
get _iconRotation() {
|
|
305 |
return -1 * Math.atan2(this._end.x - this._start.x, this._end.y - this._start.y) * 180 / Math.PI + 90;
|
|
324 | 306 |
} |
325 | 307 |
} |
sources/src/main/webapp/js/components/floatingPoint.js | ||
---|---|---|
39 | 39 |
throw new TypeError(edge.toString() + 'is not instance of Edge'); |
40 | 40 |
} |
41 | 41 |
|
42 |
edge.moveEnd(this.getPosition());
|
|
42 |
edge.end = this.getPosition();
|
|
43 | 43 |
|
44 | 44 |
inEdgeList.push(edge); |
45 | 45 |
}; |
... | ... | |
53 | 53 |
throw new TypeError(edge.toString() + 'is not instance of Edge'); |
54 | 54 |
} |
55 | 55 |
|
56 |
edge.moveStart(this.getPosition());
|
|
56 |
edge.start = this.getPosition();
|
|
57 | 57 |
|
58 | 58 |
outEdgeList.push(edge); |
59 | 59 |
}; |
... | ... | |
97 | 97 |
var edgeOffsetY = 0; |
98 | 98 |
} |
99 | 99 |
|
100 |
var archetypeList = Object.keys(node.getRelatedArchetypeMap()).map(function(archetypeIndex) {
|
|
100 |
var archetypeList = Object.keys(node.relatedArchetypeMap).map(function(archetypeIndex) {
|
|
101 | 101 |
return parseInt(archetypeIndex); |
102 | 102 |
}); |
103 | 103 |
var archetypeIconOrder; |
104 | 104 |
|
105 | 105 |
// redraw dependent edges |
106 | 106 |
inEdgeList.forEach(function(edge) { |
107 |
archetypeIconOrder = archetypeList.indexOf(edge.getFrom().archetype);
|
|
107 |
archetypeIconOrder = archetypeList.indexOf(edge.from.archetype);
|
|
108 | 108 |
|
109 |
edge.moveEnd(new Coordinates(
|
|
109 |
edge.end = new Coordinates(
|
|
110 | 110 |
position.x / app.zoom.scale, |
111 | 111 |
(position.y + edgeOffsetY + archetypeIconOrder * 20) / app.zoom.scale, |
112 |
)); |
|
113 | 112 |
}, this); |
113 |
); |
|
114 | 114 |
|
115 | 115 |
outEdgeList.forEach(function(edge) { |
116 |
archetypeIconOrder = archetypeList.indexOf(edge.getTo().archetype);
|
|
116 |
archetypeIconOrder = archetypeList.indexOf(edge.to.archetype);
|
|
117 | 117 |
|
118 |
edge.moveStart(new Coordinates(
|
|
118 |
edge.start = new Coordinates(
|
|
119 | 119 |
position.x / app.zoom.scale, |
120 | 120 |
(position.y + edgeOffsetY + archetypeIconOrder * 20) / app.zoom.scale, |
121 |
)); |
|
122 | 121 |
}, this); |
122 |
); |
|
123 | 123 |
}; |
124 | 124 |
|
125 | 125 |
/** |
... | ... | |
152 | 152 |
* @param {boolean} newValue True to highlight the node as required, otherwise false. |
153 | 153 |
*/ |
154 | 154 |
this.setHighlightedRequired = function(newValue) { |
155 |
node.setHighlightedRequired(newValue);
|
|
155 |
node.isHighlightedAsRequired = newValue;
|
|
156 | 156 |
}; |
157 | 157 |
|
158 | 158 |
/** |
... | ... | |
160 | 160 |
* @param {boolean} newValue True to highlight the node as provided, otherwise false. |
161 | 161 |
*/ |
162 | 162 |
this.setHighlightedProvided = function(newValue) { |
163 |
node.setHighlightedProvided(newValue);
|
|
163 |
node.isHighlightedAsProvided = newValue;
|
|
164 | 164 |
}; |
165 | 165 |
} |
sources/src/main/webapp/js/components/group.js | ||
---|---|---|
1 | 1 |
/** |
2 | 2 |
* Class representing a group of vertices in graph. A group is composed from one or more vertices. |
3 |
* @constructor |
|
4 |
* @param {object} props Properties of the group. |
|
5 | 3 |
*/ |
6 |
function Group(props) { |
|
7 |
/** @prop {integer} id Unique identifier of the group. */ |
|
8 |
this.id = props.hasOwnProperty('id') ? props.id : app.groupList.length + 1; |
|
9 |
/** @prop {string} name Name of the group. */ |
|
10 |
this.name = props.hasOwnProperty('name') ? props.name : 'Group ' + this.id; |
|
11 |
/** @prop {array} symbol Symbol of the group. */ |
|
12 |
this.symbol = app.markSymbol.getMarkSymbol(); |
|
13 |
|
|
14 |
var rootElement; |
|
15 |
var vertexListComponent; |
|
16 |
|
|
17 |
var position = new Coordinates(0, 0); |
|
18 |
var size = { |
|
19 |
width: 70, |
|
20 |
height: 70, |
|
21 |
}; |
|
22 |
var floater = null; |
|
23 |
|
|
24 |
var pan = false; |
|
25 |
var excluded = false; |
|
26 |
var iconsDisplayed = false; |
|
27 |
|
|
28 |
var highlighted = false; |
|
29 |
var highlightedRequiredNeighbours = false; |
|
30 |
var highlightedProvidedNeighbours = false; |
|
31 |
var highlightedArchetypeNeighbours = false; |
|
32 |
var found = false; |
|
33 |
var dimmed = false; |
|
34 |
|
|
35 |
var vertexList = []; |
|
36 |
var relatedArchetypeMap = {}; |
|
4 |
class Group extends Node { |
|
5 |
/** |
|
6 |
* @constructor |
|
7 |
* @param {object} props Properties of the group. |
|
8 |
*/ |
|
9 |
constructor(props) { |
|
10 |
super(props); |
|
11 |
|
|
12 |
this._size = new Dimensions(70, 70); |
|
13 |
|
|
14 |
this._vertexList = []; |
|
15 |
} |
|
16 |
|
|
17 |
/** |
|
18 |
* @returns {Group} newly created group |
|
19 |
*/ |
|
20 |
static create() { |
|
21 |
return new Group({ |
|
22 |
id: app.groupList.length + 1, |
|
23 |
name: 'Group ' + (app.groupList.length + 1), |
|
24 |
}); |
|
25 |
} |
|
37 | 26 |
|
38 | 27 |
/** |
39 | 28 |
* Adds a new vertex to the group. The vertex is set as excluded and its DOM element is removed from document. Its edges are moved so that they end at the group. |
40 | 29 |
* @param {Vertex} vertex Vertex to be added to the group. |
41 | 30 |
*/ |
42 |
this.addVertex = function(vertex) {
|
|
31 |
addVertex(vertex) {
|
|
43 | 32 |
if (!(vertex instanceof Vertex)) { |
44 |
throw new TypeError(vertex.toString() + 'is not instance of Vertex');
|
|
33 |
throw new TypeError(vertex.toString() + ' is not an instance of Vertex');
|
|
45 | 34 |
} |
46 | 35 |
|
47 |
if (vertexList.length === 0) { |
|
48 |
position = vertex.getPosition();
|
|
36 |
if (this._vertexList.length === 0) {
|
|
37 |
this.position = vertex.position;
|
|
49 | 38 |
} |
50 | 39 |
|
51 |
var isIconShown = this.isIconsDisplayed(); |
|
52 |
if(isIconShown) showIconClick.bind(this)(null); |
|
53 |
|
|
54 |
vertex.removeIcon(); |
|
55 |
vertex.setGroup(this); |
|
56 |
vertex.setExcluded(this.isExcluded()); |
|
57 |
vertex.remove(this.isExcluded()); // edges of the vertex should be removed from DOM only if group is excluded |
|
40 |
vertex.group = this; |
|
41 |
vertex.isExcluded = this.isExcluded; |
|
42 |
vertex.remove(this.isExcluded); // edges of the vertex should be removed from DOM only if group is excluded |
|
58 | 43 |
|
59 |
vertex.getInEdgeList().forEach(function(edge) { |
|
60 |
if (this.isExcluded()) { |
|
44 |
let inEdgeList = vertex.inEdgeList; |
|
45 |
inEdgeList.forEach(edge => { |
|
46 |
if (this.isExcluded) { |
|
61 | 47 |
floater.addInEdge(edge); |
62 | 48 |
} else { |
63 |
edge.moveEnd(this.getCenter());
|
|
49 |
edge.end = this.center;
|
|
64 | 50 |
} |
65 |
}, this);
|
|
51 |
}); |
|
66 | 52 |
|
67 |
vertex.getOutEdgeList().forEach(function(edge) { |
|
68 |
if (this.isExcluded()) { |
|
53 |
let outEdgeList = vertex.outEdgeList; |
|
54 |
outEdgeList.forEach(edge => { |
|
55 |
if (this.isExcluded) { |
|
69 | 56 |
floater.addOutEdge(edge); |
70 | 57 |
} else { |
71 |
edge.moveStart(this.getCenter());
|
|
58 |
edge.start = this.center;
|
|
72 | 59 |
} |
73 |
}, this);
|
|
60 |
}); |
|
74 | 61 |
|
75 |
var vertexRelatedArchetypeMap = vertex.getRelatedArchetypeMap();
|
|
76 |
for (var archetypeIndex in vertexRelatedArchetypeMap) {
|
|
77 |
if (!relatedArchetypeMap.hasOwnProperty(archetypeIndex)) {
|
|
78 |
relatedArchetypeMap[archetypeIndex] = 0; |
|
62 |
let vertexRelatedArchetypeMap = vertex.relatedArchetypeMap;
|
|
63 |
for (let archetypeIndex in vertexRelatedArchetypeMap) {
|
|
64 |
if (this.relatedArchetypeMap.hasOwnProperty(archetypeIndex) === false) {
|
|
65 |
this.relatedArchetypeMap[archetypeIndex] = 0;
|
|
79 | 66 |
} |
80 |
|
|
81 |
relatedArchetypeMap[archetypeIndex] += vertexRelatedArchetypeMap[archetypeIndex]; |
|
67 |
|
|
68 |
this.relatedArchetypeMap[archetypeIndex] += vertexRelatedArchetypeMap[archetypeIndex];
|
|
82 | 69 |
} |
83 | 70 |
|
84 |
app.viewportComponent.removeVertex(vertex);
|
|
71 |
app.viewportComponent.removeNode(vertex);
|
|
85 | 72 |
|
86 |
if (vertexListComponent) { |
|
87 |
vertexListComponent.appendChild(vertex); |
|
73 |
if (this._vertexListComponent) {
|
|
74 |
this._vertexListComponent.appendChild(vertex);
|
|
88 | 75 |
} |
89 | 76 |
|
90 |
vertexList.push(vertex); |
|
91 |
if(isIconShown) showIconClick.bind(this)(null); |
|
92 |
}; |
|
77 |
this._vertexList.push(vertex); |
|
78 |
} |
|
93 | 79 |
|
94 | 80 |
/** |
95 | 81 |
* Removes a vertex from the group. The vertex is returned back to the viewport and its edges are moved to it. |
96 | 82 |
* @param {Vertex} vertex Vertex to be removed from the group. |
97 | 83 |
*/ |
98 |
this.removeVertex = function(vertex) {
|
|
84 |
removeVertex(vertex) {
|
|
99 | 85 |
if (!(vertex instanceof Vertex)) { |
100 |
throw new TypeError(vertex.toString() + 'is not instance of Vertex');
|
|
86 |
throw new TypeError(vertex.toString() + ' is not an instance of Vertex');
|
|
101 | 87 |
} |
102 | 88 |
|
103 |
vertex.setGroup(null);
|
|
89 |
vertex.group = null;
|
|
104 | 90 |
|
105 |
vertex.getInEdgeList().forEach(function(edge) { |
|
106 |
edge.moveEnd(this.getCenter()); |
|
107 |
}, vertex); |
|
91 |
let inEdgeList = vertex.inEdgeList; |
|
92 |
inEdgeList.forEach(edge => { |
|
93 |
edge.end = vertex.center; |
|
94 |
}); |
|
108 | 95 |
|
109 |
vertex.getOutEdgeList().forEach(function(edge) { |
|
110 |
edge.moveStart(this.getCenter()); |
|
111 |
}, vertex); |
|
96 |
let outEdgeList = vertex.outEdgeList; |
|
97 |
outEdgeList.forEach(edge => { |
|
98 |
edge.start = vertex.center; |
|
99 |
}); |
|
112 | 100 |
|
113 |
var vertexRelatedArchetypeMap = vertex.getRelatedArchetypeMap();
|
|
114 |
for (var archetypeIndex in vertexRelatedArchetypeMap) {
|
|
115 |
relatedArchetypeMap[archetypeIndex] -= vertexRelatedArchetypeMap[archetypeIndex]; |
|
101 |
let vertexRelatedArchetypeMap = vertex.relatedArchetypeMap;
|
|
102 |
for (let archetypeIndex in vertexRelatedArchetypeMap) {
|
|
103 |
this.relatedArchetypeMap[archetypeIndex] -= vertexRelatedArchetypeMap[archetypeIndex];
|
|
116 | 104 |
} |
117 | 105 |
|
118 |
app.viewportComponent.addVertex(vertex);
|
|
106 |
app.viewportComponent.addNode(vertex);
|
|
119 | 107 |
|
120 |
if (vertexListComponent) { |
|
121 |
vertexListComponent.removeChild(vertex); |
|
108 |
if (this._vertexListComponent) {
|
|
109 |
this._vertexListComponent.removeChild(vertex);
|
|
122 | 110 |
} |
123 | 111 |
|
124 |
vertexList.splice(vertexList.indexOf(vertex), 1);
|
|
125 |
};
|
|
126 |
|
|
112 |
this._vertexList.splice(this._vertexList.indexOf(vertex), 1);
|
|
113 |
} |
|
114 |
|
|
127 | 115 |
/** |
128 |
* @returns {array<Vertex>} List of vertices added to the group.
|
|
116 |
* @returns {array<Vertex>} List of vertices added to this group.
|
|
129 | 117 |
*/ |
130 |
this.getVertexList = function() { |
|
131 |
return vertexList; |
|
132 |
}; |
|
118 |
get vertexList() { |
|
119 |
return this._vertexList; |
|
120 |
} |
|
121 |
|
|
122 |
/** |
|
123 |
* @returns {integer} Number of vertices added to this group. |
|
124 |
*/ |
|
125 |
countVertices() { |
|
126 |
return this._vertexList.length; |
|
127 |
} |
|
133 | 128 |
|
134 | 129 |
/** |
135 | 130 |
* @returns {array<Edge>} List of edges going to vertices added to the group. |
136 | 131 |
*/ |
137 |
this.getInEdgeList = function() {
|
|
138 |
var edgeList = [];
|
|
132 |
get inEdgeList() {
|
|
133 |
let edgeList = [];
|
|
139 | 134 |
|
140 |
vertexList.forEach(function(vertex) { |
|
141 |
vertex.getInEdgeList().filter(function(edge) { |
|
135 |
this._vertexList.forEach(vertex => { |
|
136 |
let inEdgeList = vertex.inEdgeList; |
|
137 |
inEdgeList.filter(edge => { |
|
142 | 138 |
return edgeList.indexOf(edge) === -1; |
143 |
}).forEach(function(edge) {
|
|
139 |
}).forEach(edge => {
|
|
144 | 140 |
edgeList.push(edge); |
145 | 141 |
}); |
146 | 142 |
}); |
147 | 143 |
|
148 | 144 |
return edgeList; |
149 |
};
|
|
145 |
} |
|
150 | 146 |
|
151 | 147 |
/** |
152 | 148 |
* @returns {array<Edge>} List of edges going from vertices added to the group. |
153 | 149 |
*/ |
154 |
this.getOutEdgeList = function() {
|
|
155 |
var edgeList = [];
|
|
150 |
get outEdgeList() {
|
|
151 |
let edgeList = [];
|
|
156 | 152 |
|
157 |
vertexList.forEach(function(vertex) { |
|
158 |
vertex.getOutEdgeList().filter(function(edge) { |
|
153 |
this._vertexList.forEach(vertex => { |
|
154 |
let outEdgeList = vertex.outEdgeList; |
|
155 |
outEdgeList.filter(edge => { |
|
159 | 156 |
return edgeList.indexOf(edge) === -1; |
160 |
}).forEach(function(edge) {
|
|
157 |
}).forEach(edge => {
|
|
161 | 158 |
edgeList.push(edge); |
162 | 159 |
}); |
163 | 160 |
}); |
164 | 161 |
|
165 | 162 |
return edgeList; |
166 |
}; |
|
167 |
|
|
168 |
/** |
|
169 |
* @returns {array<Edge>} List of all edges related to vertices added to the group. |
|
170 |
*/ |
|
171 |
this.getEdgeList = function() { |
|
172 |
// TODO: some edges get duplicated in the resulting array this way |
|
173 |
|
|
174 |
return [].concat(this.getInEdgeList(), this.getOutEdgeList()); |
|
175 |
}; |
|
176 |
|
|
177 |
/** |
|
178 |
* @returns {object} Map with archetype indexes as keys and counters of their instances as values. |
|
179 |
*/ |
|
180 |
this.getRelatedArchetypeMap = function() { |
|
181 |
return relatedArchetypeMap; |
|
182 |
}; |
|
183 |
|
|
184 |
/** |
|
185 |
* @returns {Coordinates} Current position of the group in graph. |
|
186 |
*/ |
|
187 |
this.getPosition = function() { |
|
188 |
return position; |
|
189 |
}; |
|
190 |
|
|
191 |
/** |
|
192 |
* Updates the current position of the group in graph. |
|
193 |
*/ |
|
194 |
this.setPosition = function(coords) { |
|
195 |
if (!(coords instanceof Coordinates)) { |
|
196 |
throw new TypeError(coords.toString() + 'is not instance of Coordinates'); |
|
197 |
} |
|
198 |
|
|
199 |
position = coords; |
|
200 |
}; |
|
201 |
|
|
202 |
/** |
|
203 |
* @returns {Coordinates} Centre of the group. |
|
204 |
*/ |
|
205 |
this.getCenter = function() { |
|
206 |
return new Coordinates( |
|
207 |
position.x + size.width / 2, |
|
208 |
position.y + size.height / 2, |
|
209 |
); |
|
210 |
}; |
|
211 |
|
|
212 |
/** |
|
213 |
* Moves the group to a new position in graph. Edges related to vertices in the group are moved as well. |
|
214 |
* @param {Coordinates} coords Coordinates to be moved to. |
|
215 |
*/ |
|
216 |
this.move = function(coords) { |
|
217 |
if (!(coords instanceof Coordinates)) { |
|
218 |
throw new TypeError(coords.toString() + 'is not instance of Coordinates'); |
|
219 |
} |
|
220 |
|
|
221 |
rootElement.setAttribute('x', coords.x); |
|
222 |
rootElement.setAttribute('y', coords.y); |
|
223 |
|
|
224 |
vertexList.forEach(function(vertex) { |
|
225 |
vertex.getInEdgeList().forEach(function(edge) { |
|
226 |
edge.moveEnd(new Coordinates( |
|
227 |
coords.x + size.width / 2, |
|
228 |
coords.y + size.height / 2, |
|
229 |
)); |
|
230 |
}); |
|
231 |
|
|
232 |
vertex.getOutEdgeList().forEach(function(edge) { |
|
233 |
edge.moveStart(new Coordinates( |
|
234 |
coords.x + size.width / 2, |
|
235 |
coords.y + size.height / 2, |
|
236 |
)); |
|
237 |
}); |
|
238 |
}, this); |
|
239 |
}; |
|
240 |
|
|
241 |
/** |
|
242 |
* Sets the group as found. Highlighting is skipped when the group is excluded. |
|
243 |
* @param {boolean} newValue True to mark the group as found, otherwise false. |
|
244 |
*/ |
|
245 |
this.setFound = function(newValue) { |
|
246 |
found = newValue; |
|
247 |
|
|
248 |
if (excluded) return; |
|
249 |
|
|
250 |
if (newValue) { |
|
251 |
rootElement.classList.add('node--found'); |
|
252 |
} else { |
|
253 |
rootElement.classList.remove('node--found'); |
|
254 |
} |
|
255 |
}; |
|
256 |
|
|
257 |
/** |
|
258 |
* Toggles transparency of the group. |
|
259 |
* @param {boolean} newValue True to set the group semitransparent, false to display it normally. |
|
260 |
*/ |
|
261 |
this.setDimmed = function(newValue) { |
|
262 |
dimmed = newValue; |
|
263 |
|
|
264 |
if (excluded) return; |
|
265 |
|
|
266 |
if (newValue) { |
|
267 |
rootElement.classList.add('node--dimmed'); |
|
268 |
} else { |
|
269 |
rootElement.classList.remove('node--dimmed'); |
|
270 |
} |
|
271 |
}; |
|
272 |
|
|
273 |
/** |
|
274 |
* @returns {boolean} true if icon in all neighbours should be displayed, false otherwise |
|
275 |
*/ |
|
276 |
this.isIconsDisplayed = function () { |
|
277 |
return iconsDisplayed; |
|
278 |
}; |
|
279 |
|
|
280 |
/** |
|
281 |
* @returns true if the group is currently highlighted (in any way), otherwise false |
|
282 |
*/ |
|
283 |
this.isHighlighted = function() { |
|
284 |
return highlighted; |
|
285 |
}; |
|
286 |
|
|
287 |
/** |
|
288 |
* Toggles highlighting of the group. |
|
289 |
* @param {boolean} newValue True to highlight the group, false to unhighlight. |
|
290 |
*/ |
|
291 |
this.setHighlighted = function(newValue) { |
|
292 |
highlighted = newValue; |
|
293 |
|
|
294 |
if (newValue) { |
|
295 |
rootElement.classList.add('node--highlighted'); |
|
296 |
} else { |
|
297 |
rootElement.classList.remove('node--highlighted'); |
|
298 |
} |
|
299 |
}; |
|
300 |
|
|
301 |
/** |
|
302 |
* Toggles inner state of the group marking whether highlighting of its requirements is active. |
|
303 |
* @param {boolean} newValue True to highlight the neighbours, false to unhighlight. |
|
304 |
*/ |
|
305 |
this.setHighlightedRequiredNeighbours = function(newValue) { |
|
306 |
highlightedRequiredNeighbours = newValue; |
|
307 |
}; |
|
308 |
|
|
309 |
/** |
|
310 |
* Toggles inner state of the group marking whether highlighting of its dependents is active. |
|
311 |
* @param {boolean} newValue True to highlight the neighbours, false to unhighlight. |
|
312 |
*/ |
|
313 |
this.setHighlightedProvidedNeighbours = function(newValue) { |
|
314 |
highlightedProvidedNeighbours = newValue; |
|
315 |
}; |
|
316 |
|
|
317 |
/** |
|
318 |
* Toggles inner state of the group marking whether highlighting of instances of a vertex archetype is active. |
|
319 |
* @param {boolean} newValue True to highlight the neighbours, false to unhighlight. |
|
320 |
*/ |
|
321 |
this.setHighlightedArchetypeNeighbours = function(newValue) { |
|
322 |
highlightedArchetypeNeighbours = newValue; |
|
323 |
}; |
|
324 |
|
|
325 |
/** |
|
326 |
* Toggles highlighting of the group to mark it as requirement of some other node. |
|
327 |
* @param {boolean} newValue True to highlight, false to unhighlight. |
|
328 |
*/ |
|
329 |
this.setHighlightedRequired = function(newValue) { |
|
330 |
if (newValue) { |
|
331 |
rootElement.classList.add('node--highlighted-required'); |
|
332 |
} else { |
|
333 |
rootElement.classList.remove('node--highlighted-required'); |
|
334 |
} |
|
335 |
}; |
|
336 |
|
|
337 |
/** |
|
338 |
* Toggles highlighting of the group to mark it as dependent of some other node. |
|
339 |
* @param {boolean} newValue True to highlight, false to unhighlight. |
|
340 |
*/ |
|
341 |
this.setHighlightedProvided = function(newValue) { |
|
342 |
if (newValue) { |
|
343 |
rootElement.classList.add('node--highlighted-provided'); |
|
344 |
} else { |
|
345 |
rootElement.classList.remove('node--highlighted-provided'); |
|
346 |
} |
|
347 |
}; |
|
348 |
|
|
349 |
/** |
|
350 |
* Toggles highlighting of the group to mark it as instance of archetype related to some other node. |
|
351 |
* @param {boolean} newValue True to highlight, false to unhighlight. |
|
352 |
*/ |
|
353 |
this.setHighlightedArchetype = function(newValue) { |
|
354 |
if (newValue) { |
|
355 |
rootElement.classList.add('node--highlighted-archetype'); |
|
356 |
} else { |
|
357 |
rootElement.classList.remove('node--highlighted-archetype'); |
|
358 |
} |
|
359 |
}; |
|
163 |
} |
|
360 | 164 |
|
361 | 165 |
/** |
362 |
* @returns {boolean} True is the group is currently excluded from the viewport, otherwise false. |
|
166 |
* For some reason must be here to work. |
|
167 |
* @inheritdoc |
|
363 | 168 |
*/ |
364 |
this.isExcluded = function() {
|
|
365 |
return excluded;
|
|
366 |
};
|
|
169 |
get isExcluded() {
|
|
170 |
return super.isExcluded;
|
|
171 |
} |
|
367 | 172 |
|
368 | 173 |
/** |
369 | 174 |
* Toggles excluded state of the group. If the group is set excluded, a new floating point is created to connect it with |
... | ... | |
371 | 176 |
* Any node is called excluded when it is not visible in the viewport but instead in the sidebar. |
372 | 177 |
* @param {boolean} newValue True to set the group as excluded, otherwise false. |
373 | 178 |
*/ |
374 |
this.setExcluded = function(newValue) {
|
|
375 |
excluded = newValue;
|
|
179 |
set isExcluded(newValue) {
|
|
180 |
super.isExcluded = newValue;
|
|
376 | 181 |
|
377 |
vertexList.forEach(function(vertex) {
|
|
378 |
vertex.setExcluded(newValue);
|
|
182 |
this._vertexList.forEach(vertex => {
|
|
183 |
vertex.isExcluded = newValue;
|
|
379 | 184 |
}); |
380 |
|
|
381 |
if (newValue) { |
|
382 |
// set floater |
|
383 |
floater = new FloatingPoint; |
|
384 |
floater.setNode(this); |
|
385 |
app.sidebarComponent.addFloater(floater); |
|
386 |
|
|
387 |
} else { |
|
388 |
// remove floater |
|
389 |
app.sidebarComponent.removeFloater(floater); |
|
390 |
delete floater; |
|
391 |
} |
|
392 |
}; |
|
393 |
|
|
394 |
/** |
|
395 |
* Excludes the group from the viewport. Removes group DOM element and hides its edges. If showIcon is set to True |
|
396 |
* display icon in all neighbour vertices. |
|
397 |
* @param {boolean} showIcon True to display icon in all neighbours, False otherwise |
|
398 |
*/ |
|
399 |
this.exclude = function(showIcon) { |
|
400 |
this.setExcluded(true); |
|
401 |
this.remove(true); |
|
402 |
|
|
403 |
app.viewportComponent.removeGroup(this); |
|
404 |
|
|
405 |
if(showIcon){ |
|
406 |
showIconClick.bind(this)(null) |
|
407 |
} |
|
408 |
}; |
|
185 |
} |
|
409 | 186 |
|
410 | 187 |
/** |
411 | 188 |
* Includes the group in the viewport. Afterwards, edges related to the group are moved to the current position of the group. |
412 | 189 |
*/ |
413 |
this.include = function() { |
|
414 |
this.removeFromSidebarList(); |
|
415 |
|
|
416 |
if(iconsDisplayed) { |
|
417 |
showIconClick.bind(this)(null); |
|
418 |
} |
|
419 |
|
|
420 |
this.setExcluded(false); |
|
421 |
this.remove(false); |
|
422 |
|
|
423 |
app.viewportComponent.addGroup(this); |
|
190 |
include() { |
|
191 |
super.include(); |
|
424 | 192 |
|
425 | 193 |
// set edges' ends |
426 |
var inEdgeList = this.getInEdgeList(); |
|
427 |
inEdgeList.forEach(function(edge) { |
|
428 |
edge.moveEnd(this.getCenter()); |
|
429 |
}, this); |
|
430 |
|
|
431 |
var outEdgeList = this.getOutEdgeList(); |
|
432 |
outEdgeList.forEach(function(edge) { |
|
433 |
edge.moveStart(this.getCenter()); |
|
434 |
}, this); |
|
435 |
}; |
|
436 |
|
|
437 |
/** |
|
438 |
* Hook function used to remove the group from the sidebar list it is located in before it is moved to the viewport. |
|
439 |
*/ |
|
440 |
this.removeFromSidebarList = Utils.noop; |
|
441 |
|
|
442 |
/** |
|
443 |
* Creates a new DOM element representing the group in memory. The element being created depends on whether the group |
|
444 |
* is excluded at the moment. Binds user interactions to local handler functions. |
|
445 |
* @returns {Element} depending on whether the group is excluded. |
|
446 |
*/ |
|
447 |
this.render = function() { |
|
448 |
rootElement = excluded ? renderExcluded.call(this) : renderIncluded.call(this); |
|
449 |
|
|
450 |
this.setHighlighted(highlighted); |
|
451 |
|
|
452 |
return rootElement; |
|
453 |
}; |
|
454 |
|
|
455 |
/** |
|
456 |
* Removes the DOM element representing the group from document. |
|
457 |
* @param {boolean} hideEdges True to hide edges of vertices in the group in the viewport. Edges are (almost) never really |
|
458 |
* removed but rather hidden for cases when a node is included back in the viewport. |
|
459 |
*/ |
|
460 |
this.remove = function(hideEdges) { |
|
461 |
rootElement.remove(); |
|
462 |
|
|
463 |
// hide edges |
|
464 |
var inEdgeList = this.getInEdgeList(); |
|
465 |
inEdgeList.filter(function(edge) { |
|
466 |
return !edge.getFrom().isExcluded(); |
|
467 |
}).forEach(function(edge) { |
|
468 |
edge.setHidden(hideEdges); |
|
194 |
let inEdgeList = this.inEdgeList; |
|
195 |
inEdgeList.forEach(edge => { |
|
196 |
edge.end = this.center; |
|
469 | 197 |
}); |
470 | 198 |
|
471 |
var outEdgeList = this.getOutEdgeList(); |
|
472 |
outEdgeList.filter(function(edge) { |
|
473 |
return !edge.getTo().isExcluded(); |
|
474 |
}).forEach(function(edge) { |
|
475 |
edge.setHidden(hideEdges); |
|
199 |
let outEdgeList = this.outEdgeList; |
|
200 |
outEdgeList.forEach(edge => { |
|
201 |
edge.start = this.center; |
|
476 | 202 |
}); |
477 |
};
|
|
203 |
} |
|
478 | 204 |
|
479 | 205 |
/** |
480 | 206 |
* Exports the group to a new, plain JS object. |
481 | 207 |
* @returns {Object} exported group |
482 | 208 |
*/ |
483 |
this.export = function() { |
|
484 |
var verticesId = vertexList.map(function(vertex) { |
|
485 |
return vertex.id; |
|
486 |
}); |
|
487 |
|
|
209 |
export() { |
|
488 | 210 |
return { |
489 | 211 |
id: this.id, |
490 | 212 |
name: this.name, |
491 |
verticesId: verticesId,
|
|
213 |
verticesId: this.vertexList.map(vertex => vertex.id),
|
|
492 | 214 |
verticesEdgeFromId: [], // TODO: what to put in here? ids of vertices which outgoing edges are visible in graph |
493 | 215 |
verticesEdgeToId: [], // TODO: what to put in here? ids of vertices which incoming edges are visible in graph |
494 |
position: position, |
|
216 |
position: this.position,
|
|
495 | 217 |
}; |
496 |
};
|
|
218 |
} |
|
497 | 219 |
|
498 | 220 |
/** |
499 |
* @returns {Element} SVG DOM element. |
|
221 |
* @returns {SVGElement} SVG DOM element.
|
|
500 | 222 |
*/ |
501 |
function renderIncluded() {
|
|
502 |
rootElement = DOM.createSvgElement('svg', {
|
|
503 |
'class': 'node group',
|
|
504 |
'x': position.x,
|
|
505 |
'y': position.y,
|
|
223 |
_renderIncluded() {
|
|
224 |
this._rootElement = DOM.s('svg', {
|
|
225 |
class: 'node group',
|
|
226 |
x: this.position.x,
|
|
227 |
y: this.position.y,
|
|
506 | 228 |
'data-id': this.id, |
229 |
onClick: this._onNodeClick.bind(this), |
|
230 |
onMouseDown: this._onNodeMouseDown.bind(this), |
|
231 |
}, [ |
|
232 |
DOM.s('rect', { |
|
233 |
width: this.size.width, |
|
234 |
height: this.size.height, |
|
235 |
x: 1, |
|
236 |
y: 1, |
|
237 |
fill: this.symbol[1], |
|
238 |
stroke: 'black', |
|
239 |
'stroke-width': 1, |
|
240 |
}), |
|
241 |
// name |
|
242 |
DOM.s('foreignObject', { |
|
243 |
x: 4, |
|
244 |
y: 4, |
|
245 |
width: 46, |
|
246 |
height: 16, |
|
247 |
onClick: this._onNameClick.bind(this), |
|
248 |
}, [ |
|
249 |
DOM.h('span', { |
|
250 |
class: 'group-name', |
|
251 |
innerText: this.name, |
|
252 |
}), |
|
253 |
]), |
|
254 |
// symbol |
|
255 |
DOM.s('text', { |
|
256 |
class: 'group-symbol', |
|
257 |
x: 10, |
|
258 |
y: 60, |
|
259 |
}, [ |
|
260 |
DOM.t(this.symbol[0]), |
|
261 |
]), |
|
262 |
// dissolve button |
|
263 |
DOM.s('g', { |
|
264 |
class: 'button dissolve-button', |
|
265 |
onClick: this._onDissolveClick.bind(this), |
|
266 |
}, [ |
|
267 |
DOM.s('rect', { |
|
268 |
rx: 4, |
|
269 |
ry: 4, |
|
270 |
x: 54, |
|
271 |
y: 4, |
|
272 |
height: 14, |
|
273 |
width: 14, |
|
274 |
}), |
|
275 |
DOM.s('line', { |
|
276 |
x1: 57, |
|
277 |
y1: 7, |
|
278 |
x2: 65, |
|
279 |
y2: 15, |
|
280 |
}), |
|
281 |
DOM.s('line', { |
|
282 |
x1: 65, |
|
283 |
y1: 7, |
|
284 |
x2: 57, |
|
285 |
y2: 15, |
|
286 |
}), |
|
287 |
]), |
|
288 |
]); |
|
289 |
|
|
290 |
// symbol list |
|
291 |
this._symbolListComponent = new NodeSymbolList; |
|
292 |
this._rootElement.appendChild(this._symbolListComponent.render()); |
|
293 |
|
|
294 |
this._symbolList.forEach(symbol => { |
|
295 |
this._symbolListComponent.appendChild(symbol); |
|
507 | 296 |
}); |
508 |
rootElement.addEventListener('click', click.bind(this)); |
|
509 |
rootElement.addEventListener('mousedown', mouseDown.bind(this)); |
|
510 |
|
|
511 |
rootElement.appendChild(DOM.createSvgElement('rect', { |
|
512 |
'width': size.width, |
|
513 |
'height': size.height, |
|
514 |
'x': 1, |
|
515 |
'y': 1, |
|
516 |
'fill': this.symbol[1], |
|
517 |
'stroke': 'black', |
|
518 |
'stroke-width': 1, |
|
519 |
})); |
|
520 |
|
|
521 |
// name |
|
522 |
var nameTextInner = DOM.createHtmlElement('span', { |
|
523 |
'class': 'group-name', |
|
524 |
}); |
|
525 |
nameTextInner.appendChild(DOM.createTextElement(this.name)); |
|
526 | 297 |
|
527 |
var nameText = DOM.createSvgElement('foreignObject', { |
|
528 |
'x': 4, |
|
529 |
'y': 4, |
|
530 |
'width': 46, |
|
531 |
'height': 16, |
|
532 |
}); |
|
533 |
nameText.appendChild(nameTextInner); |
|
534 |
nameText.addEventListener('click', nameClick.bind(this)); |
|
535 |
rootElement.appendChild(nameText); |
|
536 |
|
|
537 |
// symbol |
|
538 |
var symbolText = DOM.createSvgElement('text', { |
|
539 |
'class': 'group-symbol', |
|
540 |
'x': 10, |
|
541 |
'y': 60, |
|
542 |
}); |
|
543 |
symbolText.appendChild(document.createTextNode(this.symbol[0])); |
|
544 |
rootElement.appendChild(symbolText); |
|
545 |
|
|
546 |
// dissolve button |
|
547 |
var dissolveButton = DOM.createSvgElement('g', { |
|
548 |
'class': 'button dissolve-button', |
|
549 |
}); |
|
550 |
dissolveButton.appendChild(DOM.createSvgElement('rect', { |
|
551 |
'rx': 4, |
|
552 |
'ry': 4, |
|
553 |
'x': 54, |
|
554 |
'y': 4, |
|
555 |
'height': 14, |
|
556 |
'width': 14, |
|
557 |
})); |
|
558 |
dissolveButton.appendChild(DOM.createSvgElement('line', { |
|
559 |
'x1': 57, |
|
560 |
'y1': 7, |
|
561 |
'x2': 65, |
|
562 |
'y2': 15, |
|
563 |
})); |
|
564 |
dissolveButton.appendChild(DOM.createSvgElement('line', { |
|
565 |
'x1': 65, |
|
566 |
'y1': 7, |
|
567 |
'x2': 57, |
|
568 |
'y2': 15, |
|
569 |
})); |
|
570 |
dissolveButton.addEventListener('click', dissolveClick.bind(this)); |
|
571 |
rootElement.appendChild(dissolveButton); |
|
572 |
|
|
573 |
return rootElement; |
|
298 |
return this._rootElement; |
|
574 | 299 |
} |
575 | 300 |
|
576 | 301 |
/** |
577 |
* @returns {Element} HTML DOM element. |
|
302 |
* @returns {HTMLElement} HTML DOM element.
|
|
578 | 303 |
*/ |
579 |
function renderExcluded() { |
|
580 |
rootElement = DOM.createHtmlElement('li', { |
|
581 |
'class': 'node group', |
|
582 |
'data-id': this.id, |
|
304 |
_renderExcluded() { |
|
305 |
const svg = DOM.s('svg', { |
|
306 |
xmlns: 'http://www.w3.org/2000/svg', |
|
307 |
height: 60, |
|
308 |
width: 46, |
|
583 | 309 |
}); |
584 | 310 |
|
585 |
var svg = DOM.createSvgElement('svg', { |
|
586 |
'xmlns': 'http://www.w3.org/2000/svg', |
|
587 |
'height': 60, |
|
588 |
'width': 46, |
|
589 |
}); |
|
590 |
rootElement.appendChild(svg); |
|
591 |
|
|
592 | 311 |
// related archetypes |
593 |
var relatedArchetypesGroup = DOM.createSvgElement('g', {
|
|
594 |
'transform': 'translate(10, 15)',
|
|
312 |
const relatedArchetypesGroup = DOM.s('g', {
|
|
313 |
transform: 'translate(10, 15)',
|
|
595 | 314 |
}); |
596 | 315 |
svg.appendChild(relatedArchetypesGroup); |
597 | 316 |
|
598 |
var archetypeIconOrder = 0; |
|
599 |
for (var archetypeIndex in relatedArchetypeMap) { |
|
600 |
var relatedArchetype = DOM.createSvgElement('g', { |
|
601 |
'class': 'related-archetype', |
|
602 |
'transform': `translate(0, ${archetypeIconOrder * 20})`, |
|
603 |
}); |
|
604 |
relatedArchetypesGroup.appendChild(relatedArchetype); |
|
605 |
|
|
606 |
// counter |
|
607 |
var relatedArchetypeCounter = DOM.createSvgElement('text'); |
|
608 |
relatedArchetypeCounter.appendChild(DOM.createTextElement(relatedArchetypeMap[archetypeIndex])); |
|
609 |
relatedArchetype.appendChild(relatedArchetypeCounter); |
|
610 |
|
|
611 |
// icon |
|
612 |
var relatedArchetypeIcon = DOM.createSvgElement('use', { |
|
613 |
'href': '#vertexArchetypeIcon-' + app.archetype.vertex[archetypeIndex].name, |
|
614 |
'class': 'archetype-icon', |
|
615 |
'transform': `translate(15, -10)`, |
|
616 |
}); |
|
617 |
relatedArchetypeIcon.addEventListener('click', relatedArchetypeClick.bind(this, parseInt(archetypeIndex))); // TODO when icon == null can not click on item |
|
618 |
relatedArchetype.appendChild(relatedArchetypeIcon); |
Také k dispozici: Unified diff
reworked Vertex, Group and Edge to ES6 classes; Vertex and Group extend newly created Node class