commit 716a1d9ed374a790e24e9e80385bb49afb2d8734
Author: Pavel Fidranský <fidransky@outlook.com>
Date:   Mon Oct 29 19:33:17 2018 +0100

    reworked Sidebar and its lists to ES6 classes

diff --git a/sources/src/main/webapp/js/components/navbar.js b/sources/src/main/webapp/js/components/navbar.js
index 58e6f6b..b7e4faf 100644
--- a/sources/src/main/webapp/js/components/navbar.js
+++ b/sources/src/main/webapp/js/components/navbar.js
@@ -226,7 +226,7 @@ class Navbar {
 
 					if (vertexWithMostEdges !== null) {
 						vertexWithMostEdges.exclude();
-						app.sidebarComponent.excludedNodeListComponent.add(vertexWithMostEdges);
+						app.sidebarComponent.excludedNodeListComponent.addNode(vertexWithMostEdges);
 					}
 				},
 			}, [
diff --git a/sources/src/main/webapp/js/components/node.js b/sources/src/main/webapp/js/components/node.js
index 4bb7525..32b225b 100644
--- a/sources/src/main/webapp/js/components/node.js
+++ b/sources/src/main/webapp/js/components/node.js
@@ -278,13 +278,13 @@ class Node {
 		if (this instanceof Vertex && this._group !== null) return;
 
 		if (newValue) {
-			// set floater
+			// set proxy
 			this._proxy = new NodeProxy(this);
-			app.sidebarComponent.addFloater(this._proxy);
+			app.proxyList.push(this._proxy);
 
 		} else {
-			// remove floater
-			app.sidebarComponent.removeFloater(this._proxy);
+			// remove proxy
+			app.proxyList.splice(app.proxyList.indexOf(this._proxy), 1);
 			this._proxy = null;
 		}
 	}
@@ -399,7 +399,7 @@ class Node {
 				case 'exclude':
 					this.exclude();
 
-					app.sidebarComponent.excludedNodeListComponent.add(this);
+					app.sidebarComponent.excludedNodeListComponent.addNode(this);
 					break;
 			}
 		}
