Projekt

Obecné

Profil

« Předchozí | Další » 

Revize f5d0189e

Přidáno uživatelem Pavel Fidranský před asi 6 roky(ů)

dropped unused functionality

Zobrazit rozdíly:

sources/src/main/webapp/js/app.js
11 11
	this.graphExporter = new GraphExporter;
12 12
	/** @prop {GraphHistory} graphHistory */
13 13
	this.graphHistory = new GraphHistory;
14
	/** @prop {JavaComponentChanger} componentChanger */
15
	this.componentChanger = new JavaComponentChanger;
16 14
	/** @prop {Loader} loader */
17 15
	this.loader = new Loader;
18 16
	/** @prop {Zoom} zoom */
sources/src/main/webapp/js/components/change.js
1
/**
2
 * Class representing a change. In a single change, lists of components to be changed and proposals are stored. 
3
 * Changes can be postponed and back activated.
4
 * @constructor
5
 */
6
function Change() {
7
	var rootElement;
8
	var buttonGroup;
9
	var includeNotFoundCheckbox;
10
	var replaceComponentsCheckbox;
11
	var triggerChangeButton;
12
	var oldVertexListComponent;
13
	var newVertexListComponent;
14

  
15
	var oldVertexList = [];
16
	var newVertexList = [];
17

  
18
	var postponed = false;
19
	var triggered = false;
20
	var detailsLoaded = false;
21

  
22
	/* TODO: display change in modal window
23
	app.changeModalWindow.setChange(this);
24
	app.changeModalWindow.open();
25
	*/
26

  
27
	/**
28
	 * Adds a new vertex to the list of components to be changed. The vertex is set as excluded and its DOM element 
29
	 * is removed from document. Its edges are moved so that they end at the group.
30
	 * @param {Vertex} vertex Vertex to be added to the change.
31
	 */
32
	this.addVertex = function(vertex) {
33
		if (!(vertex instanceof Vertex)) {
34
			throw new TypeError(vertex.toString() + 'is not instance of Vertex');
35
		}
36

  
37
		// set remove hook
38
		vertex.removeFromSidebarList = this.removeVertex.bind(this, vertex);
39

  
40
		oldVertexList.push(vertex);
41
		oldVertexListComponent.appendChild(vertex);
42

  
43
		app.redrawEdges();
44

  
45
		// vertex list changed so different results could be retrieved when triggered again
46
		this.setTriggered(false);
47
		this.setDetailsLoaded(false);
48

  
49
		toggleButtonGroup.call(this);
50
	};
51

  
52
	/**
53
	 * Removes a vertex from the list of component to be changed. The vertex is returned back to the viewport and 
54
	 * its edges are moved to it.
55
	 * @param {Vertex} vertex Vertex to be removed from the change.
56
	 */
57
	this.removeVertex = function(vertex) {
58
		if (!(vertex instanceof Vertex)) {
59
			throw new TypeError(vertex.toString() + 'is not instance of Vertex');
60
		}
61

  
62
		// unset remove hook
63
		vertex.removeFromSidebarList = app.utils.noop;
64

  
65
		oldVertexList.splice(oldVertexList.indexOf(vertex), 1);
66
		oldVertexListComponent.removeChild(vertex);
67

  
68
		app.redrawEdges();
69

  
70
		// vertex list changed so different results could be retrieved when triggered again
71
		this.setTriggered(false);
72
		this.setDetailsLoaded(false);
73

  
74
		toggleButtonGroup.call(this);
75
	};
76

  
77
	/**
78
	 * @returns {array<Vertex>} List of components to be changed.
79
	 */
80
	this.getOldVertexList = function() {
81
		return oldVertexList;
82
	};
83

  
84
	/**
85
	 * @returns {boolean} True if the change was already triggered, otherwise false.
86
	 */
87
	this.isTriggered = function() {
88
		return triggered;
89
	};
90

  
91
	/**
92
	 * Sets the change as triggered.
93
	 * @param {boolean} newValue True to set the change as trigger, otherwise false.
94
	 */
95
	this.setTriggered = function(newValue) {
96
		triggered = newValue;
97

  
98
		if (newValue) {
99
			rootElement.classList.add('change--triggered');
100
		} else {
101
			rootElement.classList.remove('change--triggered');
102
		}
103
	};
104

  
105
	/**
106
	 * @returns {boolean} True if the details of proposed component were loaded, otherwise false.
107
	 */
108
	this.isDetailsLoaded = function() {
109
		return detailsLoaded;
110
	};
111

  
112
	/**
113
	 * Sets the change details as loaded.
114
	 * @param {boolean} newValue True to set the change as it has details loaded, otherwise false.
115
	 */
116
	this.setDetailsLoaded = function(newValue) {
117
		detailsLoaded = newValue;
118

  
119
		if (newValue) {
120
			rootElement.classList.add('change--details-loaded');
121
		} else {
122
			rootElement.classList.remove('change--details-loaded');
123
		}
124
	};
125

  
126
	/**
127
	 * @returns {boolean} True if the change is currently postponed, otherwise false.
128
	 */
129
	this.isPostponed = function() {
130
		return postponed;
131
	};
132

  
133
	/**
134
	 * Sets the change postponed.
135
	 * @param {boolean} newValue True to set the change as postponed, otherwise false.
136
	 */
137
	this.setPostponed = function(newValue) {
138
		postponed = newValue;
139

  
140
		if (newValue) {
141
			rootElement.classList.add('change--postponed');
142
		} else {
143
			rootElement.classList.remove('change--postponed');
144
		}
145
	};
146

  
147
	/**
148
	 * Postpones the change. Removes its current DOM element from document.
149
	 */
150
	this.postpone = function() {
151
		this.setPostponed(true);
152
		this.remove();
153
	};
154

  
155
	/**
156
	 * Activates the change. Removes its current DOM element from document.
157
	 */
158
	this.activate = function() {
159
		this.setPostponed(false);
160
		this.remove();
161
	};
162

  
163
	/**
164
	 * Creates a new DOM element representing the change in memory. The element being created depends on whether the change
165
	 * is postponed at the moment. Binds user interactions to local handler functions.
166
	 * @returns {Element} HTML or SVG DOM element depending on whether the change is postponed.
167
	 */
168
	this.render = function() {
169
		rootElement = postponed ? renderPostponed.call(this) : renderActive.call(this);
170

  
171
		this.setPostponed(postponed);
172
		this.setDetailsLoaded(detailsLoaded);
173
		this.setTriggered(triggered);
174
		toggleButtonGroup.call(this);
175

  
176
		return rootElement;
177
	};
178

  
179
	/**
180
	 * Removes the DOM element representing the change from document.
181
	 */
182
	this.remove = function() {
183
		rootElement.remove();
184
	};
185

  
186
	function renderActive() {
187
		rootElement = app.utils.createHtmlElement('div', {
188
			'class': 'change',
189
		});
190

  
191
		// buttons
192
		buttonGroup = app.utils.createHtmlElement('div', {
193
			'class': 'button-group hidden',
194
		});
195
		rootElement.appendChild(buttonGroup);
196

  
197
		// include not found classes checkbox
198
		includeNotFoundCheckbox = app.utils.createHtmlElement('input', {
199
			'type': 'checkbox',
200
			'name': 'includeNotFoundClasses',
201
			'class': 'include-not-found-checkbox',
202
			'title': 'Include not found classes in the change',
203
			'data-tooltip': 'left',
204
		});
205
		buttonGroup.appendChild(includeNotFoundCheckbox);
206

  
207
		// trigger change button
208
		triggerChangeButton = app.utils.createHtmlElement('button', {
209
			'class': 'trigger-change-button button',
210
			'title': 'Change components',
211
		});
212
		triggerChangeButton.appendChild(app.utils.createHtmlElement('img', {
213
			'src': 'images/tochange/crce-call-trans.gif',
214
			'alt': 'Icon of "change components" action',
215
		}));
216
		triggerChangeButton.addEventListener('click', triggerChange.bind(this));
217
		buttonGroup.appendChild(triggerChangeButton);
218

  
219
		// postpone button
220
		var postponeButton = app.utils.createHtmlElement('button', {
221
			'class': 'postpone-button button',
222
			'title': 'Postpone change',
223
		});
224
		postponeButton.appendChild(app.utils.createHtmlElement('img', {
225
			'src': 'images/tochange/postpone-trans.gif',
226
			'alt': 'Icon of "postpone current change" action',
227
		}));
228
		postponeButton.addEventListener('click', moveToPostponed.bind(this));
229
		buttonGroup.appendChild(postponeButton);
230

  
231

  
232
		// old vertex list
233
		oldVertexListComponent = new ChangeVertexList(this);
234
		rootElement.appendChild(oldVertexListComponent.render());
235

  
236
		oldVertexList.forEach(function(vertex) {
237
			oldVertexListComponent.appendChild(vertex);
238
		}, this);
239

  
240

  
241
		// controls
242
		var changeControls = app.utils.createHtmlElement('div', {
243
			'class': 'change-controls',
244
		});
245
		rootElement.appendChild(changeControls);
246

  
247
		// transition arrow
248
		var arrow = app.utils.createHtmlElement('span', {
249
			'class': 'transition-arrow',
250
		});
251
		arrow.appendChild(app.utils.createTextElement('🡻'));
252
		changeControls.appendChild(arrow);
253

  
254
		// control buttons
255
		var controlButtonGroup = app.utils.createHtmlElement('div', {
256
			'class': 'button-group',
257
		});
258
		changeControls.appendChild(controlButtonGroup);
259

  
260
		// load change details button
261
		var loadDetailsButton = app.utils.createHtmlElement('button', {
262
			'class': 'load-details-button button',
263
			'title': 'Load change details',
264
		});
265
		loadDetailsButton.appendChild(app.utils.createTextElement('…'));
266
		loadDetailsButton.addEventListener('click', loadChangeDetails.bind(this));
267
		controlButtonGroup.appendChild(loadDetailsButton);
268

  
269
		// replace old components by new ones checkbox
270
		replaceComponentsCheckbox = app.utils.createHtmlElement('input', {
271
			'type': 'checkbox',
272
			'name': 'replaceComponents',
273
			'class': 'replace-components-checkbox',
274
			'title': 'Replace old components by new ones',
275
			'checked': 'checked',
276
			'data-tooltip': 'left',
277
		});
278
		controlButtonGroup.appendChild(replaceComponentsCheckbox);
279

  
280
		// accept change button
281
		var acceptButton = app.utils.createHtmlElement('button', {
282
			'class': 'accept-button button',
283
			'title': 'Accept proposed change',
284
		});
285
		acceptButton.appendChild(app.utils.createHtmlElement('img', {
286
			'src': 'images/tochange/accept-trans.gif',
287
			'alt': 'Icon of "accept proposed change" action',
288
		}));
289
		acceptButton.addEventListener('click', acceptChange.bind(this));
290
		controlButtonGroup.appendChild(acceptButton);
291

  
292
		// revoke change button
293
		var revokeButton = app.utils.createHtmlElement('button', {
294
			'class': 'revoke-button button',
295
			'title': 'Revoke proposed change',
296
		});
297
		revokeButton.appendChild(app.utils.createHtmlElement('img', {
298
			'src': 'images/button_cancel.png',
299
			'alt': 'Icon of "revoke proposed change" action',
300
		}));
301
		revokeButton.addEventListener('click', revokeChange.bind(this));
302
		controlButtonGroup.appendChild(revokeButton);
303

  
304

  
305
		// new vertex list
306
		newVertexListComponent = new ChangeVertexList(this);
307
		rootElement.appendChild(newVertexListComponent.render());
308

  
309
		newVertexList.forEach(function(vertex) {
310
			newVertexListComponent.appendChild(vertex);
311
		}, this);
312

  
313
		return rootElement;
314
	}
315

  
316
	function renderPostponed() {
317
		rootElement = app.utils.createHtmlElement('li', {
318
			'class': 'change',
319
		});
320

  
321

  
322
		// buttons
323
		var buttonGroup = app.utils.createHtmlElement('div', {
324
			'class': 'button-group',
325
		});
326
		rootElement.appendChild(buttonGroup);
327

  
328
		// activate change button
329
		var activateButton = app.utils.createHtmlElement('button', {
330
			'class': 'activate-button button',
331
			'title': 'Set change active',
332
		});
333
		activateButton.appendChild(app.utils.createHtmlElement('img', {
334
			'src': 'images/tochange/tochange-trans.gif',
335
			'alt': 'Icon of "set change active" action',
336
		}));
337
		activateButton.addEventListener('click', moveToActive.bind(this));
338
		buttonGroup.appendChild(activateButton);
339

  
340

  
341
		// original vertex list
342
		oldVertexListComponent = new ChangeVertexList(this);
343
		rootElement.appendChild(oldVertexListComponent.render());
344

  
345
		oldVertexList.forEach(function(vertex) {
346
			oldVertexListComponent.appendChild(vertex);
347
		}, this);
348

  
349

  
350
		// new vertex list
351
		newVertexListComponent = new ChangeVertexList(this);
352
		rootElement.appendChild(newVertexListComponent.render());
353

  
354
		newVertexList.forEach(function(vertex) {
355
			newVertexListComponent.appendChild(vertex);
356
		}, this);
357

  
358

  
359
		return rootElement;
360
	}
361

  
362
	function triggerChange() {
363
		var self = this;
364
		var includeNotFound = includeNotFoundCheckbox.checked;
365

  
366
		app.loader.enable();
367

  
368
		app.componentChanger.run(oldVertexList, includeNotFound).then(function(data) {
369
			self.setTriggered(true);
370

  
371
			data.forEach(function(component) {
372
				var vertex = new VertexLight(component);
373

  
374
				newVertexList.push(vertex);
375
				newVertexListComponent.appendChild(vertex);
376
			});
377

  
378
		}, function(reason) {
379
			if (typeof reason === 'string') {
380
				alert(reason);
381
			} else {
382
				alert('Error occurred while querying CRCE. See JS console for more details.');
383
				console.error(reason);
384
			}
385

  
386
		}).always(function() {
387
			app.loader.disable();
388
		});
389
	}
390

  
391
	function loadChangeDetails() {
392
		var self = this;
393
		var graphVersion = parseInt(app.cookies.get('graphVersion'));
394
		var uuidToNameMap = {};
395

  
396
		app.loader.enable();
397

  
398
		downloadComponents(graphVersion + 1, newVertexList).then(function(responses) {
399
			// get map with component's UUID as key and its filename as value
400
			uuidToNameMap = responses.map(function(response) {
401
				return response[0];
402
			}).reduce(function(map, component) {
403
				map[component.uuid] = component.name;
404
				return map;
405
			}, {});
406

  
407
			return copyComponents(app.vertexList);
408

  
409
		}).then(function() {
410
			return loadGraphData(graphVersion + 1);
411

  
412
		}).then(function(data) {
413
			// vertices
414
			data.vertices.forEach(function(component) {
415
				var vertex = app.findVertexByName(component.name);
416

  
417
				if (app.utils.isDefined(vertex)) {
418
					// vertex already exists in graph
419
					return;
420

  
421
				} else {
422
					// create a new vertex
423
					var vertex = new Vertex(component);
424
					vertex.setPosition(new Coordinates(0, 0));
425

  
426
					app.nodeList.push(vertex);
427
					app.vertexList.push(vertex);
428

  
429
					app.viewportComponent.addVertex(vertex);
430
				}
431

  
432
				// replace light vertex placeholder by vertex component
433
				var placeholdingVertex = newVertexList.find(function(newVertex) {
434
					return uuidToNameMap[newVertex.id] == this.name;
435
				}, vertex);
436

  
437
				if (app.utils.isDefined(placeholdingVertex)) {
438
					newVertexList.splice(newVertexList.indexOf(placeholdingVertex), 1);
439
					newVertexListComponent.removeChild(placeholdingVertex);
440
				}
441

  
442
				newVertexList.push(vertex);
443
			});
444

  
445
			var newVertexNameList = newVertexList.map(function(vertex) {
446
				return vertex.name;
447
			});
448

  
449
			// edges
450
			data.edges.forEach(function(component) {
451
				// vertex names are prefixed by "vertex_" string, cut it off
452
				var fromNodeName = component.from.substring(7);
453
				var toNodeName = component.to.substring(7);
454

  
455
				// neither end of the edge is a newly added vertex
456
				if (newVertexNameList.indexOf(fromNodeName) < 0 && newVertexNameList.indexOf(toNodeName) < 0) return;
457

  
458
				var edge = new Edge(component);
459

  
460
				var fromNode = app.findVertexByName(fromNodeName);
461
				if (fromNode) {
462
					fromNode.addOutEdge(edge);
463
				}
464

  
465
				var toNode = app.findVertexByName(toNodeName);
466
				if (toNode) {
467
					toNode.addInEdge(edge);
468
				}
469

  
470
				app.edgeList.push(edge);
471

  
472
				app.viewportComponent.addEdge(edge);
473
			});
474

  
475

  
476
			newVertexList.forEach(function(vertex) {
477
				vertex.exclude();
478

  
479
				newVertexListComponent.appendChild(vertex);
480
			});
481

  
482
			app.redrawEdges();
483

  
484
			self.setDetailsLoaded(true);
485

  
486
		}).always(function() {
487
			app.loader.disable();
488
		});
489
	}
490

  
491
	function downloadComponents(graphVersion, vertexList) {
492
		var downloadPromises = [];
493
		vertexList.forEach(function(vertex) {
494
			var downloadPromise = $.ajax({
495
				type: 'POST',
496
				url: 'api/download-component?graphVersion=' + graphVersion + '&uuid=' + encodeURIComponent(vertex.id),
497
				contentType: 'application/json',
498
				timeout: 180 * 1000,	// in milliseconds
499
			});
500

  
501
			downloadPromises.push(downloadPromise);
502
		});
503

  
504
		return app.utils.promiseAll(downloadPromises);
505
	}
506

  
507
	function deleteComponents(graphVersion, vertexList) {
508
		var deletePromises = [];
509
		vertexList.forEach(function(vertex) {
510
			var deletePromise = $.ajax({
511
				type: 'GET',
512
				url: 'delete-component?graphVersion=' + graphVersion + '&name=' + encodeURIComponent(vertex.name),
513
				timeout: 180 * 1000,	// in milliseconds
514
			});
515

  
516
			deletePromises.push(deletePromise);
517
		});
518

  
519
		return app.utils.promiseAll(deletePromises);
520
	}
521

  
522
	function copyComponents(vertexList) {
523
		var involvedComponents = vertexList.filter(function(vertex) {
524
			return vertex.name !== app.constants.notFoundVertexName;
525
		}).map(function(vertex) {
526
			return {
527
				'id': vertex.id,
528
				'name': vertex.name,
529
			};
530
		});
531

  
532
		return $.ajax({
533
			type: 'POST',
534
			url: 'api/copy-components',
535
			data: JSON.stringify({
536
				'components': involvedComponents,
537
			}),
538
			contentType: 'application/json',
539
			timeout: 180 * 1000,	// in milliseconds
540
		});
541
	}
542

  
543
	function loadGraphData(graphVersion) {
544
		return $.getJSON(app.API.loadGraph + '?graphVersion=' + graphVersion);
545
	}
546

  
547
	function acceptChange() {
548
		var self = this;
549
		var graphVersion = parseInt(app.cookies.get('graphVersion'));
550
		var replaceComponents = replaceComponentsCheckbox.checked;
551

  
552
		app.loader.enable();
553

  
554
		var promise;
555
		if (this.isDetailsLoaded()) {
556
			var deleteComponentsPromise;
557
			if (replaceComponents) {
558
				deleteComponentsPromise = deleteComponents(graphVersion + 1, oldVertexList);
559
			} else {
560
				deleteComponentsPromise = $.when();
561
			}
562

  
563
			promise = deleteComponentsPromise.then(function() {
564
				return loadGraphData(graphVersion + 1);
565
			});
566

  
567
		} else {
568
			// download and copy components on the server side and then load the graph
569
			promise = downloadComponents(graphVersion + 1, newVertexList).then(function(responses) {
570
				var involvedVertexList = app.vertexList;
571

  
572
				if (replaceComponents) {
573
					involvedVertexList = involvedVertexList.filter(function(vertex) {
574
						return oldVertexList.some(function(oldVertex) {
575
							return oldVertex.name !== vertex.name;
576
						});
577
					});
578
				}
579

  
580
				return copyComponents(involvedVertexList);
581

  
582
			}).then(function() {
583
				return loadGraphData(graphVersion + 1);
584
			});
585
		}
586

  
587
		promise.then(function(data) {
588
			// increment graph version number
589
			app.cookies.set('graphVersion', graphVersion + 1);
590

  
591
			var graphExportData = app.graphExporter.run();
592

  
593
			app.reset();
594
			app.graphLoader.run(data, graphExportData);
595

  
596
		}).always(function() {
597
			app.loader.disable();
598
		});
599
	}
600

  
601
	function revokeChange() {
602
		var self = this;
603
		var graphVersion = parseInt(app.cookies.get('graphVersion'));
604

  
605
		app.loader.enable();
606

  
607
		var deleteComponentsPromise;
608
		if (this.isDetailsLoaded()) {
609
			// delete components on the server side
610
			deleteComponentsPromise = deleteComponents(graphVersion + 1, newVertexList);
611
		} else {
612
			deleteComponentsPromise = $.when();
613
		}
614

  
615
		deleteComponentsPromise.then(function() {
616
			newVertexList.forEach(function(vertex) {
617
				// change details were loaded
618
				if (self.isDetailsLoaded()) {
619
					// remove edges connected with the vertex
620
					vertex.getInEdgeList().forEach(function(edge) {
621
						edge.remove();
622
					});
623
					vertex.getOutEdgeList().forEach(function(edge) {
624
						edge.remove();
625
					});
626

  
627
					var existingVertex = app.findVertexByName(vertex.name);
628
					if (app.utils.isDefined(existingVertex)) {
629
						// remove vertex from app
630
						app.nodeList.splice(app.nodeList.indexOf(existingVertex), 1);
631
						app.vertexList.splice(app.vertexList.indexOf(existingVertex), 1);
632
					}
633
				}
634

  
635
				// remove vertex from this change
636
				newVertexListComponent.removeChild(vertex);
637
			});
638

  
639
			newVertexList = [];
640

  
641
			self.setTriggered(false);
642
			self.setDetailsLoaded(false);
643

  
644
		}).always(function() {
645
			app.loader.disable();
646
		});
647
	}
648

  
649
	function moveToPostponed() {
650
		if (oldVertexList.length === 0) return;
651
		
652
		newVertexList.forEach(function(vertex) {
653
			// remove vertex from app
654
			var existingVertex = app.findVertexByName(vertex.name);
655
			if (app.utils.isDefined(existingVertex)) {
656
				app.nodeList.splice(app.nodeList.indexOf(existingVertex), 1);
657
				app.vertexList.splice(app.vertexList.indexOf(existingVertex), 1);
658
			}
659
		});
660

  
661
		this.setPostponed(true);
662
		this.remove();
663

  
664
		app.sidebarComponent.setChangePostponed(this);
665
	}
666

  
667
	function moveToActive() {
668
		newVertexList.forEach(function(vertex) {
669
			// add vertex to app
670
			app.nodeList.push(vertex);
671
			app.vertexList.push(vertex);
672
		});
673

  
674
		this.setPostponed(false);
675
		this.remove();
676

  
677
		app.sidebarComponent.setChangeActive(this);
678
	}
679

  
680
	function toggleButtonGroup() {
681
		if (oldVertexList.length > 0) {
682
			buttonGroup.classList.remove('hidden');
683
		} else {
684
			buttonGroup.classList.add('hidden');
685
		}
686
	}
687

  
688
	function getInvolvedComponents() {
689
		var involvedComponents = [];
690

  
691
		oldVertexList.forEach(function(vertex) {
692
			vertex.getInEdgeList().forEach(function(edge) {
693
				involvedComponents.push(edge.getFrom());
694
			});
695

  
696
			vertex.getOutEdgeList().forEach(function(edge) {
697
				involvedComponents.push(edge.getTo());
698
			});
699
		});
700

  
701
		return involvedComponents;
702
	}
703
}
sources/src/main/webapp/js/components/changeModalWindow.js
1
/**
2
 * Class representing a modal window displaying a change separately from viewport and sidebar.
3
 * @see Change
4
 * @constructor
5
 * @param {Change} change Change to be displayed in the modal window.
6
 */
