Projekt

Obecné

Profil

« Předchozí | Další » 

Revize 98b06149

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

renamed App to ShowGraphApp

Zobrazit rozdíly:

sources/src/main/webapp/js/app.js
1
/**
2
 * Main class of the application.
3
 * @constructor
4
 */
5
function App() {
6
	/** @prop {Constants} constants */
7
	this.constants = new Constants;
8
	/** @prop {GraphLoader} graphLoader */
9
	this.graphLoader = new GraphLoader;
10
	/** @prop {GraphExporter} graphExporter */
11
	this.graphExporter = new GraphExporter;
12
	/** @prop {Loader} loader */
13
	this.loader = new Loader;
14
	/** @prop {Zoom} zoom */
15
	this.zoom = new Zoom(0.8);
16
	/** @prop {MarkSymbol} markSymbol */
17
	this.markSymbol = new MarkSymbol;
18

  
19
	/** @prop {string} NAME Application name. */
20
	this.NAME = document.title;
21
	/** @prop {string} HOME_URL Application home URL. */
22
	this.HOME_URL = null;
23
	/** @prop {object} API Application programming interface paths. */
24
	this.API = {
25
		loadGraphData: 'api/load-graph-data',
26
		getDiagram: 'api/get-diagram',
27
		saveDiagram: 'api/save-diagram',
28
		removeDiagram: 'api/remove-diagram',
29
	};
30

  
31
	/** @prop {float} headerHeight Current height of the application header. */
32
	this.headerHeight = getHeaderHeight();
33

  
34
	/** @prop {Sidebar} sidebarComponent */
35
	this.sidebarComponent = null;
36
	/** @prop {Viewport} viewportComponent */
37
	this.viewportComponent = null;
38
	/** @prop {ModalWindow} modalWindowComponent */
39
	this.modalWindowComponent = null;
40

  
41
	/** @prop {array<Edge>} edgeList */
42
	this.edgeList = [];
43
	/** @prop {array<(Vertex|Group)>} nodeList */
44
	this.nodeList = [];
45
	/** @prop {array<Vertex>} vertexList */
46
	this.vertexList = [];
47
	/** @prop {array<Group>} groupList */
48
	this.groupList = [];
49

  
50
	/** @prop {Diagram} diagram */
51
	this.diagram = null;
52

  
53
	/** @prop {object} archetype Object containing all vertex and edge archetypes used in currently displayed diagram. */
54
	this.archetype = {
55
		vertex: [],
56
		edge: [],
57
	};
58

  
59
	/** @prop {array} attributeTypeList Array containing all possible types of vertex/edge attributes. */
60
	this.attributeTypeList = [];
61
	/** @prop {array} possibleEnumValues Array containing all possible values of attributes with datatype ENUM. */
62
	this.possibleEnumValues = {};
63

  
64
	/**
65
	 * Loads graph using diagram (if available).
66
	 * @param diagramId Diagram identifier.
67
	 */
68
	this.diagramLoader = function(diagramId) {
69
		return loadGraphData.bind(this, diagramId);
70
	};
71

  
72
	/**
73
	 * Initiates the application.
74
	 * @param {function} startFn Function to be run to load graph data.
75
	 */
76
	this.run = function(startFn) {
77
		console.log('running...');
78

  
79
		bootstrap.call(this);
80
		startFn.call(this);
81
	};
82

  
83
	/**
84
	 * Resets the application to the state as it was when the graph was loaded for the first time.
85
	 */
86
	this.reset = function() {
87
		app.viewportComponent.reset();
88
		app.sidebarComponent.reset();
89

  
90
		app.edgeList = [];
91
		app.nodeList = [];
92
		app.vertexList = [];
93
		app.groupList = [];
94
	};
95

  
96
	/**
97
	 * Finds a vertex by its name.
98
	 * @param {string} name Name of the searched vertex.
99
	 */
100
	this.findVertexByName = function(name) {
101
		return app.vertexList.find(function(existingVertex) {
102
			return existingVertex.name == this;
103
		}, name);
104
	}
105

  
106
	/**
107
	 * Closes components floating above viewport (context menu and popovers).
108
	 */
109
	this.closeFloatingComponents = function() {
110
		app.viewportComponent.contextMenuComponent.close();
111
		app.viewportComponent.vertexPopoverComponent.close();
112
		app.viewportComponent.edgePopoverComponent.close();
113
	};
114

  
115
	/**
116
	 * Redraws edges leading from viewport to sidebar.
117
	 */
118
	this.redrawEdges = function() {
119
		app.sidebarComponent.refreshFloaters();
120
	};
121

  
122
	/**
123
	 * Binds user interactions to local handler functions.
124
	 */
125
	function bootstrap() {
126
		var self = this;
127

  
128
		self.loader.enable();
129

  
130
		var content = document.getElementById('content');
131

  
132
		self.viewportComponent = new Viewport;
133
		content.appendChild(self.viewportComponent.render());
134

  
135
		self.sidebarComponent = new Sidebar;
136
		content.appendChild(self.sidebarComponent.render());
137
		self.sidebarComponent.minimapComponent.setViewportSize(self.viewportComponent.getSize());
138

  
139
		self.modalWindowComponent = new ModalWindow;
140
		content.appendChild(self.modalWindowComponent.render());
141

  
142
		// context menu
143
		document.body.addEventListener('mousedown', function() {
144
			self.closeFloatingComponents();
145
		});
146

  
147
		// zoom
148
		document.getElementById('zoomIn').addEventListener('click', function(e) {
149
			self.zoom.zoomIn();
150
		});
151
		
152
		document.getElementById('zoomOut').addEventListener('click', function(e) {
153
			self.zoom.zoomOut();
154
		});
155

  
156
		document.getElementById('zoomValue').innerText = Math.round(self.zoom.scale * 100) + '%';
157
		document.getElementById('graph').setAttribute('transform', 'scale(' + self.zoom.scale + ')');
158

  
159
		// search
160
		document.getElementById('searchText').addEventListener('keyup', function(e) {
161
			// enter key
162
			if (e.keyCode === 13) {
163
				search(this.value);
164
				return;
165
			}
166

  
167
			// escape key
168
			if (e.keyCode === 27) {
169
				resetSearch();
170
				return;
171
			}
172
		});
173
		
174
		document.getElementById('search').addEventListener('click', function(e) {
175
			search(document.getElementById('searchText').value);
176
		});
177
		
178
		document.getElementById('countOfFound').addEventListener('click', resetSearch);
179
		
180
		function search(term) {
181
			if (term.length < 2) return;
182

  
183
			var found = 0;
184
			
185
			var nodeList = self.viewportComponent.getNodeList();
186
			nodeList.forEach(function(node) {
187
				if (!node.name.toLowerCase().includes(term.toLowerCase())) {
188
					node.setFound(false);
189

  
190
				} else {
191
					node.setFound(true);
192

  
193
					found++;
194
				}
195
			});
196
			
197
			document.getElementById('countOfFound').innerText = found;
198
		}
199

  
200
		function resetSearch(e) {
201
			var nodeList = self.viewportComponent.getNodeList();
202
			nodeList.forEach(function(node) {
203
				node.setFound(false);
204
			});
205

  
206
			document.getElementById('searchText').value = '';
207
			document.getElementById('countOfFound').innerText = 0;
208
		}
209
		
210
		// exclude vertices with most edges button
211
		document.getElementById('mostEdge').addEventListener('click', function(e) {
212
			var vertexList = self.viewportComponent.getVertexList();
213
			if (vertexList.length === 0) return;
214

  
215
			var vertexWithMostEdges = vertexList.reduce(function(prev, vertex) {
216
				return vertex.countEdges() > prev.countEdges() ? vertex : prev;
217
			});
218
			
219
			if (vertexWithMostEdges !== null) {
220
				vertexWithMostEdges.exclude();
221
				self.sidebarComponent.excludedNodeListComponent.add(vertexWithMostEdges);
222
			}
223
		});
224
		
225
		// exclude vertices with most edges to group button
226
		document.getElementById('vertexToGroup').addEventListener('click', function(e) {
227
			var vertexList = self.viewportComponent.getVertexList();
228
			if (vertexList.length === 0) return;
229

  
230
			var vertexWithMostEdges = vertexList.reduce(function(prev, vertex) {
231
				return vertex.countEdges() > prev.countEdges() ? vertex : prev;
232
			});
233

  
234
			var verticesWithMostEdges = vertexList.filter(function(vertex) {
235
				return vertex.countEdges() === vertexWithMostEdges.countEdges();
236
			});
237

  
238
			if (verticesWithMostEdges.length > 0) {
239
				var group = new Group({});
240

  
241
				verticesWithMostEdges.forEach(function(vertex) {
242
					group.addVertex(vertex);
243
				});
244

  
245
				self.nodeList.push(group);
246
				self.groupList.push(group);
247

  
248
				self.viewportComponent.addGroup(group);
249
			}
250
		});
251

  
252
		// apply force-directed layout
253
		var layouting = false;
254
		var layoutingInterval;
255

  
256
		document.getElementById('applyLayout').addEventListener('click', function() {
257
			if (layouting) {
258
				document.getElementById('applyLayoutImg').setAttribute('src', 'images/layout_off.png');
259

  
260
				layouting = false;
261
				clearInterval(layoutingInterval);
262

  
263
			} else {
264
				document.getElementById('applyLayoutImg').setAttribute('src', 'images/layout_on.png');
265

  
266
				layouting = true;
267
				layoutingInterval = window.setInterval(self.viewportComponent.forceDirected.run, 10);
268
			}
269
		});
270
		
271
		// save as PNG button
272
		document.getElementById('btnSaveDiagram').addEventListener('click', function(e) {
273
			saveSvgAsPng(document.getElementById('svg1'), 'diagram.png', {
274
				scale: 1,
275
			});
276
		});
277

  
278
		// save to database button
279
		document.getElementById('btnSaveDiagramToDatabase').addEventListener('click', function(e) {
280
			self.modalWindowComponent.open();
281
		});
282

  
283
		// window resize
284
		window.addEventListener('resize', function(e) {
285
			self.headerHeight = getHeaderHeight();
286
			self.redrawEdges();
287

  
288
			self.sidebarComponent.minimapComponent.setViewportSize(self.viewportComponent.getSize());
289
		});
290
	}
291

  
292
	/**
293
	 * Loads graph data of a diagram.
294
	 * @param {string} diagramId Identifier of the diagram to be loaded.
295
	 */
296
	async function loadGraphData(diagramId) {
297
		this.loader.enable();
298

  
299
		let loadGraphDataPromise;
300

  
301
		if (diagramId === '') {
302
			loadGraphDataPromise = AJAX.getJSON(this.API.loadGraphData);
303

  
304
		} else {
305
			const diagramData = await AJAX.getJSON(this.API.getDiagram + '?id=' + diagramId);
306

  
307
			this.diagram = new Diagram(diagramData);
308

  
309
			document.title = this.NAME + ' - ' + this.diagram.name;
310

  
311
			loadGraphDataPromise = Promise.resolve(JSON.parse(diagramData.graph_json));
312
		}
313

  
314
		try {
315
			// get vertex position data
316
			const graphData = await loadGraphDataPromise;
317

  
318
			// construct graph
319
			this.graphLoader.run(graphData);
320

  
321
			this.loader.disable();
322

  
323
		} catch (error) {
324
			if (error instanceof HttpError) {
325
				switch (error.response.status) {
326
					case 401:
327
						alert('You are not allowed to view the diagram.');
328
						break;
329
					default:
330
						alert('Something went wrong.');
331
				}
332
			} else {
333
				alert('Server has probably gone away.');
334
			}
335

  
336
			// go to the upload page
337
			window.location.replace('./');
338
		}
339
	}
340

  
341
	/**
342
	 * @returns {integer} Height of the header.
343
	 */
344
	function getHeaderHeight() {
345
		return document.getElementById('header').offsetHeight;
346
	}
347
}
sources/src/main/webapp/js/showGraphApp.js
1
/**
2
 * Main class of the application.
3
 * @constructor
4
 */
