Gaudenz Alder преди 5 години
родител
ревизия
fbaa5837ac

+ 5 - 0
ChangeLog

@@ -1,3 +1,8 @@
+24-SEP-2019: 11.3.1
+
+- Fixes uncompressed XML in SVG
+- Fixes offset for ruler
+
 20-SEP-2019: 11.3.0
 
 - Updates Google Cloud Platform stencils

+ 1 - 1
VERSION

@@ -1 +1 @@
-11.3.0
+11.3.1

+ 73 - 10
etc/docker/README.md

@@ -1,13 +1,76 @@
-Docker
-------
-After successful checkout, from the project directory run,
+This guide is based on fjudith/draw.io docker image [docker-draw.io repository](https://github.com/fjudith/docker-draw.io)
 
-```bash
-cp etc/docker/Dockerfile .
-docker build -t draw .
-docker run -d -p 8888:8080 draw
+#HTTPS SSL Certificate via Let's Encrypt
+
+###Prerequisites:
+
+1. A Linux machine connected to the Internet with ports 443 and 80 open
+1. A domain/subdomain name pointing to this machine's IP address. (e.g., drawio.example.com)
+
+###Method:
+
+1. Using fjudith/draw.io docker image, run the following command
+`docker run -it -m1g -e LETS_ENCRYPT_ENABLED=true -e PUBLIC_DNS=drawio.example.com --rm --name="draw" -p 80:80 -p 443:8443 fjudith/draw.io`
+Notice that mapping port 80 to container's port 80 allows certbot to work in stand-alone mode. Mapping port 443 to container's port 8443 allows the container tomcat to serve https requests directly.
+
+#Updating draw.io in a running container
+
+##Method 1 (Using the host machine to build draw.io):
+
+###Prerequisites:
+
+1. Host machine must have wget, unzip and ant installed
+1. Assumptions: docker container is named 'draw' and $CATALINA_HOME in the container is '/usr/local/tomcat/'
+
+###Method:
+
+1. `wget https://github.com/jgraph/draw.io/archive/v{$version}.zip` For example, `wget https://github.com/jgraph/draw.io/archive/v11.2.9.zip`
+1. `unzip v11.2.9.zip`
+1. `cd drawio-11.2.9/etc/build/`
+1. `ant war`
+1. `cd ../../build/`
+1. `unzip draw.war -d draw`
+1. `docker cp draw draw:/usr/local/tomcat/webapps/`
+
+##Method 2 (build a new docker image with the new version)
+
+1. In fjudith/draw.io Dockerfile, change VERSION to the required version. For example, `ARG VERSION=11.2.9`
+1. Build the new docker image `docker build -t fjudith/draw.io .`
+1. Run the new image instead of the old one
+
+#Changing draw.io configuration
+
+##Method 1 (Build you custom image with setting pre-loaded)
+
+1. Create you PreConfig.js & PostConfig.js files using templates found in ⁨src⁩/⁨main⁩/⁨webapp⁩/⁨js⁩/PreConfig.js & PostConfig.js
+1. Add your configuration files (PreConfig.js & PostConfig.js) next to Dockerfile
+1. Add the following line after line 7 in Dockerfile
+
+```
+COPY PreConfig.js PostConfig.js $CATALINA_HOME/webapps/draw/js/
+```
+
+##Method 2 (Using existing running docker container)
+
+1. Create you PreConfig.js & PostConfig.js files using templates found in ⁨src⁩/⁨main⁩/⁨webapp⁩/⁨js⁩/PreConfig.js & PostConfig.js
+1. Copy these files to docker container 
+
+```
+docker cp PreConfig.js draw:/usr/local/tomcat/webapps/draw/js/
+docker cp PostConfig.js draw:/usr/local/tomcat/webapps/draw/js/
 ```
-Now the app will be accessible at `http://localhost:8888/draw/?local=1` (the local URL parameter disables
-the cloud integrations as those require some keys to be changed).
 
-We recommend using [this external Docker draw.io project](https://github.com/fjudith/docker-draw.io), it is somewhat more advanced and better maintained.
+##Method 3 (Bind configuration files into the container when started)
+
+1. This method allows changing the configuration files directly on the host without invoking any other docker commands. It can be used for testing
+1. Create you PreConfig.js & PostConfig.js files using templates found in ⁨src⁩/⁨main⁩/⁨webapp⁩/⁨js⁩/PreConfig.js & PostConfig.js
+1. From within the directory that contained the configuration files, run the following command to start docker container
+
+```
+docker run -it  --rm --name="draw" --mount type=bind,source="$(pwd)"/PreConfig.js,target=/usr/local/tomcat/webapps/draw/js/PreConfig.js --mount type=bind,source="$(pwd)"/PostConfig.js,target=/usr/local/tomcat/webapps/draw/js/PostConfig.js -p 8080:8080 -p 8443:8443 fjudith/draw.io
+```
+
+#draw.io with local export server
+
+Use the docker image from `drawio-export` folder. This image is much larger since it requires Node.js in addition to Puppeteer (which requires many dependencies similar to Chromium)
+

+ 61 - 0
etc/docker/drawio-export/Dockerfile

@@ -0,0 +1,61 @@
+FROM tomcat:9-jre11-slim
+
+LABEL maintainer="Florian JUDITH <florian.judith.b@gmail.com>"
+
+ARG VERSION=11.3.0
+
+RUN apt-get update -y && \
+    apt-get install -y --no-install-recommends \
+        openjdk-11-jdk-headless ant git patch wget xmlstarlet certbot curl && \
+    curl -sL https://deb.nodesource.com/setup_12.x | bash - && \
+    apt-get install -y --no-install-recommends nodejs chromium libatk-bridge2.0-0 libgtk-3-0 && \
+    cd /tmp && \
+    wget https://github.com/jgraph/draw.io/archive/v${VERSION}.zip && \
+    unzip v${VERSION}.zip && \
+    cd /tmp/drawio-${VERSION} && \
+    cd /tmp/drawio-${VERSION}/etc/build && \
+    ant war && \
+    cd /tmp/drawio-${VERSION}/build && \
+    unzip /tmp/drawio-${VERSION}/build/draw.war -d $CATALINA_HOME/webapps/draw && \
+    mkdir /usr/local/drawio && \
+    cd /usr/local/drawio && \
+    git clone https://github.com/jgraph/draw-image-export2.git && \
+    cd draw-image-export2 && \
+    npm install && \
+    apt-get remove -y --purge openjdk-11-jdk-headless ant git patch wget && \
+    apt-get autoremove -y --purge && \
+    apt-get remove -y --purge chromium && \
+    apt-get clean && \
+    rm -r /var/lib/apt/lists/* && \
+    rm -rf \
+        /tmp/v${VERSION}.zip \
+        /tmp/drawio-${VERSION}
+    
+COPY PreConfig.js PostConfig.js $CATALINA_HOME/webapps/draw/js/
+
+# Update server.xml to set Draw.io webapp to root
+RUN cd $CATALINA_HOME && \
+    xmlstarlet ed \
+    -P -S -L \
+    -i '/Server/Service/Engine/Host/Valve' -t 'elem' -n 'Context' \
+    -i '/Server/Service/Engine/Host/Context' -t 'attr' -n 'path' -v '/' \
+    -i '/Server/Service/Engine/Host/Context[@path="/"]' -t 'attr' -n 'docBase' -v 'draw' \
+    -s '/Server/Service/Engine/Host/Context[@path="/"]' -t 'elem' -n 'WatchedResource' -v 'WEB-INF/web.xml' \
+    -i '/Server/Service/Engine/Host/Valve' -t 'elem' -n 'Context' \
+    -i '/Server/Service/Engine/Host/Context[not(@path="/")]' -t 'attr' -n 'path' -v '/ROOT' \
+    -s '/Server/Service/Engine/Host/Context[@path="/ROOT"]' -t 'attr' -n 'docBase' -v 'ROOT' \
+    -s '/Server/Service/Engine/Host/Context[@path="/ROOT"]' -t 'elem' -n 'WatchedResource' -v 'WEB-INF/web.xml' \
+    conf/server.xml
+
+
+# Copy docker-entrypoint
+COPY docker-entrypoint.sh /
+RUN chmod +x /docker-entrypoint.sh
+
+WORKDIR $CATALINA_HOME
+
+#If export server is not used outside draw.io, no need to expose port 8000
+EXPOSE 8080 8443 8000
+
+ENTRYPOINT ["/docker-entrypoint.sh"]
+CMD ["catalina.sh", "run"]

+ 8 - 0
etc/docker/drawio-export/PostConfig.js

@@ -0,0 +1,8 @@
+/**
+ * Copyright (c) 2006-2019, JGraph Ltd
+ * Copyright (c) 2006-2019, draw.io AG
+ */
+// null'ing of global vars need to be after init.js
+window.VSD_CONVERT_URL = null;
+window.EMF_CONVERT_URL = null;
+window.ICONSEARCH_PATH = null;

+ 10 - 0
etc/docker/drawio-export/PreConfig.js

@@ -0,0 +1,10 @@
+/**
+ * Copyright (c) 2006-2019, JGraph Ltd
+ * Copyright (c) 2006-2019, draw.io AG
+ */
+// Overrides of global vars need to be pre-loaded
+window.EXPORT_URL = '/export'; //This points to ExportProxyServlet which uses the local export server at port 8000. 
+							   //This proxy configuration allows https requests to the export server via Tomcat.
+window.PLANT_URL = 'REPLACE_WITH_YOUR_PLANTUML_SERVER';
+window.DRAW_MATH_URL = 'math';
+window.DRAWIO_CONFIG = null; //Replace with your custom draw.io configurations. For more details, https://desk.draw.io/support/solutions/articles/16000058316

+ 67 - 0
etc/docker/drawio-export/docker-entrypoint.sh

@@ -0,0 +1,67 @@
+#!/bin/bash
+#set -e
+
+LETS_ENCRYPT_ENABLED=${LETS_ENCRYPT_ENABLED:-false}
+PUBLIC_DNS=${PUBLIC_DNS:-'draw.example.com'}
+ORGANISATION_UNIT=${ORGANIZATION_UNIT:-'Cloud Native Application'}
+ORGANISATION=${ORGANISATION:-'example inc'}
+CITY=${CITY:-'Paris'}
+STATE=${STATE:-'Paris'}
+COUNTRY_CODE=${COUNTRY:-'FR'}
+KEYSTORE_PASS=${KEYSTORE_PASS:-'V3ry1nS3cur3P4ssw0rd'}
+KEY_PASS=${KEY_PASS:-$KEYSTORE_PASS}
+
+
+if ! [ -f $CATALINA_HOME/.keystore ] && [ "$LETS_ENCRYPT_ENABLED" == "true" ]; then
+    echo "Generating Let's Encrypt certificate"
+    
+    keytool -genkey -noprompt -alias tomcat -dname "CN=${PUBLIC_DNS}, OU=${ORGANISATION_UNIT}, O=${ORGANISATION}, L=${CITY}, S=${STATE}, C=${COUNTRY_CODE}" -keystore $CATALINA_HOME/.keystore -storepass "${KEYSTORE_PASS}" -KeySize 2048 -keypass "${KEY_PASS}" -keyalg RSA -storetype pkcs12
+
+    keytool -list -keystore $CATALINA_HOME/.keystore -v -storepass "${KEYSTORE_PASS}"
+
+    keytool -certreq -alias tomcat -file request.csr -keystore $CATALINA_HOME/.keystore -storepass "${KEYSTORE_PASS}"
+
+    certbot certonly --csr $CATALINA_HOME/request.csr --standalone --register-unsafely-without-email --agree-tos
+
+    keytool -import -trustcacerts -alias tomcat -file 0001_chain.pem -keystore $CATALINA_HOME/.keystore -storepass "${KEYSTORE_PASS}"
+fi
+
+if ! [ -f $CATALINA_HOME/.keystore ] && [ "$LETS_ENCRYPT_ENABLED" == "false" ]; then
+    echo "Generating Self-Signed certificate"
+
+    keytool -genkey -noprompt -alias selfsigned -dname "CN=${PUBLIC_DNS}, OU=${ORGANISATION_UNIT}, O=${ORGANISATION}, L=${CITY}, S=${STATE}, C=${COUNTRY_CODE}" -keystore $CATALINA_HOME/.keystore -storepass "${KEYSTORE_PASS}" -KeySize 2048 -keypass "${KEY_PASS}" -keyalg RSA -validity 3600 -storetype pkcs12
+    
+    keytool -list -keystore $CATALINA_HOME/.keystore -v -storepass "${KEYSTORE_PASS}"
+fi
+
+# Update SSL port configuration if it does'nt exists
+#
+UUID="$(cat /dev/urandom | tr -dc 'a-zA-Z' | fold -w 1 | head -n 1)$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 7 | head -n 1)"
+VAR=$(cat conf/server.xml | grep "$CATALINA_HOME/.keystore")
+
+if [ -f $CATALINA_HOME/.keystore ] && [ -z $VAR ]; then
+     echo "Append https connector to server.xml"
+
+    xmlstarlet ed \
+        -P -S -L \
+        -s '/Server/Service' -t 'elem' -n "${UUID}" \
+        -i "/Server/Service/${UUID}" -t 'attr' -n 'port' -v '8443' \
+        -i "/Server/Service/${UUID}" -t 'attr' -n 'protocol' -v 'org.apache.coyote.http11.Http11NioProtocol' \
+        -i "/Server/Service/${UUID}" -t 'attr' -n 'SSLEnabled' -v 'true' \
+        -i "/Server/Service/${UUID}" -t 'attr' -n 'maxThreads' -v '150' \
+        -i "/Server/Service/${UUID}" -t 'attr' -n 'scheme' -v 'https' \
+        -i "/Server/Service/${UUID}" -t 'attr' -n 'secure' -v 'true' \
+        -i "/Server/Service/${UUID}" -t 'attr' -n 'clientAuth' -v 'false' \
+        -i "/Server/Service/${UUID}" -t 'attr' -n 'sslProtocol' -v 'TLS' \
+        -i "/Server/Service/${UUID}" -t 'attr' -n 'KeystoreFile' -v "$CATALINA_HOME/.keystore" \
+        -i "/Server/Service/${UUID}" -t 'attr' -n 'KeystorePass' -v "${KEY_PASS}" \
+        -r "/Server/Service/${UUID}" -v 'Connector' \
+    conf/server.xml
+fi
+
+#Run the export server
+cd /usr/local/drawio/draw-image-export2
+npm start &
+cd $CATALINA_HOME
+
+exec "$@"

+ 94 - 0
src/main/java/com/mxgraph/online/ExportProxyServlet.java

@@ -0,0 +1,94 @@
+package com.mxgraph.online;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Servlet implementation ExportProxyServlet
+ */
+@SuppressWarnings("serial")
+public class ExportProxyServlet extends HttpServlet
+{
+	//TODO Move this URL to configuration
+	private final String EXPORT_URL = "http://localhost:8000/";
+	
+	/**
+	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
+	 */
+	protected void doPost(HttpServletRequest request,
+			HttpServletResponse response) throws ServletException, IOException
+	{
+		try
+		{
+			URL url = new URL(EXPORT_URL);
+			HttpURLConnection con = (HttpURLConnection) url.openConnection();
+			
+			con.setRequestMethod("POST");
+			
+			//Copy request headers to export server
+			Enumeration<String> headerNames = request.getHeaderNames();
+			 
+	        while (headerNames.hasMoreElements()) 
+	        {
+	            String headerName = headerNames.nextElement();
+	            Enumeration<String> headers = request.getHeaders(headerName);
+	            
+	            while (headers.hasMoreElements()) 
+	            {
+	                String headerValue = headers.nextElement();
+	                con.addRequestProperty(headerName, headerValue);
+	            }
+	        }
+	        
+			// Send post request
+			con.setDoOutput(true);
+			
+			OutputStream params = con.getOutputStream();
+			Utils.copy(request.getInputStream(), params);
+			params.flush();
+			params.close();
+			
+			//Copy response code
+			response.setStatus(con.getResponseCode());
+			
+			//Copy response headers
+			Map<String, List<String>> map = con.getHeaderFields();
+			
+			for (Map.Entry<String, List<String>> entry : map.entrySet()) 
+			{
+				String key = entry.getKey();
+				
+				if (key != null)
+				{
+					for (String val : entry.getValue())
+					{	
+						
+						response.addHeader(entry.getKey(), val);
+					}
+				}
+			}
+			
+			//Copy response
+			OutputStream out = response.getOutputStream();
+			Utils.copy(con.getInputStream(), out);
+			out.flush();
+			out.close();
+		}
+		catch (Exception e)
+		{
+			response.setStatus(
+					HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+			e.printStackTrace();
+		}
+	}
+}

+ 1 - 1
src/main/webapp/cache.manifest

@@ -1,7 +1,7 @@
 CACHE MANIFEST
 
 # THIS FILE WAS GENERATED. DO NOT MODIFY!
-# 09/20/2019 05:21 PM
+# 09/24/2019 11:55 AM
 
 app.html
 index.html?offline=1

+ 2 - 1
src/main/webapp/js/PreConfig.js

@@ -5,4 +5,5 @@
 // Overrides of global vars need to be pre-loaded
 window.EXPORT_URL = 'REPLACE_WITH_YOUR_IMAGE_SERVER';
 window.PLANT_URL = 'REPLACE_WITH_YOUR_PLANTUML_SERVER';
-window.DRAW_MATH_URL = 'math';
+window.DRAW_MATH_URL = 'math';
+window.DRAWIO_CONFIG = null; //Replace with your custom draw.io configurations. For more details, https://desk.draw.io/support/solutions/articles/16000058316

Файловите разлики са ограничени, защото са твърде много
+ 691 - 690
src/main/webapp/js/app.min.js


+ 49 - 41
src/main/webapp/js/diagramly/EditorUi.js

@@ -846,7 +846,7 @@
 	 * @param {number} dy Y-coordinate of the translation.
 	 */
 	EditorUi.prototype.createFileData = function(node, graph, file, url, forceXml, forceSvg, forceHtml,
-		embeddedCallback, ignoreSelection, compact, nonCompressed)
+		embeddedCallback, ignoreSelection, compact, uncompressed)
 	{
 		graph = (graph != null) ? graph : this.editor.graph;
 		forceXml = (forceXml != null) ? forceXml : false;
@@ -876,7 +876,7 @@
 			// Ignores case for possible HTML or XML nodes
 			if (fileNode.nodeName.toLowerCase() != 'mxfile')
 			{
-				if (nonCompressed)
+				if (uncompressed)
 				{
 					var diagramNode = node.ownerDocument.createElement('diagram');
 					diagramNode.setAttribute('id', Editor.guid());
@@ -962,7 +962,7 @@
 				fileNode.removeAttribute('type');
 			}
 
-			var xml = (nonCompressed) ? mxUtils.getPrettyXml(fileNode) : mxUtils.getXml(fileNode);
+			var xml = (uncompressed) ? mxUtils.getPrettyXml(fileNode) : mxUtils.getXml(fileNode);
 			
 			// Writes the file as an embedded HTML file
 			if (!forceSvg && !forceXml && (forceHtml || (file != null && /(\.html)$/i.test(file.getTitle()))))
@@ -990,17 +990,17 @@
 	 * @param {number} dx X-coordinate of the translation.
 	 * @param {number} dy Y-coordinate of the translation.
 	 */
-	EditorUi.prototype.getXmlFileData = function(ignoreSelection, currentPage, nonCompressed)
+	EditorUi.prototype.getXmlFileData = function(ignoreSelection, currentPage, uncompressed)
 	{
 		ignoreSelection = (ignoreSelection != null) ? ignoreSelection : true;
 		currentPage = (currentPage != null) ? currentPage : false;
-		nonCompressed = (nonCompressed != null) ? nonCompressed : !Editor.compressXml;
+		uncompressed = (uncompressed != null) ? uncompressed : !Editor.compressXml;
 		
 		var node = this.editor.getGraphXml(ignoreSelection);
 			
 		if (ignoreSelection && this.fileNode != null && this.currentPage != null)
 		{
-			if (nonCompressed)
+			if (uncompressed)
 			{
 				EditorUi.removeChildNodes(this.currentPage.node);
 				this.currentPage.node.appendChild(node);
@@ -1030,7 +1030,7 @@
 							var temp = enc.encode(new mxGraphModel(this.pages[i].root));
 							this.editor.graph.saveViewState(this.pages[i].viewState, temp);
 							
-							if (nonCompressed)
+							if (uncompressed)
 							{
 								EditorUi.removeChildNodes(this.pages[i].node);
 								this.pages[i].node.appendChild(temp);
@@ -1043,7 +1043,7 @@
 							// Marks the page as up-to-date
 							delete this.pages[i].needsUpdate;
 						}
-						else if (nonCompressed)
+						else if (uncompressed)
 						{
 							var temp = Editor.parseDiagramNode(this.pages[i].node);
 							EditorUi.removeChildNodes(this.pages[i].node);
@@ -1322,45 +1322,50 @@
 	 * @param {number} dy Y-coordinate of the translation.
 	 */
 	EditorUi.prototype.getFileData = function(forceXml, forceSvg, forceHtml, embeddedCallback, ignoreSelection,
-		currentPage, node, compact, file, nonCompressed)
+		currentPage, node, compact, file, uncompressed)
 	{
 		ignoreSelection = (ignoreSelection != null) ? ignoreSelection : true;
 		currentPage = (currentPage != null) ? currentPage : false;
 		
-		node = (node != null) ? node : this.getXmlFileData(ignoreSelection, currentPage, nonCompressed);
-		file = (file != null) ? file : this.getCurrentFile();
-		var graph = this.editor.graph;
-		
-		// Exports SVG for first page while other page is visible by creating a graph
-		// LATER: Add caching for the graph or SVG while not on first page
-		if (this.pages != null && this.currentPage != this.pages[0] && (forceSvg ||
-			(!forceXml && file != null && /(\.svg)$/i.test(file.getTitle()))))
+		// Forces compression of embedded XML
+		if (forceSvg || (!forceXml && file != null && /(\.svg)$/i.test(file.getTitle())))
 		{
-			var graphGetGlobalVariable = graph.getGlobalVariable;
-			graph = this.createTemporaryGraph(graph.getStylesheet());
-			var page = this.pages[0];
-	
-			graph.getGlobalVariable = function(name)
+			uncompressed = false;
+			
+			// Exports SVG for first page while other page is visible by creating a graph
+			// LATER: Add caching for the graph or SVG while not on first page
+			if (this.pages != null && this.currentPage != this.pages[0])
 			{
-				if (name == 'page')
-				{
-					return page.getName();
-				}
-				else if (name == 'pagenumber')
+				var graphGetGlobalVariable = graph.getGlobalVariable;
+				graph = this.createTemporaryGraph(graph.getStylesheet());
+				var page = this.pages[0];
+		
+				graph.getGlobalVariable = function(name)
 				{
-					return 1;
-				}
-				
-				return graphGetGlobalVariable.apply(this, arguments);
-			};
-	
-			document.body.appendChild(graph.container);
-			graph.model.setRoot(page.root);
+					if (name == 'page')
+					{
+						return page.getName();
+					}
+					else if (name == 'pagenumber')
+					{
+						return 1;
+					}
+					
+					return graphGetGlobalVariable.apply(this, arguments);
+				};
+		
+				document.body.appendChild(graph.container);
+				graph.model.setRoot(page.root);
+			}
 		}
 		
+		node = (node != null) ? node : this.getXmlFileData(ignoreSelection, currentPage, uncompressed);
+		file = (file != null) ? file : this.getCurrentFile();
+		var graph = this.editor.graph;
+
 		var result = this.createFileData(node, graph, file, window.location.href,
 			forceXml, forceSvg, forceHtml, embeddedCallback, ignoreSelection, compact,
-			nonCompressed);
+			uncompressed);
 		
 		// Removes temporary graph from DOM
 		if (graph != this.editor.graph)
@@ -1625,7 +1630,7 @@
 	 * @param {number} dx X-coordinate of the translation.
 	 * @param {number} dy Y-coordinate of the translation.
 	 */
-	EditorUi.prototype.downloadFile = function(format, nonCompressed, addShadow, ignoreSelection, currentPage, pageVisible, transparent, scale, border, grid)
+	EditorUi.prototype.downloadFile = function(format, uncompressed, addShadow, ignoreSelection, currentPage, pageVisible, transparent, scale, border, grid)
 	{
 		try
 		{
@@ -1637,7 +1642,7 @@
 			{
 		    	var data = '<?xml version="1.0" encoding="UTF-8"?>\n' +
 		    		this.getFileData(true, null, null, null, ignoreSelection, currentPage,
-		    			null, null, null, nonCompressed);
+		    			null, null, null, uncompressed);
 		    	
 		    	this.saveData(filename, format, data, 'text/xml');
 			}
@@ -4452,7 +4457,8 @@
 				
 				if (editable)
 				{
-					svgRoot.setAttribute('content', this.getFileData(true, null, null, null, ignoreSelection, currentPage));
+					svgRoot.setAttribute('content', this.getFileData(true, null, null, null, ignoreSelection,
+						currentPage, null, null, null, false));
 				}
 				
 				if (this.editor.fontCss != null)
@@ -8907,6 +8913,7 @@
 			var showRuler = urlParams['ruler'] == '1' || (mxSettings.isRulerOn() && urlParams['lightbox'] != '1');
 			
 			this.ruler = showRuler? new mxDualRuler(this, view.unit) : null;
+			this.refresh();
 		}
 		
 		// Adds an element to edit the style in the footer in test mode
@@ -10891,8 +10898,9 @@
 				        	{
 				        		bg = null;
 				        	}
-					        	
-							msg.xml = this.getFileData(true);
+
+							msg.xml = this.getFileData(true, null, null, null, null,
+								null, null, null, null, false);
 							msg.format = 'svg';
 					        	
 				        	if (data.embedImages || data.embedImages == null)

+ 2 - 0
src/main/webapp/js/diagramly/Menus.js

@@ -170,10 +170,12 @@
 			{
 				editorUi.ruler.destroy();
 				editorUi.ruler = null;
+				editorUi.refresh();
 			}
 			else
 			{
 				editorUi.ruler = new mxDualRuler(editorUi, editorUi.editor.graph.view.unit);
+				editorUi.refresh();
 			}
 		});
 		rulerAction.setToggleAction(true);

+ 18 - 33
src/main/webapp/js/diagramly/mxRuler.js

@@ -19,7 +19,7 @@
 
 function mxRuler(editorUi, unit, isVertical, isSecondery) 
 {
-	var RULER_THICKNESS = 14;
+	var RULER_THICKNESS = this.RULER_THICKNESS;
     var ruler = this;
     this.unit = unit;
     var style = window.uiTheme != 'dark'? {
@@ -49,45 +49,23 @@ function mxRuler(editorUi, unit, isVertical, isSecondery)
 	
 	function resizeRulerContainer()
 	{
-	    container.style.top = editorUi.origContTop + 'px';
-	    container.style.left = editorUi.origContLeft + 'px';
-	    container.style.width = (isVertical? RULER_THICKNESS : editorUi.origContWidth) + 'px';
-	    container.style.height = (isVertical? editorUi.origContHeight : RULER_THICKNESS) + 'px';
+		var diagCont = editorUi.diagramContainer;
+		
+	    container.style.top = (diagCont.offsetTop - RULER_THICKNESS) + 'px';
+	    container.style.left = (diagCont.offsetLeft - RULER_THICKNESS) + 'px';
+	    container.style.width = ((isVertical? 0 : diagCont.offsetWidth) + RULER_THICKNESS) + 'px';
+	    container.style.height = ((isVertical? diagCont.offsetHeight : 0) + RULER_THICKNESS) + 'px';
 	};
     
 	this.editorUiRefresh = editorUi.refresh;
 	
 	editorUi.refresh = function(minor)
 	{
-		//If it is called with true, then only our added code is executed
-		if (minor != true) 
-		{
-			ruler.editorUiRefresh.apply(editorUi, arguments);
-		}
-		
-		var cont = editorUi.diagramContainer;
-		
-		if (!isSecondery)
-		{
-			editorUi.origContTop = cont.offsetTop;
-			editorUi.origContLeft = cont.offsetLeft;
-			editorUi.origContWidth = cont.offsetWidth;
-			editorUi.origContHeight = cont.offsetHeight;
-		}
+		ruler.editorUiRefresh.apply(editorUi, arguments);
 		
 		resizeRulerContainer();
-		
-		if (isVertical)
-		{
-			cont.style.left = (cont.offsetLeft + RULER_THICKNESS) + 'px';
-		}
-		else
-		{
-			cont.style.top = (cont.offsetTop + RULER_THICKNESS) + 'px';
-		}
 	};
 
-	editorUi.refresh(true);
 	resizeRulerContainer();
     	
     var canvas = document.createElement('canvas');
@@ -437,6 +415,7 @@ function mxRuler(editorUi, unit, isVertical, isSecondery)
 	};
 };
 
+mxRuler.prototype.RULER_THICKNESS = 14;
 mxRuler.prototype.unit = mxConstants.POINTS;
 
 mxRuler.prototype.setUnit = function(unit) 
@@ -474,13 +453,18 @@ mxRuler.prototype.destroy = function()
     {
     	this.container.parentNode.removeChild(this.container);
     }
-    
-	this.ui.diagramContainer.style.left = this.ui.origContLeft + 'px';
-	this.ui.diagramContainer.style.top = this.ui.origContTop + 'px';
 };
 
 function mxDualRuler(editorUi, unit)
 {
+	var rulerOffset = new mxPoint(mxRuler.prototype.RULER_THICKNESS, mxRuler.prototype.RULER_THICKNESS);
+	this.editorUiGetDiagContOffset = editorUi.getDiagramContainerOffset;
+
+	editorUi.getDiagramContainerOffset = function()
+	{
+		return rulerOffset;
+	};
+
 	this.editorUiRefresh = editorUi.refresh;
 	this.ui = editorUi;
 	this.origGuideMove = mxGuide.prototype.move;
@@ -563,4 +547,5 @@ mxDualRuler.prototype.destroy = function()
 	this.ui.refresh = this.editorUiRefresh;
 	mxGuide.prototype.move = this.origGuideMove;
 	mxGuide.prototype.destroy = this.origGuideDestroy;
+	this.ui.getDiagramContainerOffset = this.editorUiGetDiagContOffset;
 };

+ 12 - 3
src/main/webapp/js/mxgraph/EditorUi.js

@@ -2973,6 +2973,13 @@ EditorUi.prototype.updateActionStates = function()
     this.updatePasteActionStates();
 };
 
+EditorUi.prototype.zeroOffset = new mxPoint(0, 0);
+
+EditorUi.prototype.getDiagramContainerOffset = function()
+{
+	return this.zeroOffset;
+};
+
 /**
  * Refreshes the viewport.
  */
@@ -3043,8 +3050,10 @@ EditorUi.prototype.refresh = function(sizeDidChange)
 	this.formatContainer.style.width = fw + 'px';
 	this.formatContainer.style.display = (this.format != null) ? '' : 'none';
 	
-	this.diagramContainer.style.left = (this.hsplit.parentNode != null) ? (effHsplitPosition + this.splitSize) + 'px' : '0px';
-	this.diagramContainer.style.top = this.sidebarContainer.style.top;
+	var diagContOffset = this.getDiagramContainerOffset();
+	var contLeft = (this.hsplit.parentNode != null) ? (effHsplitPosition + this.splitSize) : 0;
+	this.diagramContainer.style.left =  (contLeft + diagContOffset.x) + 'px';
+	this.diagramContainer.style.top = (tmp + diagContOffset.y) + 'px';
 	this.footerContainer.style.height = this.footerHeight + 'px';
 	this.hsplit.style.top = this.sidebarContainer.style.top;
 	this.hsplit.style.bottom = (this.footerHeight + off) + 'px';
@@ -3053,7 +3062,7 @@ EditorUi.prototype.refresh = function(sizeDidChange)
 	
 	if (this.tabContainer != null)
 	{
-		this.tabContainer.style.left = this.diagramContainer.style.left;
+		this.tabContainer.style.left = contLeft + 'px';
 	}
 	
 	if (quirks)

+ 1 - 1
src/main/webapp/js/mxgraph/Sidebar.js

@@ -1806,7 +1806,7 @@ Sidebar.prototype.addBpmnPalette = function(dir, expand)
 	 	this.createVertexTemplateEntry('shape=mxgraph.bpmn.business_rule_task;html=1;outlineConnect=0;', 14, 14, '', 'Business Rule Task', null, null, this.getTagsForStencil('mxgraph.bpmn', 'business_rule_task').join(' ')),
 	 	this.createVertexTemplateEntry('shape=mxgraph.bpmn.service_task;html=1;outlineConnect=0;', 14, 14, '', 'Service Task', null, null, this.getTagsForStencil('mxgraph.bpmn', 'service_task').join(' ')),
 	 	this.createVertexTemplateEntry('shape=mxgraph.bpmn.script_task;html=1;outlineConnect=0;', 14, 14, '', 'Script Task', null, null, this.getTagsForStencil('mxgraph.bpmn', 'script_task').join(' ')),
-		this.createVertexTemplateEntry('html=1;shape=mxgraph.flowchart.annotation_2;align=left;', 50, 100, '', 'Annotation', null, null, this.getTagsForStencil('bpmn', 'annotation_1', 'bpmn business process model ').join(' ')),
+		this.createVertexTemplateEntry('html=1;shape=mxgraph.flowchart.annotation_2;align=left;labelPosition=right;', 50, 100, '', 'Annotation', null, null, this.getTagsForStencil('bpmn', 'annotation_1', 'bpmn business process model ').join(' ')),
 		this.createVertexTemplateEntry('rounded=1;arcSize=10;dashed=1;strokeColor=#000000;fillColor=none;gradientColor=none;dashPattern=8 3 1 3;strokeWidth=2;',
 				 200, 200, '', 'Group', null, null, this.getTagsForStencil('bpmn', 'group', 'bpmn business process model ').join(' ')),
 	 	this.createEdgeTemplateEntry('endArrow=block;endFill=1;endSize=6;html=1;', 100, 0, '', 'Sequence Flow', null, 'bpmn sequence flow'),

Файловите разлики са ограничени, защото са твърде много
+ 621 - 620
src/main/webapp/js/viewer.min.js


+ 1 - 1
src/main/webapp/package.json

@@ -1,6 +1,6 @@
 {
   "name": "draw.io",
-  "version": "10.8.0",
+  "version": "11.3.0",
   "description": "draw.io desktop",
   "main": "electron.js",
   "scripts": {

+ 1 - 1
src/main/webapp/resources/dia_de.txt

@@ -54,7 +54,7 @@ aws3d=AWS 3D
 azure=Azure
 back=Zurück
 background=Hintergrund
-backgroundColor=Hintegrundfarbe
+backgroundColor=Hintergrundfarbe
 backgroundImage=Hintergrundbild
 basic=Einfach
 blankDrawing=Leere Zeichnung