7
function ChangeModalWindow(change) {
8
	var rootElement;
9

  
10
	var change;
11

  
12
	/**
13
	 * Sets a change to be displayed in the modal window.
14
	 * @param {Change} newValue Change to be displayed in this modal window.
15
	 */
16
	this.setChange = function(newValue) {
17
		change = newValue;
18
	};
19

  
20
	this.open = function() {
21
		rootElement.classList.remove('hidden');
22
	};
23

  
24
	/**
25
	 * Closes this modal window.
26
	 */
27
	this.close = function() {
28
		rootElement.classList.add('hidden');
29
	};
30

  
31
	/**
32
	 * Creates a new HTML DOM element representing the modal window in memory. Binds user interactions to local handler functions.
33
	 * @returns {Element} HTML DOM element.
34
	 */
35
	this.render = function() {
36
		rootElement = app.utils.createHtmlElement('div', {
37
			'class': 'change-modal modal hidden',
38
		});
39

  
40
		var modalContent = app.utils.createHtmlElement('div', {
41
			'class': 'modal-content',
42
		});
43
		rootElement.appendChild(modalContent);
44

  
45
		var closeButton = app.utils.createHtmlElement('button', {
46
			'class': 'close-button button',
47
		});
48
		closeButton.appendChild(app.utils.createTextElement('×'));
49
		closeButton.addEventListener('click', closeButtonClick.bind(this));
50
		modalContent.appendChild(closeButton);
51

  
52
		return rootElement;
53
	};
54

  
55
	/**
56
	 * Close button click interaction. Closes the modal window.
57
	 * @param {Event} e Click event.
58
	 */
59
	function closeButtonClick(e) {
60
		this.close();
61
	}
62
}
sources/src/main/webapp/js/components/changeVertexList.js
1
/**
2
 * Class representing a list of vertices in a change. It can be either the list of components to be changed or the list of proposals.
3
 * @see Change
4
 * @constructor
5
 * @param {Change} parentalChange Change this vertex list is bound to.
6
 */
