commit 5022c089f6b8349d80d68ffc08a67aa8b73a1e97
Author: Pavel Fidranský <fidransky@outlook.com>
Date:   Sun Feb 10 20:17:46 2019 +0100

    re #16: change node SVG element from <svg> to <g>

diff --git a/sources/imiger-core/src/main/webapp/js/components/group.js b/sources/imiger-core/src/main/webapp/js/components/group.js
index fbc1309..bbf1fb0 100644
--- a/sources/imiger-core/src/main/webapp/js/components/group.js
+++ b/sources/imiger-core/src/main/webapp/js/components/group.js
@@ -200,10 +200,9 @@ class Group extends Node {
 	 */
 	_renderIncluded() {
 		// root
-		this._rootElement = DOM.s('svg', {
+		this._rootElement = DOM.s('g', {
 			class: 'node group',
-			x: this.position.x,
-			y: this.position.y,
+			transform: `translate(${this.position.x}, ${this.position.y})`,
 			'data-id': this.id,
 			onClick: this._onNodeClick.bind(this),
 			onMouseDown: this._onNodeMouseDown.bind(this),
diff --git a/sources/imiger-core/src/main/webapp/js/components/node.js b/sources/imiger-core/src/main/webapp/js/components/node.js
index 19e748f..601b377 100644
--- a/sources/imiger-core/src/main/webapp/js/components/node.js
+++ b/sources/imiger-core/src/main/webapp/js/components/node.js
@@ -117,8 +117,7 @@ class Node {
 			throw new TypeError(coords.toString() + ' is not an instance of Coordinates');
 		}
 
-		this._rootElement.setAttribute('x', coords.x);
-		this._rootElement.setAttribute('y', coords.y);
+		this._rootElement.setAttribute('transform', `translate(${coords.x}, ${coords.y})`);
 
 		let inEdgeList = this.inEdgeList;
 		inEdgeList.forEach(edge => {
@@ -452,10 +451,9 @@ class Node {
 		 * @param {Event} e Mouse up event.
 		 */
 		function mouseUp() {
-			that.position = new Coordinates(
-				+that._rootElement.getAttribute('x'),
-				+that._rootElement.getAttribute('y'),
-			);
+			const match = Constants.transformAttributeRegex.exec(that._rootElement.getAttribute('transform'));
+
+			that.position = new Coordinates(+match.groups.x, +match.groups.y);
 
 			that._rootElement.classList.remove('node--dragged');
 			
diff --git a/sources/imiger-core/src/main/webapp/js/components/vertex.js b/sources/imiger-core/src/main/webapp/js/components/vertex.js
index ba6e1d4..6461265 100644
--- a/sources/imiger-core/src/main/webapp/js/components/vertex.js
+++ b/sources/imiger-core/src/main/webapp/js/components/vertex.js
@@ -214,10 +214,9 @@ class Vertex extends Node {
 	 */
 	_renderIncluded() {
 		// root
-		this._rootElement = DOM.s('svg', {
+		this._rootElement = DOM.s('g', {
 			class: 'node vertex',
-			x: this.position.x,
-			y: this.position.y,
+			transform: `translate(${this.position.x}, ${this.position.y})`,
 			'data-id': this.id,
 			'data-name': this.name,
 			onClick: this._onNodeClick.bind(this),
diff --git a/sources/imiger-core/src/main/webapp/js/constants.js b/sources/imiger-core/src/main/webapp/js/constants.js
index 75f614b..a56f2e6 100644
--- a/sources/imiger-core/src/main/webapp/js/constants.js
+++ b/sources/imiger-core/src/main/webapp/js/constants.js
@@ -18,3 +18,6 @@ Constants.API = {
 	removeDiagram: 'api/remove-diagram',
 	getPrivateDiagrams: 'api/get-private-diagrams',
 };
+
+/** @static @prop {object} Regular expression for extracting coordinates from SVG transform attribute. */
+Constants.transformAttributeRegex = /translate\((?<x>[0-9\.-]+), (?<y>[0-9\.-]+)\)/;
diff --git a/sources/imiger-core/src/main/webapp/tests/common.robot b/sources/imiger-core/src/main/webapp/tests/common.robot
index 22f903c..b00c290 100644
--- a/sources/imiger-core/src/main/webapp/tests/common.robot
+++ b/sources/imiger-core/src/main/webapp/tests/common.robot
@@ -3,6 +3,7 @@ Documentation	A resource file with reusable keywords and variables.
 
 Library			Selenium2Library
 Library			OperatingSystem
+Library			String
 
 
 *** Variables ***
@@ -53,19 +54,29 @@ Reload Diagram Screen
 
 Element Should Have Class
 	[Arguments]		${element}		${className}
-	Page Should Contain Element		${element}[contains(@class, "${className}")]
+	${element}=		Catenate	SEPARATOR=	${element}	[contains(@class,"		${className}	")]
+	Page Should Contain Element		${element}
 
 
 Element Should Not Have Class
 	[Arguments]		${element}		${className}
-	Page Should Not Contain Element	${element}[contains(@class, "${className}")]
+	${element}=		Catenate	SEPARATOR=	${element}	[contains(@class,"		${className}	")]
+	Page Should Not Contain Element	${element}
 
 
 Element Should Have Attribute
 	[Arguments]		${element}		${attributeName}
-	Page Should Contain Element		${element}[@${attributeName}]
+	${element}=		Catenate	SEPARATOR=	${element}	[@	${attributeName}	]
+	Page Should Contain Element		${element}
 
 
 Element Should Not Have Attribute
 	[Arguments]		${element}		${attributeName}
-	Page Should Not Contain Element	${element}[@${attributeName}]
+	Element Attribute Value Should Be	${element}	${attributeName}	${None}
+
+
+Get Element Coordinates
+	[Arguments]		${element}
+	${transformAttributeValue}=		Get Element Attribute	${element}	transform
+	${matches}=		Get Regexp Matches		${transformAttributeValue}	([0-9\.-]+)
+	[Return]		${matches}
diff --git a/sources/imiger-core/src/main/webapp/tests/edge.robot b/sources/imiger-core/src/main/webapp/tests/edge.robot
index 3dd56d3..83f237e 100644
--- a/sources/imiger-core/src/main/webapp/tests/edge.robot
+++ b/sources/imiger-core/src/main/webapp/tests/edge.robot
@@ -11,8 +11,8 @@ Resource		common.robot
 *** Variables ***
 ${edge38}		//*[name()="g"][contains(@class, "edge")][@data-id="38"]
 
-${vertex4}		//*[name()="svg"][contains(@class, "vertex")][@data-id="4"]
-${vertex2014}	//*[name()="svg"][contains(@class, "vertex")][@data-id="2014"]
+${vertex4}		//*[name()="g"][contains(@class, "vertex")][@data-id="4"]
+${vertex2014}	//*[name()="g"][contains(@class, "vertex")][@data-id="2014"]
 
 ${edgeArrow}	/*[name()="g"][contains(@class, "arrow")]
 
diff --git a/sources/imiger-core/src/main/webapp/tests/filter.robot b/sources/imiger-core/src/main/webapp/tests/filter.robot
index d482e06..7f619ed 100644
--- a/sources/imiger-core/src/main/webapp/tests/filter.robot
+++ b/sources/imiger-core/src/main/webapp/tests/filter.robot
@@ -31,7 +31,7 @@ ${dateToValueInput}					${filterModalWindow}//input[@name="value-to"]
 ${applyButton}						${filterModalWindow}//button[@type="submit"]
 ${resetButton}						${filterModalWindow}//button[@type="reset"]
 
-${foundNodes}						//*[name()="svg"][contains(@class, "node--found")]
+${foundNodes}						//*[contains(@class, "node")][@filter="url(#node--found)"]
 
 
 *** Test Cases ***
diff --git a/sources/imiger-core/src/main/webapp/tests/graph.robot b/sources/imiger-core/src/main/webapp/tests/graph.robot
index 91b790b..e3f0553 100644
--- a/sources/imiger-core/src/main/webapp/tests/graph.robot
+++ b/sources/imiger-core/src/main/webapp/tests/graph.robot
@@ -12,10 +12,10 @@ ${graphWrapper}			//*[name()="g"][@id="graph"]
 ${unconnectedNodeList}	//div[@id="unconnectedNodeListComponent"]
 ${componentCounter}		//span[@class="component-counter"]
 
-${vertex2}				//*[name()="svg"][contains(@class, "vertex")][@data-id="2"]
-${vertex4}				//*[name()="svg"][contains(@class, "vertex")][@data-id="4"]
-${vertex291}			//*[name()="svg"][contains(@class, "vertex")][@data-id="291"]
-${vertex303}			//*[name()="svg"][contains(@class, "vertex")][@data-id="303"]
+${vertex2}				//*[name()="g"][contains(@class, "vertex")][@data-id="2"]
+${vertex4}				//*[name()="g"][contains(@class, "vertex")][@data-id="4"]
+${vertex291}			//*[name()="g"][contains(@class, "vertex")][@data-id="291"]
+${vertex303}			//*[name()="g"][contains(@class, "vertex")][@data-id="303"]
 
 ${vertex0}				//*[name()="li"][contains(@class, "vertex")][@data-id="0"]
 ${vertex6}				//*[name()="li"][contains(@class, "vertex")][@data-id="6"]
diff --git a/sources/imiger-core/src/main/webapp/tests/group.robot b/sources/imiger-core/src/main/webapp/tests/group.robot
index 452a874..10a5e6c 100644
--- a/sources/imiger-core/src/main/webapp/tests/group.robot
+++ b/sources/imiger-core/src/main/webapp/tests/group.robot
@@ -12,10 +12,10 @@ Resource		common.robot
 *** Variables ***
 ${zoomInButton}		//button[@id="zoomIn"]
 
-${vertex2014}		//*[name()="svg"][contains(@class, "vertex")][@data-id="2014"]
-${vertex2019}		//*[name()="svg"][contains(@class, "vertex")][@data-id="2019"]
+${vertex2014}		//*[name()="g"][contains(@class, "vertex")][@data-id="2014"]
+${vertex2019}		//*[name()="g"][contains(@class, "vertex")][@data-id="2019"]
 
-${group1}			//*[name()="svg"][contains(@class, "group")][@data-id="1"]
+${group1}			//*[name()="g"][contains(@class, "group")][@data-id="1"]
 
 ${groupName}			//span[@class="group-name"]
 
@@ -35,17 +35,15 @@ Drag Group
 	Click Element	${zoomInButton}
 	Click Element	${zoomInButton}
 	# get old group coordinates
-	${oldX}=	Get Element Attribute	${group1}	x
-	${oldX}=	Convert To Number		${oldX}	2
-	${oldY}=	Get Element Attribute	${group1}	y
-	${oldY}=	Convert To Number		${oldY}	2
+	${oldCoords}=	Get Element Coordinates		${group1}
+	${oldX}=	Convert To Number		${oldCoords}[0]		2
+	${oldY}=	Convert To Number		${oldCoords}[1]		2
 	# move vertex by 10 pixels in both directions
 	Drag And Drop By Offset				${group1}	10	10
 	# get new group coordinates
-	${newX}=	Get Element Attribute	${group1}	x
-	${newX}=	Convert To Number		${newX}	2
-	${newY}=	Get Element Attribute	${group1}	y
-	${newY}=	Convert To Number		${newY}	2
+	${newCoords}=	Get Element Coordinates		${group1}
+	${newX}=	Convert To Number		${newCoords}[0]		2
+	${newY}=	Convert To Number		${newCoords}[1]		2
 	# check that offset matches
 	Should Be Equal As Numbers			${oldX + 10}	${newX}
 	Should Be Equal As Numbers			${oldY + 10}	${newY}
diff --git a/sources/imiger-core/src/main/webapp/tests/navbar.robot b/sources/imiger-core/src/main/webapp/tests/navbar.robot
index 61dbed0..afa20a5 100644
--- a/sources/imiger-core/src/main/webapp/tests/navbar.robot
+++ b/sources/imiger-core/src/main/webapp/tests/navbar.robot
@@ -18,8 +18,8 @@ ${backToUploadButton}	//a[@id="backToUpload"]
 
 ${excludedNodeList}		//div[@id="excludedNodeListComponent"]
 
-${group1}				//*[name()="svg"][contains(@class, "group")][@data-id="1"]
-${vertex2044}			//*[name()="svg"][contains(@class, "vertex")][@data-id="2044"]
+${group1}				//*[name()="g"][contains(@class, "group")][@data-id="1"]
+${vertex2044}			//*[name()="g"][contains(@class, "vertex")][@data-id="2044"]
 ${excludedVertex2044}	//li[contains(@class, "vertex")][@data-id="2044"]
 
 
diff --git a/sources/imiger-core/src/main/webapp/tests/node-excluding.robot b/sources/imiger-core/src/main/webapp/tests/node-excluding.robot
index d10a4bf..2c331f4 100644
--- a/sources/imiger-core/src/main/webapp/tests/node-excluding.robot
+++ b/sources/imiger-core/src/main/webapp/tests/node-excluding.robot
@@ -16,12 +16,12 @@ ${excludedNodeList}		//div[@id="excludedNodeListComponent"]
 
 ${includeAllButton}		//button[contains(@class, "include-all-button")]
 
-${vertex2013}			//*[name()="svg"][contains(@class, "vertex")][@data-id="2013"]
+${vertex2013}			//*[name()="g"][contains(@class, "vertex")][@data-id="2013"]
 ${excludedVertex2013}	//li[contains(@class, "vertex")][@data-id="2013"]
-${vertex2014}			//*[name()="svg"][contains(@class, "vertex")][@data-id="2014"]
+${vertex2014}			//*[name()="g"][contains(@class, "vertex")][@data-id="2014"]
 ${excludedVertex2014}	//li[contains(@class, "vertex")][@data-id="2014"]
 
-${group1}				//*[name()="svg"][contains(@class, "group")][@data-id="1"]
+${group1}				//*[name()="g"][contains(@class, "group")][@data-id="1"]
 ${excludedGroup1}		//li[contains(@class, "group")][@data-id="1"]
 
 
diff --git a/sources/imiger-core/src/main/webapp/tests/search.robot b/sources/imiger-core/src/main/webapp/tests/search.robot
index b19273c..1931419 100644
--- a/sources/imiger-core/src/main/webapp/tests/search.robot
+++ b/sources/imiger-core/src/main/webapp/tests/search.robot
@@ -13,8 +13,8 @@ ${searchInputField}	//input[@id="searchText"]
 ${searchButton}		//button[@id="searchButton"]
 ${searchCounter}	//span[@id="searchCount"]
 
-${vertex2015}		//*[name()="svg"][@data-id="2015"]
-${vertex2039}		//*[name()="svg"][@data-id="2039"]
+${vertex2015}		//*[contains(@class, "vertex")][@data-id="2015"]
+${vertex2039}		//*[contains(@class, "vertex")][@data-id="2039"]
 
 
 *** Test Cases ***
diff --git a/sources/imiger-core/src/main/webapp/tests/sidebar.robot b/sources/imiger-core/src/main/webapp/tests/sidebar.robot
index ac6e918..4e84f0f 100644
--- a/sources/imiger-core/src/main/webapp/tests/sidebar.robot
+++ b/sources/imiger-core/src/main/webapp/tests/sidebar.robot
@@ -15,7 +15,7 @@ ${unconnectedNodeList}	//div[@id="unconnectedNodeListComponent"]
 ${includeAllButton}		//button[contains(@class, "include-all-button")]
 ${excludeAllButton}		//button[contains(@class, "exclude-all-button")]
 
-${vertex2030}			//*[name()="svg"][contains(@class, "vertex")][@data-id="2030"]
+${vertex2030}			//*[name()="g"][contains(@class, "vertex")][@data-id="2030"]
 ${excludedVertex2030}	//li[contains(@class, "vertex")][@data-id="2030"]
 
 
diff --git a/sources/imiger-core/src/main/webapp/tests/vertex.robot b/sources/imiger-core/src/main/webapp/tests/vertex.robot
index fe7e537..7fc9cfa 100644
--- a/sources/imiger-core/src/main/webapp/tests/vertex.robot
+++ b/sources/imiger-core/src/main/webapp/tests/vertex.robot
@@ -10,9 +10,9 @@ Resource		common.robot
 *** Variables ***
 ${zoomInButton}			//button[@id="zoomIn"]
 
-${vertex9}				//*[name()="svg"][contains(@class, "vertex")][@data-id="9"]
-${vertex58}				//*[name()="svg"][contains(@class, "vertex")][@data-id="58"]
-${vertex2014}			//*[name()="svg"][contains(@class, "vertex")][@data-id="2014"]
+${vertex9}				//*[name()="g"][contains(@class, "vertex")][@data-id="9"]
+${vertex58}				//*[name()="g"][contains(@class, "vertex")][@data-id="58"]
+${vertex2014}			//*[name()="g"][contains(@class, "vertex")][@data-id="2014"]
 
 ${vertexArchetypeIcon}	/*[name()="use"][@class="archetype-icon"]
 
@@ -46,17 +46,15 @@ Drag Vertex
 	Click Element		${zoomInButton}
 	Click Element		${zoomInButton}
 	# get old vertex coordinates
-	${oldX}=	Get Element Attribute	${vertex2014}	x
-	${oldX}=	Convert To Number		${oldX}	2
-	${oldY}=	Get Element Attribute	${vertex2014}	y
-	${oldY}=	Convert To Number		${oldY}	2
+	${oldCoords}=	Get Element Coordinates		${vertex2014}
+	${oldX}=	Convert To Number		${oldCoords}[0]		2
+	${oldY}=	Convert To Number		${oldCoords}[1]		2
 	# move vertex by 10 pixels in both directions
 	Drag And Drop By Offset				${vertex2014}	10	10
 	# get new vertex coordinates
-	${newX}=	Get Element Attribute	${vertex2014}	x
-	${newX}=	Convert To Number		${newX}	2
-	${newY}=	Get Element Attribute	${vertex2014}	y
-	${newY}=	Convert To Number		${newY}	2
+	${newCoords}=	Get Element Coordinates		${vertex2014}
+	${newX}=	Convert To Number		${newCoords}[0]		2
+	${newY}=	Convert To Number		${newCoords}[1]		2
 	# check that offset matches
 	Should Be Equal As Numbers			${oldX + 10}	${newX}
 	Should Be Equal As Numbers			${oldY + 10}	${newY}