diff --git a/sources/src/main/webapp/js/components/sidebar.js b/sources/src/main/webapp/js/components/sidebar.js
index 62428f1..445645f 100644
--- a/sources/src/main/webapp/js/components/sidebar.js
+++ b/sources/src/main/webapp/js/components/sidebar.js
@@ -1,109 +1,67 @@
-/**
- * @constructor
- */
-function Sidebar() {
-	var rootElement;
-
-	/** @prop {SidebarUnconnectedNodeList} unconnectedNodeListComponent */
-	this.unconnectedNodeListComponent = null;
-	/** @prop {SidebarExcludedNodeList} excludedNodeListComponent */
-	this.excludedNodeListComponent = null;
-	/** @prop {Minimap} minimapComponent */
-	this.minimapComponent = null;
-	/** @prop {StatusBar} statusBarComponent */
-	this.statusBarComponent = null;
-
-	var floaterList = [];
-
-	this.getFloaters = function() {
-		return floaterList;
-	};
-
-	this.refreshFloaters = function() {
-		floaterList.forEach(floater => {
-			floater.updatePosition();
-		});
-	};
-
-	this.addFloater = function(floater) {
-		floaterList.push(floater);
-	};
-
-	this.removeFloater = function(floater) {
-		floaterList.splice(floaterList.indexOf(floater), 1);
-	};
-
-	this.render = function() {
-		rootElement = DOM.createHtmlElement('div', {
-			'class': 'sidebar',
-			'id': 'sidebar',
-		});
-
-		var sidebarNav = DOM.createHtmlElement('nav', {
-			'class': 'sidebar-navbar',
-			'id': 'uploadMenu',
-		});
-		rootElement.appendChild(sidebarNav);
-
-		// unconnected
-		var unconnectedButton = DOM.createHtmlElement('button', {
-			'class': 'button',
-			'id': 'unconnectedButton',
-			'title': 'Unconnected vertices',
-		});
-		unconnectedButton.appendChild(DOM.createHtmlElement('img', {
-			'src': 'images/tochange/unconnected.gif',
-			'alt': 'Icon of "toggle unconnected vertices list" action',
-		}));
-		unconnectedButton.appendChild(DOM.createTextElement('Unconnected vertices'));
-		unconnectedButton.addEventListener('click', function() {
-			document.getElementById('unconnectedNodeListComponent').classList.toggle('hidden');
-			app.redrawEdges();
-		});
-		sidebarNav.appendChild(unconnectedButton);
-		
-
-		var sidebarContainer = DOM.createHtmlElement('div', {
-			'class': 'sidebar-container',
-		});
-		rootElement.appendChild(sidebarContainer);
-
-		// unconnected components
-		this.unconnectedNodeListComponent = new SidebarUnconnectedNodeList({
-			'id': 'unconnectedNodeListComponent',
-			'class': 'hidden',
-		});
-		sidebarContainer.appendChild(this.unconnectedNodeListComponent.render());
-
-		// excluded nodes
-		this.excludedNodeListComponent = new SidebarExcludedNodeList({
-			'id': 'excludedNodeListComponent',
-		});
-		rootElement.appendChild(this.excludedNodeListComponent.render());
-
-
-		var sidebarBottom = DOM.createHtmlElement('div', {
-			'class': 'sidebar-bottom',
-		});
-		rootElement.appendChild(sidebarBottom);
-
-		// minimap
-		this.minimapComponent = new Minimap('#graph');
-		sidebarBottom.appendChild(this.minimapComponent.render());
-
-		// status bar
+class Sidebar {
+	/**
+	 * @constructor
+	 */
+	constructor() {
+		/** @prop {SidebarUnconnectedNodeList} unconnectedNodeListComponent */
+		this.unconnectedNodeListComponent = new SidebarUnconnectedNodeList;
+		/** @prop {SidebarExcludedNodeList} excludedNodeListComponent */
+		this.excludedNodeListComponent = new SidebarExcludedNodeList;
+		/** @prop {Minimap} minimapComponent */
+		this.minimapComponent = new Minimap;
+		/** @prop {StatusBar} statusBarComponent */
 		this.statusBarComponent = new StatusBar;
-		sidebarBottom.appendChild(this.statusBarComponent.render());
-
-		return rootElement;
-	};
-
-	this.reset = function() {
+	}
+
+	render() {
+		this._rootElement = DOM.h('div', {
+			class: 'sidebar',
+			id: 'sidebar',
+		}, [
+			DOM.h('nav', {
+				class: 'sidebar-navbar',
+				id: 'uploadMenu',
+			}, [
+				// toggle unconnected list button
+				DOM.h('button', {
+					class: 'button',
+					id: 'unconnectedButton',
+					title: 'Unconnected nodes',
+					innerText: 'Unconnected nodes',
+					onClick: () => {
+						this.unconnectedNodeListComponent.toggle();
+						app.redrawEdges();
+					},
+				}, [
+					DOM.h('img', {
+						src: 'images/tochange/unconnected.gif',
+						alt: 'Icon of "toggle unconnected nodes list" action',
+					}),
+				]),
+			]),
+			DOM.h('div', {
+				class: 'sidebar-container',
+			}, [
+				this.unconnectedNodeListComponent.render(),
+			]),
+			this.excludedNodeListComponent.render(),
+			DOM.h('div', {
+				class: 'sidebar-bottom',
+			}, [
+				this.minimapComponent.render(),
+				this.statusBarComponent.render(),
+			]),
+		]);
+
+		return this._rootElement;
+	}
+
+	reset() {
 		// reset lists
 		this.unconnectedNodeListComponent.reset();
 		this.excludedNodeListComponent.reset();
 
 		// reset status bar
 		this.statusBarComponent.reset();
-	};
+	}
 }
diff --git a/sources/src/main/webapp/js/components/sidebarExcludedNodeList.js b/sources/src/main/webapp/js/components/sidebarExcludedNodeList.js
index 0b3d4d4..d71bf9f 100644
--- a/sources/src/main/webapp/js/components/sidebarExcludedNodeList.js
+++ b/sources/src/main/webapp/js/components/sidebarExcludedNodeList.js
@@ -2,176 +2,39 @@
  * @constructor
  * @param {object} props Properties of the node list.
  */
-function SidebarExcludedNodeList(props) {
-	/** @prop {string} id Identifier of the component. */
-	this.id = props.id;
-
-	var rootElement;
-	var buttonGroup;
-	var sortListElement;
-	var nodeListElement;
-
-	var nodeList = [];
-
-	this.getNodeList = function() {
-		return nodeList;
-	};
-
-	this.add = function(node) {
-		if (!(node instanceof Node)) {
-			throw new TypeError(node.toString() + ' is not an instance of Node');
-		}
-
-		// set remove hook
-		node.removeFromSidebarList = this.remove.bind(this, node);
-
-		nodeList.push(node);
-		nodeListElement.appendChild(node.render());
-
-		app.redrawEdges();
-
-		toggleControls.call(this);
-	};
-
-	this.remove = function(node) {
-		if (!(node instanceof Node)) {
-			throw new TypeError(node.toString() + ' is not an instance of Node');
-		}
-
-		// unset remove hook
-		node.removeFromSidebarList = Utils.noop;
-
-		nodeList.splice(nodeList.indexOf(node), 1);
-		node.remove(false);
-
-		app.redrawEdges();
-
-		toggleControls.call(this);
-	};
-
-	this.render = function() {
-		rootElement = DOM.createHtmlElement('div', {
-			'id': props.id,
-			'class': 'node-container excluded-nodes ' + (props.class ? props.class : ''),
-		});
-		rootElement.addEventListener('scroll', function() {
-			app.redrawEdges();
-		});
-
-		// title
-		rootElement.appendChild(DOM.htmlStringToElement('<h2 class="node-container-title">Excluded components</h2>'));
-
-		// buttons
-		buttonGroup = DOM.createHtmlElement('div', {
-			'class': 'button-group hidden',
-		});
-		rootElement.appendChild(buttonGroup);
-
-		// include all button
-		var includeAllButton = DOM.createHtmlElement('button', {
-			'class': 'include-all-button button',
-			'title': 'Display all excluded nodes in viewport',
-		});
-		includeAllButton.appendChild(DOM.createHtmlElement('img', {
-			'src': 'images/button_cancel.png',
-			'alt': 'Icon of "display all excluded nodes in viewport" action',
-		}));
-		includeAllButton.addEventListener('click', includeAll.bind(this));
-		buttonGroup.appendChild(includeAllButton);
-
-		// sorting options
-		sortListElement = DOM.createHtmlElement('ul', {
-			'class': 'sort-list hidden',
-		});
-		rootElement.appendChild(sortListElement);
-
-		var sortByNameAscListItem = DOM.createHtmlElement('li', {
-			'class': 'sort-asc',
-		});
-		sortByNameAscListItem.appendChild(DOM.createTextElement('name'));
-		sortByNameAscListItem.addEventListener('click', sortByName.bind(this, 1));
-		sortListElement.appendChild(sortByNameAscListItem);
-
-		var sortByNameDescListItem = DOM.createHtmlElement('li', {
-			'class': 'sort-desc',
+class SidebarExcludedNodeList extends SidebarNodeList {
+	/**
+	 * @constructor
+	 */
+	constructor() {
+		super({
+			title: 'Excluded nodes',
+			class: 'node-container excluded-nodes',
+			id: '',	// TODO: remove as soon as Sidebar is reworked
 		});
-		sortByNameDescListItem.appendChild(DOM.createTextElement('name'));
-		sortByNameDescListItem.addEventListener('click', sortByName.bind(this, -1));
-		sortListElement.appendChild(sortByNameDescListItem);
-
-		var sortByCountAscListItem = DOM.createHtmlElement('li', {
-			'class': 'sort-asc',
-		});
-		sortByCountAscListItem.appendChild(DOM.createTextElement('#components'));
-		sortByCountAscListItem.addEventListener('click', sortByCount.bind(this, 1));
-		sortListElement.appendChild(sortByCountAscListItem);
-
-		var sortByCountDescListItem = DOM.createHtmlElement('li', {
-			'class': 'sort-desc',
-		});
-		sortByCountDescListItem.appendChild(DOM.createTextElement('#components'));
-		sortByCountDescListItem.addEventListener('click', sortByCount.bind(this, -1));
-		sortListElement.appendChild(sortByCountDescListItem);
-
-		// list
-		nodeListElement = DOM.createHtmlElement('ul', {
-			'class': 'node-list',
-		});
-		rootElement.appendChild(nodeListElement);
-
-		return rootElement;
-	};
-
-	this.reset = function() {
-		nodeList = [];
-
-		nodeListElement.innerHTML = '';
-
-		toggleControls.call(this);
-	};
-
-	function includeAll() {
-		var nodeListCopy = nodeList.slice(0);
-		nodeListCopy.forEach(node => {
-			node.include();
-		});
-
-		toggleControls.call(this);
-	}
-
-	function sortByName(sortOrder) {
-		nodeList.sort(function(a, b) {
-			return sortOrder * a.name.localeCompare(b.name);
-		});
-
-		sort.call(this);
 	}
 
-	function sortByCount(sortOrder) {
-		nodeList.sort(function(a, b) {
-			var aCount = (a instanceof Group) ? a.getVertexList().length : 1;
-			var bCount = (b instanceof Group) ? b.getVertexList().length : 1;
-
-			return sortOrder * (aCount - bCount);
-		});
-
-		sort.call(this);
+	_renderButtons() {
+		return [
+			DOM.h('button', {
+				class: 'button include-all-button',
+				title: 'Display all excluded nodes in viewport',
+				onClick: this._onIncludeAllClick.bind(this),
+			}, [
+				DOM.h('img', {
+					src: 'images/button_cancel.png',
+					alt: 'Icon of "display all excluded nodes in viewport" action',
+				}),
+			])
+		];
 	}
 
-	function sort() {
-		nodeList.forEach(node => {
-			node.remove(true);
-			nodeListElement.appendChild(node.render());
+	_onIncludeAllClick() {
+		let nodeListCopy = this.nodeList.slice(0);
+		nodeListCopy.forEach(node => {
+			node.include();
 		});
-	}
 
-	function toggleControls() {
-		if (nodeList.length > 0) {
-			buttonGroup.classList.remove('hidden');
-			sortListElement.classList.remove('hidden');
-		} else {
-			buttonGroup.classList.add('hidden');
-			sortListElement.classList.add('hidden');
-		}
+		this._toggleControls();
 	}
 }
diff --git a/sources/src/main/webapp/js/components/sidebarNodeList.js b/sources/src/main/webapp/js/components/sidebarNodeList.js
new file mode 100644
index 0000000..18c06e6
--- /dev/null
+++ b/sources/src/main/webapp/js/components/sidebarNodeList.js
@@ -0,0 +1,156 @@
+class SidebarNodeList {
+
+	constructor(props) {
+		this._properties = props;
+
+		this._nodeList = [];
+	}
+
+	get nodeList() {
+		return this._nodeList;
+	}
+
+	addNode(node) {
+		if (!(node instanceof Node)) {
+			throw new TypeError(node.toString() + ' is not an instance of Node');
+		}
+
+		// set remove hook
+		node.removeFromSidebarList = this.removeNode.bind(this, node);
+
+		this._nodeList.push(node);
+		this._nodeListElement.appendChild(node.render());
+
+		app.redrawEdges();
+		this._toggleControls();
+	}
+
+	removeNode(node) {
+		if (!(node instanceof Node)) {
+			throw new TypeError(node.toString() + ' is not an instance of Node');
+		}
+
+		// unset remove hook
+		node.removeFromSidebarList = Utils.noop;
+
+		this._nodeList.splice(this._nodeList.indexOf(node), 1);
+
+		node.remove(false);
+
+		app.redrawEdges();
+		this._toggleControls();
+	}
+
+	render() {
+		// buttons
+		this._buttonGroupElement = DOM.h('div', {
+			class: 'button-group',
+			hidden: 'hidden',
+		}, this._renderButtons());
+
+		// sorting options
+		this._sortOptionsElement = DOM.h('ul', {
+			class: 'sort-list',
+			hidden: 'hidden',
+		}, [
+			DOM.h('li', {
+				class: 'sort-asc',
+				innerText: 'name',
+				onClick: this._sortByName.bind(this, 1),
+			}),
+			DOM.h('li', {
+				class: 'sort-desc',
+				innerText: 'name',
+				onClick: this._sortByName.bind(this, -1),
+			}),
+			DOM.h('li', {
+				class: 'sort-asc',
+				innerText: '#components',
+				onClick: this._sortByCount.bind(this, 1),
+			}),
+			DOM.h('li', {
+				class: 'sort-desc',
+				innerText: '#components',
+				onClick: this._sortByCount.bind(this, -1),
+			}),
+		]);
+
+		// node list
+		this._nodeListElement = DOM.h('ul', {
+			class: 'node-list',
+		});
+
+		// root
+		this._rootElement = DOM.h('div', {
+			class: this._properties.class,
+			id: this._properties.id,
+			onScroll: () => app.redrawEdges(),
+		}, [
+			// title
+			DOM.h('h2', {
+				class: 'node-container-title',
+				innerText: this._properties.title,
+			}),
+			this._buttonGroupElement,
+			this._sortOptionsElement,
+			this._nodeListElement,
+		]);
+
+		return this._rootElement;
+	}
+
+	reset() {
+		this._nodeList = [];
+		this._nodeListElement.innerHTML = '';
+
+		this.toggleControls();
+	}
+
+	_sortByName(sortOrder) {
+		this._nodeList.sort((a, b) => {
+			return sortOrder * a.name.localeCompare(b.name);
+		});
+
+		this._sort();
+	}
+
+	_sortByCount(sortOrder) {
+		nodeList.sort((a, b) => {
+			var aCount = (a instanceof Group) ? a.countVertices : 1;
+			var bCount = (b instanceof Group) ? b.countVertices : 1;
+
+			return sortOrder * (aCount - bCount);
+		});
+
+		this._sort();
+	}
+
+	_sort() {
+		this._nodeList.forEach(node => {
+			node.remove(true);
+			this._nodeListElement.appendChild(node.render());
+		});
+	}
+
+	_renderButtons() {
+		throw new AbstractMethodError;
+	}
+
+	_displayControls() {
+		this._buttonGroupElement.removeAttribute('hidden');
+		this._sortOptionsElement.removeAttribute('hidden');
+	}
+
+	_hideControls() {
+		this._buttonGroupElement.setAttribute('hidden', 'hidden');
+		this._sortOptionsElement.setAttribute('hidden', 'hidden');
+	}
+
+	_toggleControls() {
+		if (this._nodeList.length > 0) {
+			this._displayControls();
+		} else {
+			this._hideControls();
+		}
+	}
+}
diff --git a/sources/src/main/webapp/js/components/sidebarUnconnectedNodeList.js b/sources/src/main/webapp/js/components/sidebarUnconnectedNodeList.js
index 3ebee66..1fa8f88 100644
--- a/sources/src/main/webapp/js/components/sidebarUnconnectedNodeList.js
+++ b/sources/src/main/webapp/js/components/sidebarUnconnectedNodeList.js
@@ -1,120 +1,75 @@
-/**
- * @constructor
- * @param {object} props Properties of the node list.
- */
-function SidebarUnconnectedNodeList(props) {
-	/** @prop {string} id Identifier of the component. */
-	this.id = props.id;
-
-	var rootElement;
-	var buttonGroup;
-	var nodeListElement;
-
-	var nodeList = [];
-
-	this.getNodeList = function() {
-		return nodeList;
-	};
-
-	this.add = function(node) {
-		if (!(node instanceof Vertex)) {
-			throw new TypeError(node.toString() + ' is not an instance of Vertex');
-		}
-
-		// set remove hook
-		node.removeFromSidebarList = this.remove.bind(this, node);
-
-		nodeList.push(node);
-		nodeListElement.appendChild(node.render());
-
-		app.redrawEdges();
-	};
-
-	this.remove = function(node) {
-		if (!(node instanceof Vertex)) {
-			throw new TypeError(node.toString() + ' is not an instance of Vertex');
-		}
-
-		// unset remove hook
-		node.removeFromSidebarList = Utils.noop;
-
-		nodeList.splice(nodeList.indexOf(node), 1);
-
-		node.remove(false);
-
-		app.redrawEdges();
-	};
-
-	this.render = function() {
-		rootElement = DOM.createHtmlElement('div', {
-			'id': props.id,
-			'class': 'node-container unconnected-nodes ' + (props.class ? props.class : ''),
+class SidebarUnconnectedNodeList extends SidebarNodeList {
+	/**
+	 * @constructor
+	 */
+	constructor() {
+		super({
+			title: 'Unconnected vertices',
+			class: 'node-container unconnected-nodes',
+			id: 'unconnectedNodeListComponent',
+			hidden: 'hidden',
 		});
-		rootElement.addEventListener('scroll', function() {
-			app.redrawEdges();
-		});
-
-		// title
-		rootElement.appendChild(DOM.htmlStringToElement('<h2 class="node-container-title">Unconnected vertices</h2>'));
-
-		// buttons
-		buttonGroup = DOM.createHtmlElement('div', {
-			'class': 'button-group',
-		});
-		rootElement.appendChild(buttonGroup);
-
-		// include all button
-		var includeAllButton = DOM.createHtmlElement('button', {
-			'class': 'include-all-button button',
-			'title': 'Display all unconnected nodes in viewport',
-		});
-		includeAllButton.appendChild(DOM.createHtmlElement('img', {
-			'src': 'images/unconnected/uncon_left.png',
-			'alt': 'Icon of "display all unconnected nodes in viewport" action',
-		}));
-		includeAllButton.addEventListener('click', includeAll.bind(this));
-		buttonGroup.appendChild(includeAllButton);
+	}
 
-		// exclude all button
-		var excludeAllButton = DOM.createHtmlElement('button', {
-			'class': 'exclude-all-button button',
-			'title': 'Display all unconnected nodes in sidebar',
-		});
-		excludeAllButton.appendChild(DOM.createHtmlElement('img', {
-			'src': 'images/unconnected/uncon_right.png',
-			'alt': 'Icon of "display all unconnected nodes in sidebar" action',
-		}));
-		excludeAllButton.addEventListener('click', excludeAll.bind(this));
-		buttonGroup.appendChild(excludeAllButton);
+	render() {
+		this._rootElement = super.render();
+		this._displayControls();
 
-		// list
-		nodeListElement = DOM.createHtmlElement('ul', {
-			'class': 'node-list',
-		});
-		rootElement.appendChild(nodeListElement);
+		return this._rootElement;
+	}
 
-		return rootElement;
-	};
+	toggle() {
+		if (this._rootElement.hasAttribute('hidden')) {
+			this._rootElement.removeAttribute('hidden');
+		} else {
+			this._rootElement.setAttribute('hidden', 'hidden');
+		}
+	}
 
-	this.reset = function() {
-		nodeList = [];
+	_renderButtons() {
+		return [
+			// include all button
+			DOM.h('button', {
+				class: 'button include-all-button',
+				title: 'Display all unconnected nodes in viewport',
+				onClick: this._onIncludeAllClick.bind(this),
+			}, [
+				DOM.h('img', {
+					src: 'images/unconnected/uncon_left.png',
+					alt: 'Icon of "display all unconnected nodes in viewport" action',
+				}),
+			]),
+			// exclude all button
+			DOM.h('button', {
+				class: 'exclude-all-button button',
+				title: 'Display all unconnected nodes in sidebar',
+				onClick: this._onExcludeAllClick.bind(this),
+			}, [
+				DOM.h('img', {
+					src: 'images/unconnected/uncon_right.png',
+					alt: 'Icon of "display all unconnected nodes in sidebar" action',
+				})
+			]),
+		];
+	}
 
-		nodeListElement.innerHTML = '';
-	};
+	_toggleControls() {
+		// do nothing
+	}
 
-	function includeAll() {
-		var nodeListCopy = nodeList.slice(0);
+	_onIncludeAllClick() {
+		let nodeListCopy = this.nodeList.slice(0);
 		nodeListCopy.forEach(node => {
 			node.include();
 		});
 	}
 
-	function excludeAll() {
+	_onExcludeAllClick() {
 		app.vertexList.filter(vertex => {
 			return vertex.isUnconnected;
 		}).forEach(vertex => {
 			vertex.exclude();
-			this.add(vertex);
+			this.addNode(vertex);
 		});
 	}
 }
diff --git a/sources/src/main/webapp/js/components/vertex.js b/sources/src/main/webapp/js/components/vertex.js
index 82f524c..a31e562 100644
--- a/sources/src/main/webapp/js/components/vertex.js
+++ b/sources/src/main/webapp/js/components/vertex.js
@@ -133,8 +133,8 @@ class Vertex extends Node {
 		this._group = newValue;
 
 		if (newValue && this.isExcluded) {
-			// remove floater
-			app.sidebarComponent.removeFloater(this._proxy);
+			// remove proxy
+			app.proxyList.splice(app.proxyList.indexOf(proxy), 1);
 			this._proxy = null;
 		}
 	}
@@ -400,7 +400,7 @@ class Vertex extends Node {
 	_onNodeContextMenu(e) {
 		e.preventDefault();
 
-		let excludedNodeList = app.sidebarComponent.excludedNodeListComponent.getNodeList();
+		let excludedNodeList = app.sidebarComponent.excludedNodeListComponent.nodeList;
 		let includedGroupList = app.viewportComponent.groupList;
 
 		let nodeList = [].concat(excludedNodeList, includedGroupList);
diff --git a/sources/src/main/webapp/js/components/vertexContextMenuList.js b/sources/src/main/webapp/js/components/vertexContextMenuList.js
index 309de75..9e352d0 100644
--- a/sources/src/main/webapp/js/components/vertexContextMenuList.js
+++ b/sources/src/main/webapp/js/components/vertexContextMenuList.js
@@ -108,8 +108,8 @@ class VertexContextMenuList {
 			app.nodeList.push(group);
 			app.groupList.push(group);
 
-			app.sidebarComponent.excludedNodeListComponent.add(group);
-			app.sidebarComponent.excludedNodeListComponent.remove(node);
+			app.sidebarComponent.excludedNodeListComponent.addNode(group);
+			app.sidebarComponent.excludedNodeListComponent.removeNode(node);
 
 			node.remove(true);
 
diff --git a/sources/src/main/webapp/js/services/graphExporter.js b/sources/src/main/webapp/js/services/graphExporter.js
index e5f6f61..f44f102 100644
--- a/sources/src/main/webapp/js/services/graphExporter.js
+++ b/sources/src/main/webapp/js/services/graphExporter.js
@@ -4,7 +4,7 @@ class GraphExporter {
 	 */
 	run() {
 		// nodes excluded to the sidebar
-		let excludedNodeList = app.sidebarComponent.excludedNodeListComponent.getNodeList();
+		let excludedNodeList = app.sidebarComponent.excludedNodeListComponent.nodeList;
 		let sideBar = excludedNodeList.map(node => {
 			return {
 				id: Utils.getUniqueId(node),
diff --git a/sources/src/main/webapp/js/services/graphLoader.js b/sources/src/main/webapp/js/services/graphLoader.js
index 7c9a995..b2c28e1 100644
--- a/sources/src/main/webapp/js/services/graphLoader.js
+++ b/sources/src/main/webapp/js/services/graphLoader.js
@@ -117,7 +117,7 @@ class GraphLoader {
 			return vertex.isUnconnected;
 		}).forEach(vertex => {
 			vertex.exclude();
-			app.sidebarComponent.unconnectedNodeListComponent.add(vertex);
+			app.sidebarComponent.unconnectedNodeListComponent.addNode(vertex);
 		});
 
 		// construct groups
@@ -175,7 +175,7 @@ class GraphLoader {
 			if (Utils.isDefined(node)) {
 				node.exclude();
 
-				app.sidebarComponent.excludedNodeListComponent.add(node);
+				app.sidebarComponent.excludedNodeListComponent.addNode(node);
 			}
 		});
 
diff --git a/sources/src/main/webapp/js/showGraphApp.js b/sources/src/main/webapp/js/showGraphApp.js
index 9f61980..30a1054 100644
--- a/sources/src/main/webapp/js/showGraphApp.js
+++ b/sources/src/main/webapp/js/showGraphApp.js
@@ -25,6 +25,8 @@ class ShowGraphApp extends App {
 		this.vertexList = [];
 		/** @prop {array<Group>} groupList */
 		this.groupList = [];
+		/** @prop {array<NodeProxy>} proxyList */
+		this.proxyList = [];
 
 		/** @prop {Diagram} diagram */
 		this.diagram = null;
@@ -63,6 +65,7 @@ class ShowGraphApp extends App {
 		this.nodeList = [];
 		this.vertexList = [];
 		this.groupList = [];
+		this.proxyList = [];
 	}
 
 	/**
@@ -78,7 +81,9 @@ class ShowGraphApp extends App {
 	 * Redraws edges leading from viewport to sidebar.
 	 */
 	redrawEdges() {
-		this.sidebarComponent.refreshFloaters();
+		this.proxyList.forEach(proxy => {
+			proxy.updatePosition();
+		});
 	}
 
 	/**
diff --git a/sources/src/main/webapp/showGraph.jsp b/sources/src/main/webapp/showGraph.jsp
index eea00d2..a701556 100644
--- a/sources/src/main/webapp/showGraph.jsp
+++ b/sources/src/main/webapp/showGraph.jsp
@@ -54,6 +54,7 @@
 		<script src="js/components/registerPopup.js"></script>
 		<script src="js/components/saveDiagramModalWindow.js"></script>
 		<script src="js/components/sidebar.js"></script>
+		<script src="js/components/sidebarNodeList.js"></script>
 		<script src="js/components/sidebarExcludedNodeList.js"></script>
 		<script src="js/components/sidebarUnconnectedNodeList.js"></script>
 		<script src="js/components/spinLoader.js" type="module"></script>