7
function ChangeVertexList(parentalChange) {
8
	var rootElement;
9

  
10
	/**
11
	 * Adds a new vertex to the list.
12
	 * @param {vertex} vertex Vertex to be added to this list.
13
	 */
14
	this.appendChild = function(vertex) {
15
		if (parentalChange.isPostponed()) {
16
			var listItemElement = app.utils.createHtmlElement('li', {
17
				'class': 'node vertex',
18
				'data-id': vertex.id,
19
			});
20
			listItemElement.appendChild(document.createTextNode(vertex.name));
21

  
22
		} else {
23
			listItemElement = vertex.render();
24
		}
25

  
26
		rootElement.appendChild(listItemElement);
27
	};
28

  
29
	/**
30
	 * Removes a vertex from the list.
31
	 * @param {vertex} vertex Vertex to be removed from this list.
32
	 */
33
	this.removeChild = function(vertex) {
34
		var listItemElement = rootElement.querySelector('[data-id="' + vertex.id + '"]');
35

  
36
		listItemElement.remove();
37
	};
38

  
39
	/**
40
	 * Creates a new DOM element representing the list in memory.
41
	 * @returns {Element} HTML DOM element.
42
	 */
43
	this.render = function() {
44
		rootElement = app.utils.createHtmlElement('ul', {});
45
		rootElement.setAttribute('class', 'node-list');
46

  
47
		return rootElement;
48
	};
49
}
sources/src/main/webapp/js/components/edge.js
9 9

  
10 10
	var rootElement;
