Projekt

Obecné

Profil

« Předchozí | Další » 

Revize a4090428

Přidáno uživatelem Pavel Fidranský před více než 6 roky(ů)

reworked Vertex, Group and Edge to ES6 classes; Vertex and Group extend newly created Node class

Zobrazit rozdíly:

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);
... Rozdílový soubor je zkrácen, protože jeho délka přesahuje max. limit.

Také k dispozici: Unified diff