5
function ShowGraphApp() {
6
	/** @prop {Constants} constants */
7
	this.constants = new Constants;
8
	/** @prop {GraphLoader} graphLoader */
9
	this.graphLoader = new GraphLoader;
10
	/** @prop {GraphExporter} graphExporter */
11
	this.graphExporter = new GraphExporter;
12
	/** @prop {Loader} loader */
13
	this.loader = new Loader;
14
	/** @prop {Zoom} zoom */
15
	this.zoom = new Zoom(0.8);
16
	/** @prop {MarkSymbol} markSymbol */
17
	this.markSymbol = new MarkSymbol;
18

  
19
	/** @prop {string} NAME Application name. */
20
	this.NAME = document.title;
21
	/** @prop {string} HOME_URL Application home URL. */
22
	this.HOME_URL = null;
23
	/** @prop {object} API Application programming interface paths. */
24
	this.API = {
25
		loadGraphData: 'api/load-graph-data',
26
		getDiagram: 'api/get-diagram',
27
		saveDiagram: 'api/save-diagram',
28
		removeDiagram: 'api/remove-diagram',
29
	};
30

  
31
	/** @prop {float} headerHeight Current height of the application header. */
32
	this.headerHeight = getHeaderHeight();
33

  
34
	/** @prop {Sidebar} sidebarComponent */
35
	this.sidebarComponent = null;
36
	/** @prop {Viewport} viewportComponent */
37
	this.viewportComponent = null;
38
	/** @prop {ModalWindow} modalWindowComponent */
39
	this.modalWindowComponent = null;
40

  
41
	/** @prop {array<Edge>} edgeList */
42
	this.edgeList = [];
43
	/** @prop {array<(Vertex|Group)>} nodeList */
44
	this.nodeList = [];
45
	/** @prop {array<Vertex>} vertexList */
46
	this.vertexList = [];
47
	/** @prop {array<Group>} groupList */
48
	this.groupList = [];
49

  
50
	/** @prop {Diagram} diagram */
51
	this.diagram = null;
52

  
53
	/** @prop {object} archetype Object containing all vertex and edge archetypes used in currently displayed diagram. */
54
	this.archetype = {
55
		vertex: [],
56
		edge: [],
57
	};
58

  
59
	/** @prop {array} attributeTypeList Array containing all possible types of vertex/edge attributes. */
60
	this.attributeTypeList = [];
61
	/** @prop {array} possibleEnumValues Array containing all possible values of attributes with datatype ENUM. */
62
	this.possibleEnumValues = {};
63

  
64
	/**
65
	 * Loads graph using diagram (if available).
66
	 * @param diagramId Diagram identifier.
67
	 */
68
	this.diagramLoader = function(diagramId) {
69
		return loadGraphData.bind(this, diagramId);
70
	};
71

  
72
	/**
73
	 * Initiates the application.
74
	 * @param {function} startFn Function to be run to load graph data.
75
	 */
76
	this.run = function(startFn) {
77
		console.log('running...');
78

  
79
		bootstrap.call(this);
80
		startFn.call(this);
81
	};
82

  
83
	/**
84
	 * Resets the application to the state as it was when the graph was loaded for the first time.
85
	 */
86
	this.reset = function() {
87
		app.viewportComponent.reset();
88
		app.sidebarComponent.reset();
89

  
90
		app.edgeList = [];
91
		app.nodeList = [];
92
		app.vertexList = [];
93
		app.groupList = [];
94
	};
95

  
96
	/**
97
	 * Finds a vertex by its name.
98
	 * @param {string} name Name of the searched vertex.
99
	 */
100
	this.findVertexByName = function(name) {
101
		return app.vertexList.find(function(existingVertex) {
102
			return existingVertex.name == this;
103
		}, name);
104
	}
105

  
106
	/**
107
	 * Closes components floating above viewport (context menu and popovers).
108
	 */
109
	this.closeFloatingComponents = function() {
110
		app.viewportComponent.contextMenuComponent.close();
111
		app.viewportComponent.vertexPopoverComponent.close();
112
		app.viewportComponent.edgePopoverComponent.close();
113
	};
114

  
115
	/**
116
	 * Redraws edges leading from viewport to sidebar.
117
	 */
118
	this.redrawEdges = function() {
119
		app.sidebarComponent.refreshFloaters();
120
	};
121

  
122
	/**
123
	 * Binds user interactions to local handler functions.
124
	 */
125
	function bootstrap() {
126
		var self = this;
127

  
128
		self.loader.enable();
129

  
130
		var content = document.getElementById('content');
131

  
132
		self.viewportComponent = new Viewport;
133
		content.appendChild(self.viewportComponent.render());
134

  
135
		self.sidebarComponent = new Sidebar;
136
		content.appendChild(self.sidebarComponent.render());
137
		self.sidebarComponent.minimapComponent.setViewportSize(self.viewportComponent.getSize());
138

  
139
		self.modalWindowComponent = new ModalWindow;
140
		content.appendChild(self.modalWindowComponent.render());
141

  
142
		// context menu
143
		document.body.addEventListener('mousedown', function() {
144
			self.closeFloatingComponents();
145
		});
146

  
147
		// zoom
148
		document.getElementById('zoomIn').addEventListener('click', function(e) {
149
			self.zoom.zoomIn();
150
		});
151
		
152
		document.getElementById('zoomOut').addEventListener('click', function(e) {
153
			self.zoom.zoomOut();
154
		});
155

  
156
		document.getElementById('zoomValue').innerText = Math.round(self.zoom.scale * 100) + '%';
157
		document.getElementById('graph').setAttribute('transform', 'scale(' + self.zoom.scale + ')');
158

  
159
		// search
160
		document.getElementById('searchText').addEventListener('keyup', function(e) {
161
			// enter key
162
			if (e.keyCode === 13) {
163
				search(this.value);
164
				return;
165
			}
166

  
167
			// escape key
168
			if (e.keyCode === 27) {
169
				resetSearch();
170
				return;
171
			}
172
		});
173
		
174
		document.getElementById('search').addEventListener('click', function(e) {
175
			search(document.getElementById('searchText').value);
176
		});
177
		
178
		document.getElementById('countOfFound').addEventListener('click', resetSearch);
179
		
180
		function search(term) {
181
			if (term.length < 2) return;
182

  
183
			var found = 0;
184
			
185
			var nodeList = self.viewportComponent.getNodeList();
186
			nodeList.forEach(function(node) {
187
				if (!node.name.toLowerCase().includes(term.toLowerCase())) {
188
					node.setFound(false);
189

  
190
				} else {
191
					node.setFound(true);
192

  
193
					found++;
194
				}
195
			});
196
			
197
			document.getElementById('countOfFound').innerText = found;
198
		}
199

  
200
		function resetSearch(e) {
201
			var nodeList = self.viewportComponent.getNodeList();
202
			nodeList.forEach(function(node) {
203
				node.setFound(false);
204
			});
205

  
206
			document.getElementById('searchText').value = '';
207
			document.getElementById('countOfFound').innerText = 0;
208
		}
209
		
210
		// exclude vertices with most edges button
211
		document.getElementById('mostEdge').addEventListener('click', function(e) {
212
			var vertexList = self.viewportComponent.getVertexList();
213
			if (vertexList.length === 0) return;
214

  
215
			var vertexWithMostEdges = vertexList.reduce(function(prev, vertex) {
216
				return vertex.countEdges() > prev.countEdges() ? vertex : prev;
217
			});
218
			
219
			if (vertexWithMostEdges !== null) {
220
				vertexWithMostEdges.exclude();
221
				self.sidebarComponent.excludedNodeListComponent.add(vertexWithMostEdges);
222
			}
223
		});
224
		
225
		// exclude vertices with most edges to group button
226
		document.getElementById('vertexToGroup').addEventListener('click', function(e) {
227
			var vertexList = self.viewportComponent.getVertexList();
228
			if (vertexList.length === 0) return;
229

  
230
			var vertexWithMostEdges = vertexList.reduce(function(prev, vertex) {
231
				return vertex.countEdges() > prev.countEdges() ? vertex : prev;
232
			});
233

  
234
			var verticesWithMostEdges = vertexList.filter(function(vertex) {
235
				return vertex.countEdges() === vertexWithMostEdges.countEdges();
236
			});
237

  
238
			if (verticesWithMostEdges.length > 0) {
239
				var group = new Group({});
240

  
241
				verticesWithMostEdges.forEach(function(vertex) {
242
					group.addVertex(vertex);
243
				});
244

  
245
				self.nodeList.push(group);
246
				self.groupList.push(group);
247

  
248
				self.viewportComponent.addGroup(group);
249
			}
250
		});
251

  
252
		// apply force-directed layout
253
		var layouting = false;
254
		var layoutingInterval;
255

  
256
		document.getElementById('applyLayout').addEventListener('click', function() {
257
			if (layouting) {
258
				document.getElementById('applyLayoutImg').setAttribute('src', 'images/layout_off.png');
259

  
260
				layouting = false;
261
				clearInterval(layoutingInterval);
262

  
263
			} else {
264
				document.getElementById('applyLayoutImg').setAttribute('src', 'images/layout_on.png');
265

  
266
				layouting = true;
267
				layoutingInterval = window.setInterval(self.viewportComponent.forceDirected.run, 10);
268
			}
269
		});
270
		
271
		// save as PNG button
272
		document.getElementById('btnSaveDiagram').addEventListener('click', function(e) {
273
			saveSvgAsPng(document.getElementById('svg1'), 'diagram.png', {
274
				scale: 1,
275
			});
276
		});
277

  
278
		// save to database button
279
		document.getElementById('btnSaveDiagramToDatabase').addEventListener('click', function(e) {
280
			self.modalWindowComponent.open();
281
		});
282

  
283
		// window resize
284
		window.addEventListener('resize', function(e) {
285
			self.headerHeight = getHeaderHeight();
286
			self.redrawEdges();
287

  
288
			self.sidebarComponent.minimapComponent.setViewportSize(self.viewportComponent.getSize());
289
		});
290
	}
291

  
292
	/**
293
	 * Loads graph data of a diagram.
294
	 * @param {string} diagramId Identifier of the diagram to be loaded.
295
	 */
296
	async function loadGraphData(diagramId) {
297
		this.loader.enable();
298

  
299
		let loadGraphDataPromise;
300

  
301
		if (diagramId === '') {
302
			loadGraphDataPromise = AJAX.getJSON(this.API.loadGraphData);
303

  
304
		} else {
305
			const diagramData = await AJAX.getJSON(this.API.getDiagram + '?id=' + diagramId);
306

  
307
			this.diagram = new Diagram(diagramData);
308

  
309
			document.title = this.NAME + ' - ' + this.diagram.name;
310

  
311
			loadGraphDataPromise = Promise.resolve(JSON.parse(diagramData.graph_json));
312
		}
313

  
314
		try {
315
			// get vertex position data
316
			const graphData = await loadGraphDataPromise;
317

  
318
			// construct graph
319
			this.graphLoader.run(graphData);
320

  
321
			this.loader.disable();
322

  
323
		} catch (error) {
324
			if (error instanceof HttpError) {
325
				switch (error.response.status) {
326
					case 401:
327
						alert('You are not allowed to view the diagram.');
328
						break;
329
					default:
330
						alert('Something went wrong.');
331
				}
332
			} else {
333
				alert('Server has probably gone away.');
334
			}
335

  
336
			// go to the upload page
337
			window.location.replace('./');
338
		}
339
	}
340

  
341
	/**
342
	 * @returns {integer} Height of the header.
343
	 */
344
	function getHeaderHeight() {
345
		return document.getElementById('header').offsetHeight;
346
	}
347
}
sources/src/main/webapp/showGraph.jsp
60 60
		<script src="js/userMenu.js"></script>
61 61
		<script src="js/markSymbol.js"></script>
62 62
		<script src="js/constants.js"></script>
63
		<script src="js/app.js"></script>
63
		<script src="js/showGraphApp.js"></script>
64 64

  
65 65
		<title>IMiGEr</title>
66 66
	</head>
......
178 178
		</div>
179 179

  
180 180
		<script>
181
		var app = new App;
181
		var app = new ShowGraphApp;
182 182
		app.HOME_URL = '${HOME_URL}';
183 183

  
184 184
		document.addEventListener('DOMContentLoaded', function() {

Také k dispozici: Unified diff