11 11

  
12
	var compatible = props.isCompatible;
13
	var compatibilityInfo = props.compInfoJSON ? JSON.parse(props.compInfoJSON) : [];
14

  
15 12
	var hidden = false;
16 13
	var dimmed = false;
17 14
	var highlighted = false;
......
108 105
		lollipop.setAttribute('transform', `rotate(${rotation}, ${position.x},${position.y}) translate(${position.x},${position.y})`);
109 106
	};
110 107

  
111
	/**
112
	 * @returns {boolean} True if the edge is compatible, otherwise false.
113
	 */
114
	this.isCompatible = function() {
115
		return compatible;
116
	};
117

  
118
	/**
119
	 * @returns {object} Information on compatibility of the two vertices this edge is connecting.
120
	 */
121
	this.getCompatibilityInfo = function() {
122
		return compatibilityInfo;
123
	};
124

  
125 108
	/**
126 109
	 * Toggles visibility of the edge.
127 110
	 * @param {boolean} newValue True to hide the edge, false to display it.
......
294 277
	 * @param {Event} e Click event.
295 278
	 */
296 279
	function click(e) {
297
		if (compatibilityInfo.length > 0) {
298
			app.viewportComponent.edgePopoverComponent.setContent(compatibilityInfo);
299
			app.viewportComponent.edgePopoverComponent.setPosition(new Coordinates(e.clientX, e.clientY));
300
			app.viewportComponent.edgePopoverComponent.open();
301
		}
302 280

  
303 281
		// unhighlight other edges
304 282
		app.edgeList.filter(function(edge) {
sources/src/main/webapp/js/components/sidebar.js
5 5
	var rootElement;
6 6
	var activeChangeElement;
7 7

  
8
	/** @prop {SidebarPostponedChangeList} postponedChangeListComponent */
9
	this.postponedChangeListComponent = null;
10 8
	/** @prop {SidebarUnconnectedNodeList} unconnectedNodeListComponent */
11 9
	this.unconnectedNodeListComponent = null;
12
	/** @prop {SidebarMissingClassList} missingClassListComponent */
13
	this.missingClassListComponent = null;
14 10
	/** @prop {SidebarExcludedNodeList} excludedNodeListComponent */
15 11
	this.excludedNodeListComponent = null;
16 12
	/** @prop {StatusBar} statusBarComponent */
17 13
	this.statusBarComponent = null;
18 14

  
19
	var activeChange = new Change;
20 15
	var floaterList = [];
21 16

  
22 17
	this.getFloaters = function() {
......
37 32
		floaterList.splice(floaterList.indexOf(floater), 1);
38 33
	};
39 34

  
40
	this.addToChange = function(node) {
41
		node.removeFromSidebarList();
42
		node.remove(true);
43

  
44
		activeChange.addVertex(node);
45
	};
46

  
47
	this.setChangeActive = function(change) {
48
		// postpone currently active change
49
		if (activeChange.getOldVertexList().length > 0) {
50
			activeChange.postpone();
51
			this.postponedChangeListComponent.add(activeChange);
52
		} else {
53
			activeChange.remove();
54
		}
55

  
56
		change.activate();
57
		activeChange = change;
58
		activeChangeElement.appendChild(activeChange.render());
59
	};
60

  
61
	this.setChangePostponed = function(change) {
62
		// postpone currently active change if there are some vertices in it
63
		if (change.getOldVertexList().length > 0) {
64
			this.postponedChangeListComponent.add(change);
65
		}
66

  
67
		// set a new active change
68
		activeChange = new Change;
69
		activeChangeElement.appendChild(activeChange.render());
70
	};
71

  
72 35
	this.render = function() {
73 36
		rootElement = app.utils.createHtmlElement('div', {
74 37
			'class': 'sidebar',
......
82 45
		});
83 46
		rootElement.appendChild(sidebarNav);
84 47

  
85
		// change
86
		var changeButton = app.utils.createHtmlElement('button', {
87
			'class': 'button',
88
			'id': 'changeButton',
89
			'title': 'Active change',
90
			'data-tooltip': 'top',
91
		});
92
		changeButton.appendChild(app.dom.createHtmlElement('img', {
93
			'src': 'images/tochange/crce-call-trans.gif',
94
			'alt': 'Icon of "toggle active change" action',
95
		}));
96
		changeButton.appendChild(app.dom.createTextElement('List'));
97
		changeButton.addEventListener('click', function() {
98
			document.getElementById('activeChange').classList.toggle('hidden');
99
			app.redrawEdges();
100
		});
101
		sidebarNav.appendChild(changeButton);
102

  
103
		// postponed
104
		var postponedButton = app.utils.createHtmlElement('button', {
105
			'class': 'button',
106
			'id': 'postponedButton',
107
			'title': 'Postponed changes',
108
			'data-tooltip': 'top',
109
		});
110
		postponedButton.appendChild(app.dom.createHtmlElement('img', {
111
			'src': 'images/tochange/postpone-trans.gif',
112
			'alt': 'Icon of "toggle postponed changes list" action',
113
		}));
114
		postponedButton.appendChild(app.dom.createTextElement('List'));
115
		postponedButton.addEventListener('click', function() {
116
			document.getElementById('postponedChangeListComponent').classList.toggle('hidden');
117
			app.redrawEdges();
118
		});
119
		sidebarNav.appendChild(postponedButton);
120
		
121 48
		// unconnected
122 49
		var unconnectedButton = app.utils.createHtmlElement('button', {
123 50
			'class': 'button',
......
136 63
		});
137 64
		sidebarNav.appendChild(unconnectedButton);
138 65
		
139
		// missing
140
		var missingButton = app.utils.createHtmlElement('button', {
141
			'class': 'button',
142
			'id': 'missingButton',
143
			'title': 'Missing classes',
144
			'data-tooltip': 'top-left',
145
		});
146
		missingButton.appendChild(app.dom.createHtmlElement('img', {
147
			'src': 'images/tochange/accept-trans.gif',
148
			'alt': 'Icon of "toggle missing classes list" action',
149
		}));
150
		missingButton.appendChild(app.dom.createTextElement('List'));
151
		missingButton.addEventListener('click', function() {
152
			document.getElementById('missingClassListComponent').classList.toggle('hidden');
153
			app.redrawEdges();
154
		});
155
		sidebarNav.appendChild(missingButton);
156

  
157 66

  
158 67
		var sidebarContainer = app.utils.createHtmlElement('div', {
159 68
			'class': 'sidebar-container',
160 69
		});
161 70
		rootElement.appendChild(sidebarContainer);
162 71

  
163
		// active change
164
		activeChangeElement = app.utils.createHtmlElement('div', {
165
			'id': 'activeChange',
166
			'class': 'node-container change-nodes',
167
		});
168
		activeChangeElement.appendChild(app.dom.htmlStringToElement('<h2 class="node-container-title">Active change</h2>'));
169
		activeChangeElement.appendChild(activeChange.render());
170

  
171
		sidebarContainer.appendChild(activeChangeElement);
172

  
173
		// postponed changes
174
		this.postponedChangeListComponent = new SidebarPostponedChangeList({
175
			'id': 'postponedChangeListComponent',
176
			'class': 'hidden',
177
		});
178
		sidebarContainer.appendChild(this.postponedChangeListComponent.render());
179

  
180 72
		// unconnected components
181 73
		this.unconnectedNodeListComponent = new SidebarUnconnectedNodeList({
182 74
			'id': 'unconnectedNodeListComponent',
......
184 76
		});
185 77
		sidebarContainer.appendChild(this.unconnectedNodeListComponent.render());
186 78

  
187
		// missing classes
188
		this.missingClassListComponent = new SidebarMissingComponentList({
189
			'id': 'missingClassListComponent',
190
			'class': 'hidden',
191
		});
192
		sidebarContainer.appendChild(this.missingClassListComponent.render());
193

  
194 79

  
195 80
		// excluded nodes
196 81
		this.excludedNodeListComponent = new SidebarExcludedNodeList({
......
208 93
	};
209 94

  
210 95
	this.reset = function() {
211
		// remove active change
212
		activeChange.remove();
213

  
214
		// set a new active change
215
		activeChange = new Change;
216
		activeChangeElement.appendChild(activeChange.render());
217

  
218 96
		// reset lists
219
		this.postponedChangeListComponent.reset();
220 97
		this.unconnectedNodeListComponent.reset();
221
		this.missingClassListComponent.reset();
222 98
		this.excludedNodeListComponent.reset();
223 99

  
224 100
		// reset status bar
sources/src/main/webapp/js/components/sidebarMissingComponentList.js
1
/**
2
 * @constructor
3
 * @param {object} props Properties of the components list.
4
 */
5
function SidebarMissingComponentList(props) {
6
	/** @prop {string} id Identifier of the component. */
7
	this.id = props.id;
8

  
9
	var rootElement;
10
	var componentListElement;
11

  
12
	var componentList = [];
13

  
14
	this.getComponentList = function() {
15
		return componentList;
16
	};
17

  
18
	this.add = function(componentName) {
19
		if (componentList.indexOf(componentName) > -1) return;
20

  
21
		componentList.push(componentName);
22

  
23
		var listItem = app.utils.createHtmlElement('li', {
24
			'title': componentName,
25
		});
26
		listItem.appendChild(document.createTextNode(componentName));
27
		componentListElement.appendChild(listItem);
28
	};
29

  
30
	this.render = function() {
31
		rootElement = app.utils.createHtmlElement('div', {
32
			'id': props.id,
33
			'class': 'node-container missing-components ' + (props.class ? props.class : ''),
34
		});
35
		rootElement.addEventListener('scroll', function() {
36
			app.redrawEdges();
37
		});
38

  
39
		// title
40
		rootElement.appendChild(app.dom.htmlStringToElement('<h2 class="node-container-title">Missing classes</h2>'));
41

  
42
		// component list
43
		componentListElement = app.utils.createHtmlElement('ul', {
44
			'class': 'node-list',
45
		});
46
		rootElement.appendChild(componentListElement);
47

  
48
		return rootElement;
49
	};
50

  
51
	this.reset = function() {
52
		componentList = [];
53

  
54
		$(componentListElement).empty();
55
	};
56
}
sources/src/main/webapp/js/components/sidebarPostponedChangeList.js
1
/**
2
 * @constructor
3
 * @param {object} props Properties of the change list.
4
 */
5
function SidebarPostponedChangeList(props) {
6
	/** @prop {string} id Identifier of the component. */
7
	this.id = props.id;
8

  
9
	var rootElement;
10
	var buttonGroup;
11
	var changeListElement;
12

  
13
	var changeList = [];
14

  
15
	this.getChangeList = function() {
16
		return changeList;
17
	};
18

  
19
	this.add = function(change) {
20
		if (!(change instanceof Change)) {
21
			throw new TypeError(change.toString() + 'is not instance of Change');
22
		}
23

  
24
		change.setPostponed(true);
25

  
26
		changeList.push(change);
27
		changeListElement.appendChild(change.render());
28

  
29
		app.redrawEdges();
30
	};
31

  
32
	this.remove = function(change) {
33
		if (!(change instanceof Change)) {
34
			throw new TypeError(change.toString() + 'is not instance of Change');
35
		}
36

  
37
		change.setPostponed(false);
38

  
39
		changeList.splice(changeList.indexOf(change), 1);
40
		change.remove();
41

  
42
		app.redrawEdges();
43
	};
44

  
45
	this.render = function() {
46
		rootElement = app.utils.createHtmlElement('div', {
47
			'id': props.id,
48
			'class': 'node-container postponed-nodes ' + (props.class ? props.class : ''),
49
		});
50
		rootElement.addEventListener('scroll', function() {
51
			app.redrawEdges();
52
		});
53

  
54
		// title
55
		rootElement.appendChild(app.dom.htmlStringToElement('<h2 class="node-container-title">Postponed changes</h2>'));
56

  
57
		// list
58
		changeListElement = app.utils.createHtmlElement('ul', {
59
			'class': 'change-list',
60
		});
61
		rootElement.appendChild(changeListElement);
62

  
63
		return rootElement;
64
	};
65

  
66
	this.reset = function() {
67
		console.log('TODO: should SidebarPostponedChangeList.reset() method do something?');
68
	};
69

  
70
	function includeAll() {
71
		var nodeListCopy = nodeList.slice(0);
72
		nodeListCopy.forEach(function(node) {
73
			node.include();
74
		}, this);
75

  
76
		toggleButtonGroup.call(this);
77
	}
78

  
79
	function toggleButtonGroup() {
80
		if (nodeList.length > 0) {
81
			buttonGroup.classList.remove('hidden');
82
		} else {
83
			buttonGroup.classList.add('hidden');
84
		}
85
	}
86
}
sources/src/main/webapp/js/components/statusBar.js
1 1
/**
2
 * Class representing the sidebar status bar. It displays number of components loaded in the diagram and the current graph version.
2
 * Class representing the sidebar status bar. It displays number of components loaded in the diagram.
3 3
 * @constructor
4 4
 */
5 5
function StatusBar() {
6 6
	var rootElement;
7 7
	var componentCounterElement;
8
	var graphVersionElement;
9 8

  
10 9
	/**
11 10
	 * Sets a new count of components loaded in the diagram.
......
16 15
		componentCounterElement.appendChild(app.utils.createTextElement('loaded components: ' + componentCount));
17 16
	};
18 17

  
19
	/**
20
	 * Sets a new graph version.
21
	 * @param {string|integer} graphVersion New graph version.
22
	 */
23
	this.setGraphVersion = function(graphVersion) {
24
		graphVersionElement.innerHTML = '';
25
		graphVersionElement.appendChild(app.utils.createTextElement('graph version: ' + graphVersion));
26
	};
27

  
28 18
	/**
29 19
	 * Creates a new DOM element representing the status bar in memory.
30 20
	 * @returns {Element} HTML DOM element.
......
39 29
		});
40 30
		rootElement.appendChild(componentCounterElement);
41 31

  
42
		graphVersionElement = app.utils.createHtmlElement('span', {
43
			'class': 'graph-version',
44
		});
45
		rootElement.appendChild(graphVersionElement);
46

  
47 32
		return rootElement;
48 33
	};
49 34

  
......
52 37
	 */
53 38
	this.reset = function() {
54 39
		componentCounterElement.innerHTML = '';
55
		graphVersionElement.innerHTML = '';
56 40
	};
57 41
}
sources/src/main/webapp/js/constants.js
3 3
 * @constructor
4 4
 */
5 5
function Constants() {
6
	/** @prop {string} crceApiBase CRCE API base path. */
7
	this.crceApiBase = 'http://localhost:8081/rest/v2';
8 6
	/** @prop {string} notFoundVertexName Name of the vertex that groups all components that were not found while constructing graph. */
9 7
	this.notFoundVertexName = 'NOT_FOUND';
10 8
}
sources/src/main/webapp/js/graphLoader.js
87 87
			app.sidebarComponent.unconnectedNodeListComponent.add(vertex);
88 88
		});
89 89

  
90
		// find missing components
91
		app.edgeList.filter(function(edge) {
92
			return edge.getFrom().name === app.constants.notFoundVertexName;
93
		}).forEach(function(edge) {
94
			var compatibilityInfoList = edge.getCompatibilityInfo();
95
			compatibilityInfoList.forEach(function(compatibilityInfo) {
96
				if (compatibilityInfo.incomps.length === 0) return;
97

  
98
				compatibilityInfo.incomps.forEach(function(incompatibility) {
99
					if (!incompatibility.desc.isIncompCause) return;
100

  
101
					app.sidebarComponent.missingClassListComponent.add(incompatibility.desc.name);
102
				});
103
			});
104
		});
105

  
106 90
		// update status bar
107 91
		app.sidebarComponent.statusBarComponent.setComponentCount(data.vertices.length);
108
		app.sidebarComponent.statusBarComponent.setGraphVersion(app.cookies.get('graphVersion'));
109 92
	};
110 93

  
111 94
}
sources/src/main/webapp/js/javaComponentChanger.js
1
/**
2
 * @constructor
3
 */
4
function JavaComponentChanger() {
5
	var javaClasses = {
6
		boolean: 'java.lang.Boolean',
7
		string: 'java.lang.String',
8
		list: 'java.util.List',
9
		set: 'java.util.Set',
10
	};
11

  
12
	var crceClasses = {
13
		package: 'crce.api.java.package',
14
		class: 'crce.api.java.class',
15
		method: 'crce.api.java.method',
16
		property: 'crce.api.java.property',
17
	};
18

  
19
	var ns = '';	// http://relisa.kiv.zcu.cz
20
	var xsi = 'http://www.w3.org/2001/XMLSchema-instance';
21
	var xsd = 'crce.xsd';
22

  
23
	var xmlDocument;
24
	var nodeCounter;
25

  
26
	var xmlParser = new DOMParser();
27
	var xmlSerializer = new XMLSerializer();
28

  
29
	/**
30
	 * Sends change requirements to CRCE.
31
	 * @param {array} components Components to be changed.
32
	 * @param {boolean} includeNotFound True if not found classes should be added as change requirements, otherwise false.
33
	 */
34
	this.run = function(components, includeNotFound) {
35
		if (components.length === 0) return;
36

  
37
		// initialize requirements XML to be sent to CRCE
38
		xmlDocument = constructXmlDocument(components, includeNotFound);
39

  
40
		console.log('CRCE request:', xmlDocument);
41

  
42
		// trigger change
43
		return $.ajax({
44
			type: 'POST',	// jQuery docs tells to use "method" but it doesn't work and always sends GET -> use "type" instead
45
			url: app.constants.crceApiBase + '/metadata/catalogue/',
46
			data: xmlSerializer.serializeToString(xmlDocument),
47
			contentType: 'application/xml',
48
			timeout: 180 * 1000,	// in milliseconds
49

  
50
		}).then(function(data, textStatus, jqXHR) {
51
			console.log('CRCE response:', data);
52

  
53
			var resourcesEl = data.childNodes[0];
54
			if (resourcesEl.childNodes.length === 0) {
55
				return $.Deferred().reject('CRCE did not find any resources fitting your requirements.').promise();
56
			}
57

  
58
			var proposals = [];
59

  
60
			resourcesEl.childNodes.forEach(function(resourceEl, index) {
61
				var proposal = {
62
					uuid: resourceEl.getAttribute('uuid'),
63
				};
64

  
65
				var capabilityEl = resourceEl.childNodes[0];
66
				capabilityEl.childNodes.forEach(function(attributeEl) {
67
					if (['name', 'external-id', 'version'].includes(attributeEl.getAttribute('name'))) {
68
						proposal[attributeEl.getAttribute('name')] = attributeEl.getAttribute('value');
69
					}
70
				});
71
				proposals.push(proposal);
72
			});
73

  
74
			return proposals;
75
		});
76
	};
77

  
78
	function constructXmlDocument(components, includeNotFound) {
79
		// initialize requirements XML to be sent to CRCE
80
		xmlDocument = document.implementation.createDocument(ns, 'requirements', null);
81
		//xmlDocument.documentElement.setAttributeNS(xsi, 'xsi:schemaLocation', ns + ' ' + xsd);
82

  
83
		nodeCounter = 0;
84

  
85
		// optimize returned results
86
		var optimizeByRequirementEl = xmlDocument.createElementNS(ns, 'requirement');
87
		optimizeByRequirementEl.setAttribute('namespace', 'result.optimize-by');
88

  
89
		var optimizeByFunctionAttributeEl = xmlDocument.createElementNS(ns, 'attribute');
90
		optimizeByFunctionAttributeEl.setAttribute('name', 'function-ID');
91
		optimizeByFunctionAttributeEl.setAttribute('type', javaClasses.string);
92
		optimizeByFunctionAttributeEl.setAttribute('value', 'cf-equal-cost');
93

  
94
		var optimizeByMethodAttributeEl = xmlDocument.createElementNS(ns, 'attribute');
95
		optimizeByMethodAttributeEl.setAttribute('name', 'method-ID');
96
		optimizeByMethodAttributeEl.setAttribute('type', javaClasses.string);
97
		optimizeByMethodAttributeEl.setAttribute('value', 'ro-ilp-direct-dependencies');
98

  
99
		optimizeByRequirementEl.appendChild(optimizeByFunctionAttributeEl);
100
		optimizeByRequirementEl.appendChild(optimizeByMethodAttributeEl);
101

  
102
		xmlDocument.documentElement.appendChild(optimizeByRequirementEl);
103

  
104
		// component does not have to fulfill all requirements
105
		var directiveEl = xmlDocument.createElementNS(ns, 'directive');
106
		directiveEl.setAttribute('name', 'operator');
107
		directiveEl.setAttribute('value', 'or');
108

  
109
		xmlDocument.documentElement.appendChild(directiveEl);
110

  
111
		// component requirements
112
		components.forEach(function(component) {
113
			var inEdgeList = component.getInEdgeList();
114
			if (!includeNotFound) {
115
				inEdgeList = inEdgeList.filter(function(edge) {
116
					return edge.getFrom().name !== app.constants.notFoundVertexName;
117
				});
118
			}
119

  
120
			if (inEdgeList.length === 0) return;
121

  
122
			// construct functionality requirements tree
123
			inEdgeList.forEach(function(edge) {
124
				var compatibilityInfoList = edge.getCompatibilityInfo();
125

  
126
				compatibilityInfoList.forEach(function(compatibilityInfo) {
127
					compatibilityInfo.incomps.forEach(function(incompatibility) {
128
						appendRequirementTree(xmlDocument.documentElement, incompatibility);
129
					});
130
				});
131
			});
132
		});
133

  
134
		return xmlDocument;
135
	}
136

  
137
	function appendRequirementTree(element, incompatibility) {
138
		var type = incompatibility.desc.type;
139

  
140
		if (app.utils.isUndefined(type)) {
141
			incompatibility.subtree.forEach(function(incompatibility) {
142
				appendRequirementTree(element, incompatibility);
143
			});
144

  
145
		} else {
146
			// add package for classes
147
			if (type === 'class') {
148
				var packageRequirementEl = xmlDocument.createElementNS(ns, 'requirement');
149
				packageRequirementEl.setAttribute('uuid', nodeCounter++);
150
				packageRequirementEl.setAttribute('namespace', crceClasses['package']);
151

  
152
				var packageAttributeEl = xmlDocument.createElementNS(ns, 'attribute');
153
				packageAttributeEl.setAttribute('name', 'name');
154
				packageAttributeEl.setAttribute('type', javaClasses.string);
155
				packageAttributeEl.setAttribute('value', incompatibility.desc.details.package);
156

  
157
				packageRequirementEl.appendChild(packageAttributeEl);
158
			}
159

  
160
			var requirementEl = xmlDocument.createElementNS(ns, 'requirement');
161
			requirementEl.setAttribute('uuid', nodeCounter++);
... Rozdílový soubor je zkrácen, protože jeho délka přesahuje max. limit.

Také k dispozici: Unified diff