浏览代码

13.1.13 release

Gaudenz Alder 5 年之前
父节点
当前提交
461a1ff1d1

+ 24 - 1
ChangeLog

@@ -1,4 +1,27 @@
-29-MAY-2020: 13.1.9
+02-JUN-2020: 13.1.13
+
+- Fixes caching for HTML embed code
+- Fixes copy paste from other apps
+- Fixes math client-side printing
+- Adds math-output URL parameter
+- Adds custom table properties
+- Uses mxGraph 4.2.0 beta 7
+
+01-JUN-2020: 13.1.12
+
+- Uses mxGraph 4.2.0 beta 6
+- Adds tables to Misc sidebar
+- Fixes OS clipboard in Firefox
+
+31-MAY-2020: 13.1.11
+
+- Stability improvements and bug fixes
+
+31-MAY-2020: 13.1.10
+
+- Fixes minor bugs for tables
+
+30-MAY-2020: 13.1.9
 
 
 - Fixes insert tables on touch devices
 - Fixes insert tables on touch devices
 - Adds Feature-Policy header
 - Adds Feature-Policy header

+ 1 - 1
VERSION

@@ -1 +1 @@
-13.1.9
+13.1.13

文件差异内容过多而无法显示
+ 3 - 3
etc/mxgraph/mxClient.js


+ 190 - 169
src/main/java/com/mxgraph/online/AbsAuthServlet.java

@@ -9,19 +9,27 @@ import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.io.OutputStream;
 import java.io.PrintWriter;
 import java.io.PrintWriter;
+import java.io.StringWriter;
 import java.math.BigInteger;
 import java.math.BigInteger;
 import java.net.HttpURLConnection;
 import java.net.HttpURLConnection;
 import java.net.URL;
 import java.net.URL;
 import java.security.SecureRandom;
 import java.security.SecureRandom;
-import java.util.Date;
 import java.util.HashMap;
 import java.util.HashMap;
+import java.util.Map;
 
 
+import javax.cache.Cache;
+import javax.cache.CacheException;
+import javax.cache.CacheFactory;
+import javax.cache.CacheManager;
 import javax.servlet.ServletException;
 import javax.servlet.ServletException;
 import javax.servlet.http.Cookie;
 import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpServletResponse;
 
 
+import com.google.appengine.api.memcache.MemcacheService;
+import com.google.appengine.api.memcache.stdimpl.GCacheFactory;
+
 @SuppressWarnings("serial")
 @SuppressWarnings("serial")
 abstract public class AbsAuthServlet extends HttpServlet
 abstract public class AbsAuthServlet extends HttpServlet
 {
 {
@@ -33,8 +41,25 @@ abstract public class AbsAuthServlet extends HttpServlet
 	private static final int COOKIE_AGE = 600;
 	private static final int COOKIE_AGE = 600;
 	
 	
 	public static final SecureRandom random = new SecureRandom();
 	public static final SecureRandom random = new SecureRandom();
-	public static BigInteger prime1 = null, prime2 = null;
-	public static long lastPrimeChange = 0;
+	protected static Cache tokens;
+	
+	static
+	{
+		try
+		{
+			CacheFactory cacheFactory = CacheManager.getInstance()
+					.getCacheFactory();
+			Map<Object, Object> properties = new HashMap<>();
+			properties.put(MemcacheService.SetPolicy.ADD_ONLY_IF_NOT_PRESENT,
+					true);
+			properties.put(GCacheFactory.EXPIRATION_DELTA, COOKIE_AGE); //Cache servlet set it to 300 (5 min), all cache instances are the same so 5 will be enforced
+			tokens = cacheFactory.createCache(properties);
+		}
+		catch (CacheException e)
+		{
+			e.printStackTrace();
+		}
+	}
 	
 	
 	protected int postType = X_WWW_FORM_URLENCODED; 
 	protected int postType = X_WWW_FORM_URLENCODED; 
 	
 	
@@ -96,29 +121,17 @@ abstract public class AbsAuthServlet extends HttpServlet
 	protected void doGet(HttpServletRequest request,
 	protected void doGet(HttpServletRequest request,
 			HttpServletResponse response) throws ServletException, IOException
 			HttpServletResponse response) throws ServletException, IOException
 	{
 	{
-		//Prime secrets changes every 10 minutes
-		if (new Date().getTime() - lastPrimeChange > COOKIE_AGE * 1000)
-		{
-			synchronized (AbsAuthServlet.class)
-			{
-				//Recheck after acquiring the lock
-				if (new Date().getTime() - lastPrimeChange > COOKIE_AGE * 1000)
-				{
-					prime2 = prime1;
-					prime1 = BigInteger.probablePrime(256, random);
-					lastPrimeChange = new Date().getTime();
-				}
-			}
-		}
-		
 		String stateOnly = request.getParameter("getState");
 		String stateOnly = request.getParameter("getState");
 		
 		
 		if ("1".equals(stateOnly))
 		if ("1".equals(stateOnly))
 		{
 		{
-			String state = new BigInteger(256, random).multiply(prime1).toString(32);
+			String state = new BigInteger(256, random).toString(32);
+			String key = new BigInteger(256, random).toString(32);
+			String casheKey = request.getRemoteAddr() + ":" + key;
+			tokens.put(casheKey, state);
 			response.setStatus(HttpServletResponse.SC_OK);
 			response.setStatus(HttpServletResponse.SC_OK);
 			//Chrome blocks this cookie when draw.io is running in an iframe. The cookie is added to parent frame. TODO FIXME
 			//Chrome blocks this cookie when draw.io is running in an iframe. The cookie is added to parent frame. TODO FIXME
-			response.setHeader("Set-Cookie", STATE_COOKIE + "=" + state + "; Max-Age=" + COOKIE_AGE + "; Secure; HttpOnly; SameSite=none"); //10 min to finish auth
+			response.setHeader("Set-Cookie", STATE_COOKIE + "=" + key + "; Max-Age=" + COOKIE_AGE + "; Secure; HttpOnly; SameSite=none"); //10 min to finish auth
 			response.setHeader("Content-Type", "text/plain");
 			response.setHeader("Content-Type", "text/plain");
 			OutputStream out = response.getOutputStream();
 			OutputStream out = response.getOutputStream();
 			out.write(state.getBytes());
 			out.write(state.getBytes());
@@ -161,18 +174,8 @@ abstract public class AbsAuthServlet extends HttpServlet
 				{
 				{
 					if (STATE_COOKIE.equals(cookie.getName()))
 					if (STATE_COOKIE.equals(cookie.getName()))
 					{
 					{
-						//Ensure cookie value is divisible with prime1 or prime2
-						String val = cookie.getValue();
-						BigInteger iVal = new BigInteger(val, 32);
-						
-						if (iVal.mod(prime1).equals(BigInteger.ZERO))
-						{
-							cookieToken = val;
-						}
-						else if (prime2 != null && iVal.mod(prime2).equals(BigInteger.ZERO))
-						{
-							cookieToken = val;
-						}
+						//Get the cached state based on the cookie key 
+						cookieToken = (String) tokens.get(request.getRemoteAddr() + ":" + cookie.getValue());
 						break;
 						break;
 					}
 					}
 				}
 				}
@@ -213,158 +216,176 @@ abstract public class AbsAuthServlet extends HttpServlet
 			}
 			}
 			else
 			else
 			{
 			{
-				HttpURLConnection con = null;
+				Response authResp = contactOAuthServer(CONFIG.AUTH_SERVICE_URL, code, refreshToken, secret, client, redirectUri, 1);
+				response.setStatus(authResp.status);
 				
 				
-				try
+				if (authResp.content != null)
 				{
 				{
-					String url = CONFIG.AUTH_SERVICE_URL;
-					URL obj = new URL(url);
-					con = (HttpURLConnection) obj.openConnection();
-		
-					con.setRequestMethod("POST");
-					
-					boolean jsonResponse = false;
-					StringBuilder urlParameters = new StringBuilder();
+					OutputStream out = response.getOutputStream();
+					PrintWriter writer = new PrintWriter(out);
+					writer.println(authResp.content);
+					writer.flush();
+					writer.close();
+				}
+			}
+		}
+		catch (Exception e) 
+		{
+			response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+		}
+	}
 
 
-					if (postType == X_WWW_FORM_URLENCODED)
-					{
-						con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
-		
-						urlParameters.append("client_id=");
-						urlParameters.append(client);
-						urlParameters.append("&redirect_uri=");
-						urlParameters.append(redirectUri);
-						urlParameters.append("&client_secret=");
-						urlParameters.append(secret);
-					
-						if (code != null)
-						{
-							urlParameters.append("&code=");
-							urlParameters.append(code);
-							urlParameters.append("&grant_type=authorization_code");
-						}
-						else
-						{
-							urlParameters.append("&refresh_token=");
-							urlParameters.append(refreshToken);
-							urlParameters.append("&grant_type=refresh_token");
-							jsonResponse = true;
-						}
-					}
-					else if (postType == JSON)
-					{
-						con.setRequestProperty("Content-Type", "application/json");
-						
-						urlParameters.append("{");
-						urlParameters.append("\"client_id\": \"");
-						urlParameters.append(client);
-						urlParameters.append("\", \"redirect_uri\": \"");
-						urlParameters.append(redirectUri);
-						urlParameters.append("\", \"client_secret\": \"");
-						urlParameters.append(secret);
-					
-						if (code != null)
-						{
-							urlParameters.append("\", \"code\": \"");
-							urlParameters.append(code);
-							urlParameters.append("\", \"grant_type\": \"authorization_code\"}");
-						}
-						else
-						{
-							urlParameters.append("\", \"refresh_token\": \"");
-							urlParameters.append(refreshToken);
-							urlParameters.append("\", \"grant_type\": \"refresh_token\"}");
-							jsonResponse = true;
-						}
-					}
-					
-					// Send post request
-					con.setDoOutput(true);
-					DataOutputStream wr = new DataOutputStream(con.getOutputStream());
-					wr.writeBytes(urlParameters.toString());
-					wr.flush();
-					wr.close();
+	class Response
+	{
+		public int status = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
+		public String content = null;
+	}
+	
+	private Response contactOAuthServer(String authSrvUrl, String code, String refreshToken, String secret,
+			String client, String redirectUri, int retryCount)
+	{
+		HttpURLConnection con = null;
+		Response response = new Response();
 		
 		
+		try
+		{
+			URL obj = new URL(authSrvUrl);
+			con = (HttpURLConnection) obj.openConnection();
+
+			con.setRequestMethod("POST");
+			
+			boolean jsonResponse = false;
+			StringBuilder urlParameters = new StringBuilder();
+
+			if (postType == X_WWW_FORM_URLENCODED)
+			{
+				con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+
+				urlParameters.append("client_id=");
+				urlParameters.append(client);
+				urlParameters.append("&redirect_uri=");
+				urlParameters.append(redirectUri);
+				urlParameters.append("&client_secret=");
+				urlParameters.append(secret);
+			
+				if (code != null)
+				{
+					urlParameters.append("&code=");
+					urlParameters.append(code);
+					urlParameters.append("&grant_type=authorization_code");
+				}
+				else
+				{
+					urlParameters.append("&refresh_token=");
+					urlParameters.append(refreshToken);
+					urlParameters.append("&grant_type=refresh_token");
+					jsonResponse = true;
+				}
+			}
+			else if (postType == JSON)
+			{
+				con.setRequestProperty("Content-Type", "application/json");
+				
+				urlParameters.append("{");
+				urlParameters.append("\"client_id\": \"");
+				urlParameters.append(client);
+				urlParameters.append("\", \"redirect_uri\": \"");
+				urlParameters.append(redirectUri);
+				urlParameters.append("\", \"client_secret\": \"");
+				urlParameters.append(secret);
+			
+				if (code != null)
+				{
+					urlParameters.append("\", \"code\": \"");
+					urlParameters.append(code);
+					urlParameters.append("\", \"grant_type\": \"authorization_code\"}");
+				}
+				else
+				{
+					urlParameters.append("\", \"refresh_token\": \"");
+					urlParameters.append(refreshToken);
+					urlParameters.append("\", \"grant_type\": \"refresh_token\"}");
+					jsonResponse = true;
+				}
+			}
+			
+			// Send post request
+			con.setDoOutput(true);
+			DataOutputStream wr = new DataOutputStream(con.getOutputStream());
+			wr.writeBytes(urlParameters.toString());
+			wr.flush();
+			wr.close();
+
+			BufferedReader in = new BufferedReader(
+					new InputStreamReader(con.getInputStream()));
+			String inputLine;
+			StringBuffer authRes = new StringBuffer();
+
+			while ((inputLine = in.readLine()) != null)
+			{
+				authRes.append(inputLine);
+			}
+			in.close();
+
+			response.status = con.getResponseCode();
+			// Writes JavaScript code
+			response.content = processAuthResponse(authRes.toString(), jsonResponse);
+		}
+		catch(IOException e)
+		{
+			StringBuilder details = new StringBuilder("");
+			
+			if (con != null)
+			{
+				try 
+				{
 					BufferedReader in = new BufferedReader(
 					BufferedReader in = new BufferedReader(
-							new InputStreamReader(con.getInputStream()));
+							new InputStreamReader(con.getErrorStream()));
+					
 					String inputLine;
 					String inputLine;
-					StringBuffer authRes = new StringBuffer();
-		
+
 					while ((inputLine = in.readLine()) != null)
 					while ((inputLine = in.readLine()) != null)
 					{
 					{
-						authRes.append(inputLine);
+						System.err.println(inputLine);
+						details.append(inputLine);
+						details.append("\n");
 					}
 					}
 					in.close();
 					in.close();
-	
-					response.setStatus(con.getResponseCode());
-					
-					OutputStream out = response.getOutputStream();
-		
-					PrintWriter writer = new PrintWriter(out);
-	
-					// Writes JavaScript code
-					writer.println(processAuthResponse(authRes.toString(), jsonResponse));
-		
-					writer.flush();
-					writer.close();
 				}
 				}
-				catch(IOException e)
+				catch (Exception e2) 
 				{
 				{
-					StringBuilder details = new StringBuilder("");
-					
-					if (con != null)
-					{
-						try 
-						{
-							BufferedReader in = new BufferedReader(
-									new InputStreamReader(con.getErrorStream()));
-							
-							String inputLine;
-		
-							while ((inputLine = in.readLine()) != null)
-							{
-								System.err.println(inputLine);
-								details.append(inputLine);
-								details.append("\n");
-							}
-							in.close();
-						}
-						catch (Exception e2) 
-						{
-							// Ignore
-						}
-					}
-					
-					if (e.getMessage() != null && e.getMessage().contains("401"))
-					{
-						response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
-					}
-					else
-					{
-						response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-						e.printStackTrace();
-						System.err.println(details);
-					}
-					
-					if (DEBUG)
-					{
-						OutputStream out = response.getOutputStream();
-						
-						PrintWriter writer = new PrintWriter(out);
-		
-						e.printStackTrace(writer);
-						writer.println(details.toString());
-			
-						writer.flush();
-						writer.close();
-					}
+					// Ignore
 				}
 				}
 			}
 			}
+			
+			if (e.getMessage() != null && e.getMessage().contains("401"))
+			{
+				response.status = HttpServletResponse.SC_UNAUTHORIZED;
+			}
+			else if (retryCount > 0 && e.getMessage() != null && e.getMessage().contains("Connection timed out"))
+		    {
+				return contactOAuthServer(authSrvUrl, code, refreshToken, secret,
+						client, redirectUri, --retryCount);
+		    }
+			else
+			{
+				response.status = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
+				e.printStackTrace();
+				System.err.println(details);
+			}
+			
+			if (DEBUG)
+			{
+				StringWriter sw = new StringWriter();
+				PrintWriter pw = new PrintWriter(sw);
+				e.printStackTrace(pw);
+				pw.println(details.toString());
+				pw.flush();
+				response.content = sw.toString();
+			}
 		}
 		}
-		catch (Exception e) 
-		{
-			response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-		}
+		
+		return response;
 	}
 	}
 
 
 }
 }

+ 5 - 0
src/main/java/com/mxgraph/online/EmbedServlet2.java

@@ -227,6 +227,11 @@ public class EmbedServlet2 extends HttpServlet
 		response.setCharacterEncoding("UTF-8");
 		response.setCharacterEncoding("UTF-8");
 		response.setContentType("application/javascript; charset=UTF-8");
 		response.setContentType("application/javascript; charset=UTF-8");
 		response.setHeader("Last-Modified", lastModified);
 		response.setHeader("Last-Modified", lastModified);
+		
+		if (request.getParameter("fetch") != null)
+		{
+			response.setHeader("Cache-Control", "no-store");
+		}
 
 
 		OutputStream out = response.getOutputStream();
 		OutputStream out = response.getOutputStream();
 
 

+ 0 - 1
src/main/webapp/WEB-INF/appengine-web.xml

@@ -13,7 +13,6 @@
   <!-- Path patterns not supported in production -->
   <!-- Path patterns not supported in production -->
   <static-files>
   <static-files>
     <include path="/**">
     <include path="/**">
-	  <http-header name="Feature-Policy" value="accelerometer 'none'; ambient-light-sensor 'none'; battery 'none'; camera 'none'; display-capture 'none'; document-domain 'none; encrypted-media 'none'; fullscreen 'none'; geolocation 'none'; gyroscope 'none'; magnetometer 'none'; midi 'none'; navigation-override 'none'; payment 'none'; picture-in-picture 'none'; publickey-credentials-get 'none'; usb 'none'; wake-lock 'none'; xr-spatial-tracking 'none';"/>	
       <http-header name="Referrer-Policy" value="strict-origin" />
       <http-header name="Referrer-Policy" value="strict-origin" />
 	  <http-header name="Access-Control-Allow-Origin" value="*"/>
 	  <http-header name="Access-Control-Allow-Origin" value="*"/>
 	</include>
 	</include>

+ 9 - 2
src/main/webapp/connect/confluence/admin.html

@@ -12,7 +12,9 @@
 			<h4 style="padding-top:20px;" data-i18n="gliffyImport">Gliffy Import</h4>
 			<h4 style="padding-top:20px;" data-i18n="gliffyImport">Gliffy Import</h4>
 			<div><br><span data-i18n="gliffyImportInst1">Click the "Start Import" button to import all Gliffy diagrams to draw.io.</span><br>
 			<div><br><span data-i18n="gliffyImportInst1">Click the "Start Import" button to import all Gliffy diagrams to draw.io.</span><br>
 			<span data-i18n="gliffyImportInst2">Please note that the import procedure will take some time and the browser window must remain open until the import is completed.</span></div>
 			<span data-i18n="gliffyImportInst2">Please note that the import procedure will take some time and the browser window must remain open until the import is completed.</span></div>
-			<div style="padding-bottom:4px;"><br><button id="importBtn" disabled="disabled" class="aui-button aui-button-primary" data-i18n="startImport">Start Import</button></div>
+			<div style="padding-bottom:4px;"><br><button id="importBtn" disabled="disabled" class="aui-button aui-button-primary" data-i18n="startImport">Start Import</button>
+				<img id="busyIcon" src="/images/spin.gif" style="display: none">
+			</div>
 			<div id="operationLog"><br></div>
 			<div id="operationLog"><br></div>
 		</div>
 		</div>
 		<script type="text/javascript">
 		<script type="text/javascript">
@@ -48,7 +50,12 @@
 					
 					
 					importBtn.click(function()
 					importBtn.click(function()
 					{
 					{
-						GliffyMassImporter(logDiv);
+						$('#busyIcon').show();
+						
+						GliffyMassImporter(logDiv, function()
+						{
+							$('#busyIcon').hide();
+						});
 					});
 					});
 				});
 				});
 			};
 			};

+ 18 - 13
src/main/webapp/connect/confluence/admin.js

@@ -409,7 +409,7 @@ var MassDiagramsProcessor = function(macroName, readableName, attParams, process
 	});
 	});
 };
 };
 
 
-var GliffyMassImporter = function(logDiv) 
+var GliffyMassImporter = function(logDiv, doneFn) 
 {
 {
 	var link = document.createElement('a');
 	var link = document.createElement('a');
 	link.href = location.href;
 	link.href = location.href;
@@ -513,7 +513,7 @@ var GliffyMassImporter = function(logDiv)
 	
 	
 	logDiv.html("<br>");
 	logDiv.html("<br>");
 	
 	
-	MassDiagramsProcessor('gliffy', 'Gliffy', ['name'], importGliffyAtt, logDiv);
+	MassDiagramsProcessor('gliffy', 'Gliffy', ['name'], importGliffyAtt, logDiv, doneFn);
 };
 };
 
 
 function cleanBrokenCustomContents(logDiv, callback, error)
 function cleanBrokenCustomContents(logDiv, callback, error)
@@ -547,7 +547,7 @@ function cleanBrokenCustomContents(logDiv, callback, error)
 			obj[diagramName] = {id: contentId, ver: contentVer};
 			obj[diagramName] = {id: contentId, ver: contentVer};
 			pageCustomContents[pageId] = obj;
 			pageCustomContents[pageId] = obj;
 		}
 		}
-		else if (pageCustomContents[pageId][diagramName])
+		else if (pageCustomContents[pageId][diagramName] && pageCustomContents[pageId][diagramName].id != contentId) //Sometimes, search returns duplicate entries!
 		{
 		{
 			customContent2Del.push({id: contentId, name: diagramName, duplicate: true});
 			customContent2Del.push({id: contentId, name: diagramName, duplicate: true});
 			return false;
 			return false;
@@ -601,11 +601,13 @@ function cleanBrokenCustomContents(logDiv, callback, error)
 		}
 		}
 	};
 	};
 	
 	
-    function collectAtts(pageId, callback, error, url, atts)
+    function collectAtts(pageId, callback, error, start, atts)
     {
     {
     	//first call
     	//first call
-    	if (url == null)
+    	if (start == null)
     	{
     	{
+    		start = 0;
+
     		if (typeof(pendingCallbacks[pageId]) === 'undefined')
     		if (typeof(pendingCallbacks[pageId]) === 'undefined')
 			{
 			{
     			atts = {};
     			atts = {};
@@ -620,7 +622,7 @@ function cleanBrokenCustomContents(logDiv, callback, error)
     	
     	
     	AP.request({
     	AP.request({
             type: 'GET',
             type: 'GET',
-			url: url != null? url : '/rest/api/content/' + pageId + '/child/attachment?limit=100&expand=version',
+			url: '/rest/api/content/' + pageId + '/child/attachment?limit=100&expand=version&start=' + start,
             contentType: 'application/json;charset=UTF-8',
             contentType: 'application/json;charset=UTF-8',
             success: function (resp) 
             success: function (resp) 
             {
             {
@@ -635,7 +637,8 @@ function cleanBrokenCustomContents(logDiv, callback, error)
                	//Support pageing
                	//Support pageing
 				if (resp._links && resp._links.next) 
 				if (resp._links && resp._links.next) 
 				{
 				{
-					collectAtts(pageId, callback, error, resp._links.next, atts);
+					start += resp.limit; //Sometimes the limit is changed by the server
+					collectAtts(pageId, callback, error, start, atts);
 				}
 				}
 				else
 				else
 				{
 				{
@@ -672,10 +675,10 @@ function cleanBrokenCustomContents(logDiv, callback, error)
 		});
 		});
     };
     };
     
     
-	function processChunk(nextUrl)
+	function processChunk(start)
 	{
 	{
 		AP.request({
 		AP.request({
-			url: nextUrl != null? nextUrl : '/rest/api/content/search?cql=' + encodeURIComponent('type="ac:com.mxgraph.confluence.plugins.diagramly:drawio-diagram"') + '&limit=50&expand=body.storage,version',  
+			url: '/rest/api/content/search?cql=' + encodeURIComponent('type="ac:com.mxgraph.confluence.plugins.diagramly:drawio-diagram"') + '&limit=50&expand=body.storage,version&start=' + start,  
 			success: function(resp) 
 			success: function(resp) 
 			{
 			{
 				resp = JSON.parse(resp);
 				resp = JSON.parse(resp);
@@ -748,6 +751,7 @@ function cleanBrokenCustomContents(logDiv, callback, error)
 							//ignore, this should not happen! But, if it happens, it means a corrupted custom content. Just delete it
 							//ignore, this should not happen! But, if it happens, it means a corrupted custom content. Just delete it
 							console.log(e);
 							console.log(e);
 							customContent2Del.push({id: list[i].id, name: list[i].title});
 							customContent2Del.push({id: list[i].id, name: list[i].title});
+							processedItems++;
 							checkDone();
 							checkDone();
 						}
 						}
 					}
 					}
@@ -758,7 +762,8 @@ function cleanBrokenCustomContents(logDiv, callback, error)
 				//Support pageing
 				//Support pageing
 				if (resp._links && resp._links.next) 
 				if (resp._links && resp._links.next) 
 				{
 				{
-					processChunk(resp._links.next);
+					start += resp.limit; //Sometimes the limit is changed by the server
+					processChunk(start);
 				}
 				}
 				else
 				else
 				{
 				{
@@ -770,10 +775,10 @@ function cleanBrokenCustomContents(logDiv, callback, error)
 		});
 		});
 	};
 	};
 	
 	
-	processChunk();
+	processChunk(0);
 };
 };
 
 
-var DrawIoDiagramsIndexer = function(logDiv)
+var DrawIoDiagramsIndexer = function(logDiv, doneFn)
 {
 {
 	var pageCustomContents = {}, customContentsMap = {};
 	var pageCustomContents = {}, customContentsMap = {};
 	
 	
@@ -890,7 +895,7 @@ var DrawIoDiagramsIndexer = function(logDiv)
 		
 		
 		MassDiagramsProcessor('drawio', 'draw.io', 
 		MassDiagramsProcessor('drawio', 'draw.io', 
 				drawioMacroParams,
 				drawioMacroParams,
-				fixDrawIoCustomContent, logDiv);
+				fixDrawIoCustomContent, logDiv, doneFn);
 	});
 	});
 };
 };
 
 

+ 3 - 1
src/main/webapp/connect/confluence/config.html

@@ -161,7 +161,9 @@
 			    <h4 data-i18n="refreshDrawIndex">Refresh draw.io Diagrams Index</h4>
 			    <h4 data-i18n="refreshDrawIndex">Refresh draw.io Diagrams Index</h4>
 				<div><br><span data-i18n="reindexInst1">Click the "Start Indexing" button to refresh draw.io diagrams index.</span><br>
 				<div><br><span data-i18n="reindexInst1">Click the "Start Indexing" button to refresh draw.io diagrams index.</span><br>
 				<span data-i18n="reindexInst2">Please note that the indexing procedure will take some time and the browser window must remain open until the indexing is completed.</span></div>
 				<span data-i18n="reindexInst2">Please note that the indexing procedure will take some time and the browser window must remain open until the indexing is completed.</span></div>
-				<div><br><button id="indexBtn" disabled="disabled" class="aui-button aui-button-primary" data-i18n="startIndexing">Start Indexing</button></div>
+				<div><br><button id="indexBtn" disabled="disabled" class="aui-button aui-button-primary" data-i18n="startIndexing">Start Indexing</button>
+					<img id="DRIbusyIcon" src="/images/spin.gif" style="display: none">
+				</div>
 				<div id="operationLog"><br></div>
 				<div id="operationLog"><br></div>
 			</div>
 			</div>
 		</div>
 		</div>

+ 5 - 1
src/main/webapp/connect/confluence/config.js

@@ -1622,7 +1622,11 @@ script.onload = function()
 		
 		
 		indexBtn.click(function()
 		indexBtn.click(function()
 		{
 		{
-			DrawIoDiagramsIndexer($('#operationLog'));
+			$('#DRIbusyIcon').show();
+			DrawIoDiagramsIndexer($('#operationLog'), function()
+			{
+				$('#DRIbusyIcon').hide();
+			});
 		});
 		});
 		
 		
 		var exportBtn = $('#exportBtn');
 		var exportBtn = $('#exportBtn');

+ 5 - 4
src/main/webapp/connect/confluence/connectUtils-1-4-8.js

@@ -789,10 +789,10 @@ AC.getPageAttachments = function(pageId, success, error)
 {
 {
 	var attachments = [];
 	var attachments = [];
 
 
-	function getAttsChunk(nextUrl)
+	function getAttsChunk(start)
 	{
 	{
 		AP.request({
 		AP.request({
-			url: nextUrl != null? nextUrl : '/rest/api/content/' + pageId + '/child/attachment?limit=100',
+			url: '/rest/api/content/' + pageId + '/child/attachment?limit=100&start=' + start,
 			type: 'GET',
 			type: 'GET',
 			contentType: 'application/json;charset=UTF-8',
 			contentType: 'application/json;charset=UTF-8',
 			success: function(resp) 
 			success: function(resp) 
@@ -803,7 +803,8 @@ AC.getPageAttachments = function(pageId, success, error)
 				//Support paging
 				//Support paging
 				if (resp._links && resp._links.next) 
 				if (resp._links && resp._links.next) 
 				{
 				{
-					getAttsChunk(resp._links.next);
+					start += resp.limit; //Sometimes the limit is changed by the server
+					getAttsChunk(start);
 				}
 				}
 				else
 				else
 				{
 				{
@@ -814,7 +815,7 @@ AC.getPageAttachments = function(pageId, success, error)
 		});
 		});
 	};
 	};
 	
 	
-	getAttsChunk();	
+	getAttsChunk(0);	
 };
 };
 
 
 AC.searchDiagrams = function(searchStr, success, error)
 AC.searchDiagrams = function(searchStr, success, error)

文件差异内容过多而无法显示
+ 946 - 939
src/main/webapp/js/app.min.js


+ 11 - 3
src/main/webapp/js/diagramly/App.js

@@ -1458,8 +1458,8 @@ App.prototype.init = function()
 		}
 		}
 		
 		
 		if (!mxClient.IS_CHROMEAPP && !EditorUi.isElectronApp && !this.isOffline() &&
 		if (!mxClient.IS_CHROMEAPP && !EditorUi.isElectronApp && !this.isOffline() &&
-			!mxClient.IS_ANDROID && !mxClient.IS_IOS &&
-			urlParams['open'] == null && (!this.editor.chromeless || this.editor.editable))
+			!mxClient.IS_ANDROID && !mxClient.IS_IOS && urlParams['open'] == null &&
+			(!this.editor.chromeless || this.editor.editable))
 		{
 		{
 			this.editor.addListener('fileLoaded', mxUtils.bind(this, function()
 			this.editor.addListener('fileLoaded', mxUtils.bind(this, function()
 			{
 			{
@@ -2727,7 +2727,7 @@ App.prototype.start = function()
 				}
 				}
 
 
 				if (!mxClient.IS_CHROMEAPP && !EditorUi.isElectronApp && !this.isOfflineApp() &&
 				if (!mxClient.IS_CHROMEAPP && !EditorUi.isElectronApp && !this.isOfflineApp() &&
-					/.*\.draw\.io$/.test(window.location.hostname) &&
+					urlParams['open'] == null && /.*\.draw\.io$/.test(window.location.hostname) &&
 					(!this.editor.chromeless || this.editor.editable))
 					(!this.editor.chromeless || this.editor.editable))
 				{
 				{
 					this.showNameChangeBanner();
 					this.showNameChangeBanner();
@@ -3036,6 +3036,14 @@ App.prototype.start = function()
 				}
 				}
 				else
 				else
 				{
 				{
+					// Removes open URL parameter. Hash is also updated in Init to load client.
+					if (urlParams['open'] != null && window.history && window.history.replaceState)
+					{
+						window.history.replaceState(null, null, window.location.pathname +
+							this.getSearch(['open']));
+						window.location.hash = urlParams['open'];
+					}
+					
 					done();
 					done();
 				}
 				}
 			}
 			}

+ 7 - 4
src/main/webapp/js/diagramly/Devel.js

@@ -11,19 +11,22 @@ if (!mxIsElectron)
 	(function()
 	(function()
 	{
 	{
 		var csp = 'default-src \'self\'; ' +
 		var csp = 'default-src \'self\'; ' +
+			// storage.googleapis.com is needed for workbox-service-worker
 			'script-src %dev-script-src% \'self\' https://storage.googleapis.com ' +
 			'script-src %dev-script-src% \'self\' https://storage.googleapis.com ' +
 				'https://apis.google.com https://*.pusher.com https://code.jquery.com '+
 				'https://apis.google.com https://*.pusher.com https://code.jquery.com '+
 				'https://www.dropbox.com https://api.trello.com ' +
 				'https://www.dropbox.com https://api.trello.com ' +
 				// Scripts in index.html (not checked here)
 				// Scripts in index.html (not checked here)
 				'\'sha256-JqdgAC+ydIDMtmQclZEqgbw94J4IeABIfXAxwEJGDJs=\' ' +
 				'\'sha256-JqdgAC+ydIDMtmQclZEqgbw94J4IeABIfXAxwEJGDJs=\' ' +
-				'\'sha256-4Dg3/NrB8tLC7TUSCbrtUDWD/J6bSLka01GHn+qtNZ0=\';' +
+				'\'sha256-4Dg3/NrB8tLC7TUSCbrtUDWD/J6bSLka01GHn+qtNZ0=\'; ' +
 			'connect-src \'self\' https://*.draw.io https://*.googleapis.com wss://*.pusher.com ' +
 			'connect-src \'self\' https://*.draw.io https://*.googleapis.com wss://*.pusher.com ' +
 				'https://api.github.com https://raw.githubusercontent.com https://gitlab.com ' +
 				'https://api.github.com https://raw.githubusercontent.com https://gitlab.com ' +
 				'https://graph.microsoft.com https://*.sharepoint.com  https://*.1drv.com ' +
 				'https://graph.microsoft.com https://*.sharepoint.com  https://*.1drv.com ' +
 				'https://*.dropboxapi.com https://api.trello.com https://*.google.com ' +
 				'https://*.dropboxapi.com https://api.trello.com https://*.google.com ' +
-				'https://fonts.gstatic.com https://fonts.googleapis.com;' +
-			'img-src * data:; media-src * data:; font-src *; ' +
-			'frame-src \'self\' https://*.google.com; ' +
+				'https://fonts.gstatic.com https://fonts.googleapis.com; ' +
+			// font-src about: is required for MathJax HTML-CSS output with STIX
+			'img-src * data:; media-src * data:; font-src * about:; ' +
+			// www.draw.io required for browser data migration to app.diagrams.net
+			'frame-src \'self\' https://www.draw.io https://*.google.com; ' +
 			'style-src %dev-style-src% \'self\' \'unsafe-inline\' https://fonts.googleapis.com;';
 			'style-src %dev-style-src% \'self\' \'unsafe-inline\' https://fonts.googleapis.com;';
 		var devCsp = csp.
 		var devCsp = csp.
 			// Loads common.css from mxgraph
 			// Loads common.css from mxgraph

+ 89 - 60
src/main/webapp/js/diagramly/Editor.js

@@ -4,6 +4,15 @@
  */
  */
 (function()
 (function()
 {
 {
+	/**
+	 * Enables paste from Lucidchart
+	 */
+	if (typeof html4 !== 'undefined')
+	{
+		html4.ATTRIBS["span::data-lucid-content"] = 0;
+		html4.ATTRIBS["span::data-lucid-type"] = 0;
+	}
+
 	/**
 	/**
 	 * Specifies the app name. Default is document.title.
 	 * Specifies the app name. Default is document.title.
 	 */
 	 */
@@ -207,6 +216,34 @@
 	 */
 	 */
 	Editor.commonVertexProperties = [
 	Editor.commonVertexProperties = [
         {type: 'separator'},
         {type: 'separator'},
+        {name: 'resizeLastRow', dispName: 'Resize Last Row', type: 'bool', getDefaultValue: function(state, format)
+        {
+        	var cell = (state.vertices.length == 1 && state.edges.length == 0) ? state.vertices[0] : null;
+        	var graph = format.editorUi.editor.graph;
+        	var style = graph.getCellStyle(cell);
+        	
+        	return mxUtils.getValue(style, 'resizeLastRow', '0') == '1';
+        }, isVisible: function(state, format)
+        {
+        	var graph = format.editorUi.editor.graph;
+        	
+    		return state.vertices.length == 1 && state.edges.length == 0 &&
+    			graph.isTable(state.vertices[0]);
+        }},
+        {name: 'resizeLast', dispName: 'Resize Last Column', type: 'bool', getDefaultValue: function(state, format)
+        {
+        	var cell = (state.vertices.length == 1 && state.edges.length == 0) ? state.vertices[0] : null;
+        	var graph = format.editorUi.editor.graph;
+        	var style = graph.getCellStyle(cell);
+        	
+        	return mxUtils.getValue(style, 'resizeLast', '0') == '1';
+        }, isVisible: function(state, format)
+        {
+        	var graph = format.editorUi.editor.graph;
+        	
+    		return state.vertices.length == 1 && state.edges.length == 0 &&
+    			graph.isTable(state.vertices[0]);
+        }},
         {name: 'fillOpacity', dispName: 'Fill Opacity', type: 'int', min: 0, max: 100, defVal: 100},
         {name: 'fillOpacity', dispName: 'Fill Opacity', type: 'int', min: 0, max: 100, defVal: 100},
         {name: 'strokeOpacity', dispName: 'Stroke Opacity', type: 'int', min: 0, max: 100, defVal: 100},
         {name: 'strokeOpacity', dispName: 'Stroke Opacity', type: 'int', min: 0, max: 100, defVal: 100},
         {name: 'overflow', dispName: 'Text Overflow', defVal: 'visible', type: 'enum',
         {name: 'overflow', dispName: 'Text Overflow', defVal: 'visible', type: 'enum',
@@ -221,7 +258,16 @@
         	enumList: [{val: 'none', dispName: 'None'}, {val: 'north', dispName: 'North'}, {val: 'east', dispName: 'East'}, {val: 'south', dispName: 'South'}, {val: 'west', dispName: 'West'}]
         	enumList: [{val: 'none', dispName: 'None'}, {val: 'north', dispName: 'North'}, {val: 'east', dispName: 'East'}, {val: 'south', dispName: 'South'}, {val: 'west', dispName: 'West'}]
         },
         },
         {name: 'portConstraintRotation', dispName: 'Rotate Constraint', type: 'bool', defVal: false},
         {name: 'portConstraintRotation', dispName: 'Rotate Constraint', type: 'bool', defVal: false},
-        {name: 'connectable', dispName: 'Connectable', type: 'bool', defVal: true},
+        {name: 'connectable', dispName: 'Connectable', type: 'bool', getDefaultValue: function(state, format)
+        {
+        	var cell = (state.vertices.length == 1 && state.edges.length == 0) ? state.vertices[0] : null;
+        	var graph = format.editorUi.editor.graph;
+        	
+        	return graph.isCellConnectable(cell);
+        }, isVisible: function(state, format)
+        {
+    		return state.vertices.length == 1 && state.edges.length == 0;
+        }},
         {name: 'allowArrows', dispName: 'Allow Arrows', type: 'bool', defVal: true},
         {name: 'allowArrows', dispName: 'Allow Arrows', type: 'bool', defVal: true},
         {name: 'snapToPoint', dispName: 'Snap to Point', type: 'bool', defVal: false},
         {name: 'snapToPoint', dispName: 'Snap to Point', type: 'bool', defVal: false},
         {name: 'perimeter', dispName: 'Perimeter', defVal: 'none', type: 'enum',
         {name: 'perimeter', dispName: 'Perimeter', defVal: 'none', type: 'enum',
@@ -1368,6 +1414,37 @@
 				}
 				}
 			}, 0);
 			}, 0);
 		};
 		};
+		
+		var font = (urlParams['math-font'] != null) ?
+			decodeURIComponent(urlParams['math-font']) :
+			'TeX';
+		
+		config = (config != null) ? config :
+		{
+			jax: ['input/TeX', 'input/MathML', 'input/AsciiMath'].concat(
+				[(urlParams['math-output'] == 'html') ?
+					'output/HTML-CSS' : 'output/SVG']),
+			extensions: ['tex2jax.js', 'mml2jax.js', 'asciimath2jax.js'],
+			TeX: {
+				extensions: ['AMSmath.js', 'AMSsymbols.js', 'noErrors.js', 'noUndefined.js']
+			},
+			'HTML-CSS': {
+				availableFonts: [font],
+				imageFont: null
+			},
+			SVG: {
+				font: font,
+				// Needed for client-side export to work
+				useFontCache: false
+			},
+			// Ignores math in in-place editor
+			tex2jax: {
+				ignoreClass: 'mxCellEditor'
+		  	},
+		  	asciimath2jax: {
+				ignoreClass: 'mxCellEditor'
+		  	}
+		};
 
 
 		// Disables global typesetting and messages on startup, adds queue for
 		// Disables global typesetting and messages on startup, adds queue for
 		// asynchronous rendering while MathJax is loading
 		// asynchronous rendering while MathJax is loading
@@ -1378,36 +1455,7 @@
 			messageStyle: 'none',
 			messageStyle: 'none',
 			AuthorInit: function ()
 			AuthorInit: function ()
 			{
 			{
-				// Specification recommends using SVG over HTML-CSS if browser is known
-				// Check if too inconsistent with image export and print output
-				MathJax.Hub.Config(config || {
-					jax: ['input/TeX', 'input/MathML', 'input/AsciiMath', 'output/SVG'],
-					extensions: ['tex2jax.js', 'mml2jax.js', 'asciimath2jax.js'],
-					TeX: {
-						extensions: ['AMSmath.js', 'AMSsymbols.js', 'noErrors.js', 'noUndefined.js']
-					},
-					// Needed for client-side export to work
-					SVG: {
-						font: (urlParams['math-font'] != null) ?
-							decodeURIComponent(urlParams['math-font']) :
-							'TeX',
-						useFontCache: false
-					},
-					// Ignores math in in-place editor
-					tex2jax: {
-						ignoreClass: 'mxCellEditor'
-				  	},
-				  	asciimath2jax: {
-						ignoreClass: 'mxCellEditor'
-				  	}
-				});
-				MathJax.Hub.Register.StartupHook('Begin', function()
-				{
-					for (var i = 0; i < Editor.mathJaxQueue.length; i++)
-					{
-						Editor.doMathJaxRender(Editor.mathJaxQueue[i]);
-					}
-				});
+				MathJax.Hub.Config(config);
 		    }
 		    }
 		};
 		};
 
 
@@ -6040,39 +6088,20 @@
 				if (pv.mathEnabled)
 				if (pv.mathEnabled)
 				{
 				{
 					var doc = pv.wnd.document;
 					var doc = pv.wnd.document;
-			
-					doc.writeln('<script type="text/x-mathjax-config">');
-					doc.writeln('MathJax.Hub.Config({');
-					doc.writeln('showMathMenu: false,');
-					doc.writeln('messageStyle: "none",');
-					doc.writeln('jax: ["input/TeX", "input/MathML", "input/AsciiMath", "output/SVG"],');
-					doc.writeln('extensions: ["tex2jax.js", "mml2jax.js", "asciimath2jax.js"],');
-					doc.writeln('TeX: {');
-					doc.writeln('extensions: ["AMSmath.js", "AMSsymbols.js", "noErrors.js", "noUndefined.js"]');
-					doc.writeln('},');
-					doc.writeln('SVG: {');
-					doc.writeln('font: "' + ((urlParams['math-font'] != null) ?
-						decodeURIComponent(urlParams['math-font']) :
-						'TeX') + '"');
-					doc.writeln('},');
-					doc.writeln('tex2jax: {');
-					doc.writeln('ignoreClass: "geDisableMathJax"');
-				  	doc.writeln('},');
-				  	doc.writeln('asciimath2jax: {');
-					doc.writeln('ignoreClass: "geDisableMathJax"');
-				  	doc.writeln('}');
-					doc.writeln('});');
 					
 					
-					// Adds asynchronous printing when MathJax finished rendering
+					// Adds asynchronous printing when MathJax finishes rendering
 					if (print)
 					if (print)
 					{
 					{
-						doc.writeln('MathJax.Hub.Queue(function () {');
-						doc.writeln('window.print();');
-						doc.writeln('});');
+						// Injects variable to execute print via Queue without unsafe-inline
+						pv.wnd.IMMEDIATE_PRINT = true;
+//						doc.writeln('<script type="text/x-mathjax-config">');
+//						doc.writeln('MathJax.Hub.Queue(function () {');
+//						doc.writeln('window.print();');
+//						doc.writeln('});');
+//						doc.writeln('</script>');
 					}
 					}
-					
-					doc.writeln('</script>');
-					doc.writeln('<script type="text/javascript" src="' + DRAW_MATH_URL + '/MathJax.js"></script>');
+
+					doc.writeln('<script type="text/javascript" src="' + DRAWIO_BASE_URL + '/js/math-print.js"></script>');
 				}
 				}
 				
 				
 				pv.closeDocument();
 				pv.closeDocument();

+ 12 - 5
src/main/webapp/js/diagramly/EditorUi.js

@@ -9092,7 +9092,7 @@
 					    {
 					    {
 				    		var html = evt.dataTransfer.getData('text/html');
 				    		var html = evt.dataTransfer.getData('text/html');
 				    		var div = document.createElement('div');
 				    		var div = document.createElement('div');
-				    		div.innerHTML = html;
+				    		div.innerHTML = graph.sanitizeHtml(html);
 				    		
 				    		
 				    		// The default is based on the extension
 				    		// The default is based on the extension
 				    		var asImage = null;
 				    		var asImage = null;
@@ -9104,6 +9104,11 @@
 				    		{
 				    		{
 				    			html = imgs[0].getAttribute('src');
 				    			html = imgs[0].getAttribute('src');
 				    			
 				    			
+				    			if (html == null)
+				    			{
+				    				html = imgs[0].getAttribute('srcset');
+				    			}
+				    			
 				    			// Handles special case where the src attribute has no valid extension
 				    			// Handles special case where the src attribute has no valid extension
 				    			// in which case the text would be inserted as text with a link
 				    			// in which case the text would be inserted as text with a link
 				    			if (!(/\.(gif|jpg|jpeg|tiff|png|svg)$/i).test(html))
 				    			if (!(/\.(gif|jpg|jpeg|tiff|png|svg)$/i).test(html))
@@ -9139,7 +9144,7 @@
 				    			graph.setSelectionCells(this.insertTextAt(html, x, y, true, asImage, null, resizeImages));
 				    			graph.setSelectionCells(this.insertTextAt(html, x, y, true, asImage, null, resizeImages));
 				    		});
 				    		});
 				    		
 				    		
-				    		if (asImage && html.length > this.resampleThreshold)
+				    		if (asImage && html != null && html.length > this.resampleThreshold)
 				    		{
 				    		{
 				    			this.confirmImageResize(function(doResize)
 				    			this.confirmImageResize(function(doResize)
 		    					{
 		    					{
@@ -9712,8 +9717,10 @@
 				
 				
 				if (data != null && data.length > 0)
 				if (data != null && data.length > 0)
 				{
 				{
+					var hasMeta = data.substring(0, 6) == '<meta ';
 					elt = document.createElement('div');
 					elt = document.createElement('div');
-					elt.innerHTML = data;
+					elt.innerHTML = ((hasMeta) ? '<meta charset="utf-8">' : '') +
+						this.editor.graph.sanitizeHtml(data);
 					asHtml = true;
 					asHtml = true;
 					
 					
 					// Workaround for innerText not ignoring style elements in Chrome
 					// Workaround for innerText not ignoring style elements in Chrome
@@ -10006,7 +10013,7 @@
 								    	
 								    	
 								    	if (mxUtils.indexOf(provider.types, 'text/uri-list') >= 0)
 								    	if (mxUtils.indexOf(provider.types, 'text/uri-list') >= 0)
 								    	{
 								    	{
-								    		var data = evt.dataTransfer.getData('text/uri-list');
+								    		data = evt.dataTransfer.getData('text/uri-list');
 								    	}
 								    	}
 								    	else
 								    	else
 								    	{
 								    	{
@@ -10016,7 +10023,7 @@
 										if (data != null && data.length > 0)
 										if (data != null && data.length > 0)
 										{
 										{
 											var div = document.createElement('div');
 											var div = document.createElement('div');
-								    		div.innerHTML = data;
+								    		div.innerHTML = this.editor.graph.sanitizeHtml(data);
 		
 		
 								    		// Extracts single image
 								    		// Extracts single image
 								    		var imgs = div.getElementsByTagName('img');
 								    		var imgs = div.getElementsByTagName('img');

+ 5 - 1
src/main/webapp/js/diagramly/Menus.js

@@ -1314,8 +1314,12 @@
 			{
 			{
 				if (!graph.isSelectionEmpty())
 				if (!graph.isSelectionEmpty())
 				{
 				{
+					var cells = graph.cloneCells(graph.getSelectionCells());
+					var bbox = graph.getBoundingBoxFromGeometry(cells);
+					cells = graph.moveCells(cells, -bbox.x, -bbox.y);
+					
 					editorUi.showTextDialog('Create Sidebar Entry', 'sb.createVertexTemplateFromData(\'' +
 					editorUi.showTextDialog('Create Sidebar Entry', 'sb.createVertexTemplateFromData(\'' +
-						Graph.compress(mxUtils.getXml(graph.encodeCells(graph.getSelectionCells()))) +
+						Graph.compress(mxUtils.getXml(graph.encodeCells(cells))) +
 						'\', width, height, \'Title\');');
 						'\', width, height, \'Title\');');
 				}
 				}
 			}));
 			}));

+ 63 - 0
src/main/webapp/js/math-print.js

@@ -0,0 +1,63 @@
+(function() {
+	
+	var font = (window.opener.urlParams['math-font'] != null) ?
+		decodeURIComponent(window.opener.urlParams['math-font']) : 'TeX';
+
+	window.MathJax = {
+		showMathMenu: false,
+		messageStyle: 'none',
+		AuthorInit: function () {
+			MathJax.Hub.Config({
+				jax: ['input/TeX', 'input/MathML', 'input/AsciiMath'].concat(
+				[(window.opener.urlParams['math-output'] == 'html') ?
+					'output/HTML-CSS' : 'output/SVG']),
+				extensions: ['tex2jax.js', 'mml2jax.js', 'asciimath2jax.js'],
+				TeX: {
+					extensions:['AMSmath.js', 'AMSsymbols.js', 'noErrors.js', 'noUndefined.js']
+				},
+				'HTML-CSS': {
+					availableFonts: [font],
+					imageFont: null
+				},
+				SVG: {
+					font: font,
+					useFontCache: false
+				},
+				tex2jax: {
+					ignoreClass: 'geDisableMathJax'
+				},
+				asciimath2jax: {
+					ignoreClass: 'geDisableMathJax'
+				}
+			});
+		}
+	};
+	
+	var s = document.createElement('script');
+	s.setAttribute('type', 'text/javascript');
+	s.setAttribute('src', window.opener.DRAW_MATH_URL + '/MathJax.js');
+	
+	// Waits for the Math to be rendered and then calls window.print
+	if (window.IMMEDIATE_PRINT)
+    {
+		var r = false;
+		
+		s.onload = s.onreadystatechange = function()
+		{
+			if (!r && (!this.readyState || this.readyState == 'complete'))
+			{
+	      		MathJax.Hub.Queue(function ()
+	      		{
+	      			window.print();
+	      		});
+			}
+		};
+    }
+	
+	var t = document.getElementsByTagName('script')[0];
+			  	
+  	if (t != null)
+  	{
+  		t.parentNode.insertBefore(s, t);
+  	}
+})();

+ 459 - 245
src/main/webapp/js/mxgraph/Graph.js

@@ -254,11 +254,13 @@ Graph = function(container, model, renderHint, stylesheet, themes, standalone)
 		    		{
 		    		{
 			   			var handler = this.selectionCellsHandler.getHandler(state.cell);
 			   			var handler = this.selectionCellsHandler.getHandler(state.cell);
 
 
-			   			// Cell custom handles have precedence over row and col resize
+			   			// Cell handles have precedence over row and col resize
 		    			if (handler == null || handler.getHandleForEvent(me) == null)
 		    			if (handler == null || handler.getHandleForEvent(me) == null)
 		    			{
 		    			{
 				    		var box = new mxRectangle(me.getGraphX(), me.getGraphY());
 				    		var box = new mxRectangle(me.getGraphX(), me.getGraphY());
-			    			box.grow(mxShape.prototype.svgStrokeTolerance - 1);
+			    			box.grow(mxEvent.isTouchEvent(me.getEvent()) ?
+			    				mxShape.prototype.svgStrokeTolerance - 1 :
+			    				mxShape.prototype.svgStrokeTolerance / 2);
 			    			
 			    			
 			    			if (this.isTableCell(state.cell))
 			    			if (this.isTableCell(state.cell))
 			    			{
 			    			{
@@ -519,7 +521,7 @@ Graph = function(container, model, renderHint, stylesheet, themes, standalone)
 				    		else
 				    		else
 				    		{
 				    		{
 				    			var box = new mxRectangle(me.getGraphX(), me.getGraphY());
 				    			var box = new mxRectangle(me.getGraphX(), me.getGraphY());
-			    				box.grow(mxShape.prototype.svgStrokeTolerance - 1);
+			    				box.grow(mxShape.prototype.svgStrokeTolerance / 2);
 	
 	
 					    		if (this.isTableCell(state.cell))
 					    		if (this.isTableCell(state.cell))
 					    		{
 					    		{
@@ -2055,7 +2057,12 @@ Graph.prototype.initLayoutManager = function()
 	
 	
 	this.layoutManager.getLayout = function(cell, eventName)
 	this.layoutManager.getLayout = function(cell, eventName)
 	{
 	{
-		if (eventName != mxEvent.BEGIN_UPDATE)
+		var parent = this.graph.model.getParent(cell);
+		
+		// Executes layouts from top to bottom except for nested layouts where
+		// child layouts are executed before and after the parent layout runs
+		// in case the layout changes the size of the child cell
+		if (eventName != mxEvent.BEGIN_UPDATE || this.hasLayout(parent, eventName))
 		{
 		{
 			var style = this.graph.getCellStyle(cell);
 			var style = this.graph.getCellStyle(cell);
 	
 	
@@ -3000,7 +3007,8 @@ Graph.prototype.getLinkForCell = function(cell)
 };
 };
 
 
 /**
 /**
- * Overrides label orientation for collapsed swimlanes inside stack.
+ * Overrides label orientation for collapsed swimlanes inside stack and
+ * for partial rectangles inside tables.
  */
  */
 Graph.prototype.getCellStyle = function(cell)
 Graph.prototype.getCellStyle = function(cell)
 {
 {
@@ -3222,26 +3230,6 @@ Graph.prototype.isContainer = function(cell)
 	}
 	}
 };
 };
 
 
-/**
- * Adds a expand style.
- */
-Graph.prototype.isExtendParent = function(cell)
-{
-	var parent = this.model.getParent(cell);
-	
-	if (parent != null)
-	{
-		var style = this.getCurrentCellStyle(parent);
-		
-		if (style['expand'] != null)
-		{
-			return style['expand'] != '0';
-		}
-	}
-	
-	return mxGraph.prototype.isExtendParent.apply(this, arguments);
-};
-
 /**
 /**
  * Adds a connectable style.
  * Adds a connectable style.
  */
  */
@@ -4471,13 +4459,44 @@ Graph.prototype.createTable = function(rowCount, colCount, w, h, title, startSiz
 		((title != null) ? 'swimlane;startSize=' + startSize + ';' : '') +
 		((title != null) ? 'swimlane;startSize=' + startSize + ';' : '') +
 		'html=1;whiteSpace=wrap;container=1;collapsible=0;childLayout=tableLayout;'),
 		'html=1;whiteSpace=wrap;container=1;collapsible=0;childLayout=tableLayout;'),
 		this.createParent(this.createVertex(null, null, '', 0, 0, colCount * w, h,
 		this.createParent(this.createVertex(null, null, '', 0, 0, colCount * w, h,
-    		'html=1;whiteSpace=wrap;collapsible=0;dropTarget=0;pointerEvents=0;fillColor=none;' +
-    		'strokeColor=none;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;'),
+    		'shape=partialRectangle;html=1;whiteSpace=wrap;collapsible=0;dropTarget=0;pointerEvents=0;' +
+    		'fillColor=none;strokeColor=none;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;'),
 			this.createVertex(null, null, '', 0, 0, w, h,
 			this.createVertex(null, null, '', 0, 0, w, h,
 				'shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;fillColor=none;'),
 				'shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;fillColor=none;'),
 				colCount, w, 0), rowCount, 0, h);
 				colCount, w, 0), rowCount, 0, h);
 };
 };
 
 
+/**
+ * Sets the values for the cells and rows in the given table and returns the table.
+ */
+Graph.prototype.setTableValues = function(table, values, rowValues)
+{
+	var rows = this.model.getChildCells(table, true);
+	
+	for (var i = 0; i < rows.length; i++)
+	{
+		if (rowValues != null)
+		{
+			rows[i].value = rowValues[i];
+		}	
+		
+		if (values != null)
+		{
+			var cells = this.model.getChildCells(rows[i], true);
+			
+			for (var j = 0; j < cells.length; j++)
+			{
+				if (values[i][j] != null)
+				{
+					cells[j].value = values[i][j];
+				}
+			}
+		}
+	}
+	
+	return table;
+};
+
 /**
 /**
  * 
  * 
  */
  */
@@ -4545,8 +4564,9 @@ Graph.prototype.isTable = function(cell)
 /**
 /**
  * Updates the row and table heights.
  * Updates the row and table heights.
  */
  */
-Graph.prototype.setTableRowHeight = function(row, dy)
+Graph.prototype.setTableRowHeight = function(row, dy, extend)
 {
 {
+	extend = (extend != null) ? extend : true;
 	var model = this.getModel();
 	var model = this.getModel();
 	
 	
 	model.beginUpdate();
 	model.beginUpdate();
@@ -4561,15 +4581,47 @@ Graph.prototype.setTableRowHeight = function(row, dy)
 			rgeo.height += dy;
 			rgeo.height += dy;
 			model.setGeometry(row, rgeo);
 			model.setGeometry(row, rgeo);
 			
 			
-			// Updates height of table
 			var table = model.getParent(row);
 			var table = model.getParent(row);
+			var rows = model.getChildCells(table, true);
+			
+			// Shifts and resizes neighbor row
+			if (!extend)
+			{
+				var index = mxUtils.indexOf(rows, row);
+	
+				if (index < rows.length - 1)
+				{
+					var nextRow = rows[index + 1];
+					var geo = this.getCellGeometry(nextRow);
+				
+					if (geo != null)
+					{
+						geo = geo.clone();
+						geo.y += dy;
+						geo.height -= dy;
+						
+						model.setGeometry(nextRow, geo);
+					}
+				}
+			}
+			
+			// Updates height of table
 			var tgeo = this.getCellGeometry(table);
 			var tgeo = this.getCellGeometry(table);
 			
 			
 			if (tgeo != null)
 			if (tgeo != null)
 			{
 			{
-				tgeo = tgeo.clone();
-				tgeo.height += dy;
-				model.setGeometry(table, tgeo);
+				// Always extends for last row
+				if (!extend)
+				{
+					extend = row == rows[rows.length - 1];
+				}
+				
+				if (extend)
+				{
+					tgeo = tgeo.clone();
+					tgeo.height += dy;
+					model.setGeometry(table, tgeo);
+				}
 			}
 			}
 		}
 		}
 	}
 	}
@@ -4582,8 +4634,10 @@ Graph.prototype.setTableRowHeight = function(row, dy)
 /**
 /**
  * Updates column width and row height.
  * Updates column width and row height.
  */
  */
-Graph.prototype.setTableColumnWidth = function(col, dx)
+Graph.prototype.setTableColumnWidth = function(col, dx, extend)
 {
 {
+	extend = (extend != null) ? extend : false;
+	
 	var model = this.getModel();
 	var model = this.getModel();
 	var row = model.getParent(col);
 	var row = model.getParent(col);
 	var table = model.getParent(row);
 	var table = model.getParent(row);
@@ -4621,13 +4675,18 @@ Graph.prototype.setTableColumnWidth = function(col, dx)
 				{
 				{
 					geo = geo.clone();
 					geo = geo.clone();
 					geo.x += dx;
 					geo.x += dx;
-					geo.width -= dx;
+					
+					if (!extend)
+					{
+						geo.width -= dx;
+					}
+					
 					model.setGeometry(cell, geo);
 					model.setGeometry(cell, geo);
 				}
 				}
 			}
 			}
 		}
 		}
 		
 		
-		if (lastColumn)
+		if (lastColumn || extend)
 		{
 		{
 			// Updates width of table
 			// Updates width of table
 			var tgeo = this.getCellGeometry(table);
 			var tgeo = this.getCellGeometry(table);
@@ -4749,8 +4808,8 @@ TableLayout.prototype.layoutRow = function(row, positions, height)
 		{
 		{
 			cell = cell.clone();
 			cell = cell.clone();
 			
 			
-			cell.y = 0;
-			cell.height = height;
+			cell.y = off.y;
+			cell.height = height - off.y - off.height;
 			
 			
 			if (positions != null)
 			if (positions != null)
 			{
 			{
@@ -6013,6 +6072,28 @@ if (typeof mxVertexHandler != 'undefined')
 			return codec.encode(model);
 			return codec.encode(model);
 		};
 		};
 		
 		
+		/**
+		 * Overridden to add expand style.
+		 */
+		var graphIsExtendParent = Graph.prototype.isExtendParent;
+		Graph.prototype.isExtendParent = function(cell)
+		{
+			var parent = this.model.getParent(cell);
+			
+			if (parent != null)
+			{
+				var style = this.getCurrentCellStyle(parent);
+				
+				if (style['expand'] != null)
+				{
+					return style['expand'] != '0';
+				}
+			}
+			
+			return graphIsExtendParent.apply(this, arguments) &&
+				(parent == null || !this.isTable(parent));
+		};
+		
 		/**
 		/**
 		 * Overridden to use table cell instead of table as parent.
 		 * Overridden to use table cell instead of table as parent.
 		 */
 		 */
@@ -6100,6 +6181,44 @@ if (typeof mxVertexHandler != 'undefined')
 		Graph.prototype.moveCells = function(cells, dx, dy, clone, target, evt, mapping)
 		Graph.prototype.moveCells = function(cells, dx, dy, clone, target, evt, mapping)
 		{
 		{
 			mapping = (mapping != null) ? mapping : new Object();
 			mapping = (mapping != null) ? mapping : new Object();
+			
+			// Updates source and target table heights for moving rows
+			for (var i = 0; i < cells.length; i++)
+			{
+				if (target != null && this.isTableRow(cells[i]))
+				{
+					var parent = this.model.getParent(cells[i]);
+					var row = this.getCellGeometry(cells[i]);
+					
+					if (parent != null && row != null &&
+						this.isTable(parent) &&
+						this.isTable(target) &&
+						(clone || parent != target))
+					{
+						if (!clone)
+						{
+							var table = this.getCellGeometry(parent);
+					
+							if (table != null)
+							{
+								table = table.clone();
+								table.height -= row.height;
+								this.model.setGeometry(parent, table);
+							}
+						}
+
+						var table = this.getCellGeometry(target);
+				
+						if (table != null)
+						{
+							table = table.clone();
+							table.height += row.height;
+							this.model.setGeometry(target, table);
+						}
+					}
+				}
+			}
+			
 			var result = graphMoveCells.apply(this, arguments);
 			var result = graphMoveCells.apply(this, arguments);
 			
 			
 			if (clone)
 			if (clone)
@@ -6127,10 +6246,52 @@ if (typeof mxVertexHandler != 'undefined')
 				{
 				{
 					if (this.isTableCell(cells[i]))
 					if (this.isTableCell(cells[i]))
 					{
 					{
-						this.labelChanged(cells[i], '');
+						var row = this.model.getParent(cells[i]);
+						var table = this.model.getParent(row);
+						
+						// Removes table if one cell in one row left
+						if (this.model.getChildCount(row) == 1 &&
+							this.model.getChildCount(table) == 1)
+						{
+							if (mxUtils.indexOf(cells, table) < 0 &&
+								mxUtils.indexOf(result, table) < 0)
+							{
+								result.push(table);
+							}
+						}
+						else
+						{
+							this.labelChanged(cells[i], '');
+						}
 					}
 					}
 					else
 					else
 					{
 					{
+						// Deletes table if all rows are removed
+						if (this.isTableRow(cells[i]))
+						{
+							var table = this.model.getParent(cells[i]);
+							
+							if (mxUtils.indexOf(cells, table) < 0 &&
+								mxUtils.indexOf(result, table) < 0)
+							{
+								var rows = this.model.getChildCells(table, true);
+								var deleteCount = 0;
+								
+								for (var j = 0; j < rows.length; j++)
+								{
+									if (mxUtils.indexOf(cells, rows[j]) >= 0)
+									{
+										deleteCount++;
+									}
+								}
+								
+								if (deleteCount == rows.length)
+								{
+									result.push(table);
+								}
+							}
+						}
+						
 						result.push(cells[i]);
 						result.push(cells[i]);
 					}
 					}
 				}
 				}
@@ -6422,13 +6583,17 @@ if (typeof mxVertexHandler != 'undefined')
 						if (geo != null)
 						if (geo != null)
 						{
 						{
 							// Rotates the size and position in the geometry
 							// Rotates the size and position in the geometry
-							geo = geo.clone();
-							geo.x += geo.width / 2 - geo.height / 2;
-							geo.y += geo.height / 2 - geo.width / 2;
-							var tmp = geo.width;
-							geo.width = geo.height;
-							geo.height = tmp;
-							model.setGeometry(cell, geo);
+							if (!this.isTable(cell) && !this.isTableRow(cell) &&
+								!this.isTableCell(cell))
+							{
+								geo = geo.clone();
+								geo.x += geo.width / 2 - geo.height / 2;
+								geo.y += geo.height / 2 - geo.width / 2;
+								var tmp = geo.width;
+								geo.width = geo.height;
+								geo.height = tmp;
+								model.setGeometry(cell, geo);
+							}
 							
 							
 							// Reads the current direction and advances by 90 degrees
 							// Reads the current direction and advances by 90 degrees
 							var state = this.view.getState(cell);
 							var state = this.view.getState(cell);
@@ -6775,16 +6940,6 @@ if (typeof mxVertexHandler != 'undefined')
 			this.model.setValue(cell, value);
 			this.model.setValue(cell, value);
 		};
 		};
 		
 		
-		
-		/**
-		 * Overridden to stop extending tables.
-		 */
-		var graphIsExtendParent = Graph.prototype.isExtendParent; 
-		Graph.prototype.isExtendParent = function(cell)
-		{
-			return graphIsExtendParent.apply(this, arguments) && !this.isTable(cell);
-		};
-		
 		/**
 		/**
 		 * Overridden to stop moving edge labels between cells.
 		 * Overridden to stop moving edge labels between cells.
 		 */
 		 */
@@ -6936,18 +7091,18 @@ if (typeof mxVertexHandler != 'undefined')
 			if (mxUtils.hasScrollbars(this.container))
 			if (mxUtils.hasScrollbars(this.container))
 			{
 			{
 				return new mxPoint(
 				return new mxPoint(
-					this.snap((this.container.scrollLeft + this.container.clientWidth / 2) / this.view.scale -
-						this.view.translate.x - bbox.width / 2),
-					this.snap((this.container.scrollTop + this.container.clientHeight / 2) / this.view.scale -
-						this.view.translate.y - bbox.height / 2));
+					this.snap(Math.round((this.container.scrollLeft + this.container.clientWidth / 2) /
+						this.view.scale - this.view.translate.x - bbox.width / 2)),
+					this.snap(Math.round((this.container.scrollTop + this.container.clientHeight / 2) /
+						this.view.scale - this.view.translate.y - bbox.height / 2)));
 			}
 			}
 			else
 			else
 			{
 			{
 				return new mxPoint(
 				return new mxPoint(
-					this.snap(this.container.clientWidth / 2 / this.view.scale -
-						this.view.translate.x - bbox.width / 2),
-					this.snap(this.container.clientHeight / 2 / this.view.scale -
-						this.view.translate.y - bbox.height / 2));
+					this.snap(Math.round(this.container.clientWidth / 2 / this.view.scale -
+						this.view.translate.x - bbox.width / 2)),
+					this.snap(Math.round(this.container.clientHeight / 2 / this.view.scale -
+						this.view.translate.y - bbox.height / 2)));
 			}
 			}
 		};
 		};
 		
 		
@@ -8188,50 +8343,71 @@ if (typeof mxVertexHandler != 'undefined')
 			try
 			try
 			{
 			{
 				var table = cell;
 				var table = cell;
-				var rows = null;
-				var index = 0;
+				var row = cell;
 				
 				
 				if (this.isTableCell(cell))
 				if (this.isTableCell(cell))
 				{
 				{
-					var row = model.getParent(cell);
-					table = model.getParent(row);
-					index = mxUtils.indexOf(model.getChildCells(row, true), cell);
-				}
-				else if (this.isTableRow(cell))
-				{
-					table = model.getParent(cell);
-					index = model.getChildCells(cell, true).length - 1;
+					row = model.getParent(cell);
 				}
 				}
-				else if (this.isTable(cell))
+				
+				if (this.isTableRow(row))
 				{
 				{
-					rows = model.getChildCells(cell, true);
-					index = model.getChildCells(rows[0], true).length - 1;
+					table = model.getParent(row);
 				}
 				}
 				
 				
-				var width = 0;
-				rows = (rows != null) ? rows : model.getChildCells(table, true);
+				var rows = model.getChildCells(table, true);
 				
 				
-				for (var i = 0; i < rows.length; i++)
+				if (rows.length == 0)
 				{
 				{
-					var child = model.getChildCells(rows[i], true)[index];
-					model.remove(child);
+					model.remove(table);
+				}
+				else
+				{
+					if (!this.isTableRow(row))
+					{
+						row = rows[0];
+					}
 					
 					
-					var geo = this.getCellGeometry(child);
+					var cells = model.getChildCells(row, true);
 					
 					
-					if (geo != null)
+					if (cells.length <= 1)
 					{
 					{
-						width = Math.max(width, geo.width);
+						model.remove(table);
+					}
+					else
+					{
+						var index = cells.length - 1;
+						
+						if (this.isTableCell(cell))
+						{
+							index = mxUtils.indexOf(cells, cell);
+						}
+
+						var width = 0;
+		
+						for (var i = 0; i < rows.length; i++)
+						{
+							var child = model.getChildCells(rows[i], true)[index];
+							model.remove(child);
+							
+							var geo = this.getCellGeometry(child);
+							
+							if (geo != null)
+							{
+								width = Math.max(width, geo.width);
+							}
+						}
+						
+						var tableGeo = this.getCellGeometry(table);
+						
+						if (tableGeo != null)
+						{
+							tableGeo = tableGeo.clone();
+							tableGeo.width -= width;
+							
+							model.setGeometry(table, tableGeo);
+						}
 					}
 					}
-				}
-				
-				var tableGeo = this.getCellGeometry(table);
-				
-				if (tableGeo != null)
-				{
-					tableGeo = tableGeo.clone();
-					tableGeo.width -= width;
-					
-					model.setGeometry(table, tableGeo);
 				}
 				}
 			}
 			}
 			finally
 			finally
@@ -8250,37 +8426,52 @@ if (typeof mxVertexHandler != 'undefined')
 			
 			
 			try
 			try
 			{
 			{
+				var table = cell;
 				var row = cell;
 				var row = cell;
 				
 				
 				if (this.isTableCell(cell))
 				if (this.isTableCell(cell))
 				{
 				{
 					row = model.getParent(cell);
 					row = model.getParent(cell);
+					cell = row;
 				}
 				}
-				else if (this.isTable(cell))
+				
+				if (this.isTableRow(cell))
 				{
 				{
-					var rows = model.getChildCells(cell, true);
-					row = rows[rows.length - 1];
+					table = model.getParent(row);
 				}
 				}
 				
 				
-				var table = model.getParent(row);
-				model.remove(row);
-				var height = 0;
-				
-				var geo = this.getCellGeometry(row);
+				var rows = model.getChildCells(table, true);
 				
 				
-				if (geo != null)
+				if (rows.length <= 1)
 				{
 				{
-					height = geo.height;
+					model.remove(table);
 				}
 				}
-				
-				var tableGeo = this.getCellGeometry(table);
-				
-				if (tableGeo != null)
+				else
 				{
 				{
-					tableGeo = tableGeo.clone();
-					tableGeo.height -= height;
+					if (!this.isTableRow(row))
+					{
+						row = rows[rows.length - 1];
+					}	
 					
 					
-					model.setGeometry(table, tableGeo);
+					model.remove(row);
+					var height = 0;
+					
+					var geo = this.getCellGeometry(row);
+					
+					if (geo != null)
+					{
+						height = geo.height;
+					}
+					
+					var tableGeo = this.getCellGeometry(table);
+					
+					if (tableGeo != null)
+					{
+						tableGeo = tableGeo.clone();
+						tableGeo.height -= height;
+						
+						model.setGeometry(table, tableGeo);
+					}
 				}
 				}
 			}
 			}
 			finally
 			finally
@@ -9316,7 +9507,46 @@ if (typeof mxVertexHandler != 'undefined')
 				this.hint = null;
 				this.hint = null;
 			}
 			}
 		};
 		};
-		
+								
+		/**
+		 * Overridden to allow for shrinking pools when lanes are resized.
+		 */
+		var stackLayoutResizeCell = mxStackLayout.prototype.resizeCell;
+		mxStackLayout.prototype.resizeCell = function(cell, bounds)
+		{
+			stackLayoutResizeCell.apply(this, arguments);
+			var style = this.graph.getCellStyle(cell);
+				
+			if (style['childLayout'] == null)
+			{
+				var parent = this.graph.model.getParent(cell);
+				var geo = (parent != null) ? this.graph.getCellGeometry(parent) : null;
+			
+				if (geo != null)
+				{
+					style = this.graph.getCellStyle(parent);
+					
+					if (style['childLayout'] == 'stackLayout')
+					{
+						var horizontal = mxUtils.getValue(style, 'horizontalStack', '1') == '1';
+						geo = geo.clone();
+						
+						if (horizontal)
+						{
+							geo.height = bounds.height;
+						}
+						else
+						{
+							geo.width = bounds.width;
+							
+						}
+			
+						this.graph.model.setGeometry(parent, geo);			
+					}
+				}
+			}
+		};
+
 		/**
 		/**
 		 * Shows handle for table instead of rows and cells.
 		 * Shows handle for table instead of rows and cells.
 		 */
 		 */
@@ -9530,153 +9760,137 @@ if (typeof mxVertexHandler != 'undefined')
 					handles = [];
 					handles = [];
 				}
 				}
 				
 				
-				// Finds rows
-				var rows = [];
+				// Adds handles for rows and columns
+				var rows = graph.view.getCellStates(model.getChildCells(this.state.cell, true));
 				
 				
-				for (var i = 0; i < model.getChildCount(this.state.cell); i++)
+				if (rows.length > 0)
 				{
 				{
-					var row = this.graph.view.getState(model.getChildAt(this.state.cell, i));
-					
-					if (row != null && model.isVertex(row.cell))
-					{
-						rows.push(row); 
-					}
-				}
-				
-				// Finds columns in row
-				var cols = [];
-				
-				for (var i = 0; i < model.getChildCount(rows[0].cell); i++)
-				{
-					var col = this.graph.view.getState(model.getChildAt(rows[0].cell, i));
-					
-					if (col != null && model.isVertex(col.cell))
-					{
-						cols.push(col); 
-					}
-				}
-				
-				// Adds col width handles
-				for (var i = 0; i < cols.length; i++)
-				{
-					(mxUtils.bind(this, function(index)
+					var cols = graph.view.getCellStates(model.getChildCells(rows[0].cell, true));
+	
+					// Adds column width handles
+					for (var i = 0; i < cols.length; i++)
 					{
 					{
-						var colState = cols[index];
-						var nextCol = (index < cols.length - 1) ? cols[index + 1] : null;
-						
-						// Adds handle to change column width
-						var shape = new mxLine(new mxRectangle(), mxConstants.NONE, 1, true);
-						shape.isDashed = sel.isDashed;
-						var handle = new mxHandle(colState, 'col-resize', null, shape);
-						handle.tableHandle = true;
-						var dx = 0;
-						
-						handle.shape.node.parentNode.insertBefore(handle.shape.node,
-							handle.shape.node.parentNode.firstChild);
-
-						handle.redraw = function()
+						(mxUtils.bind(this, function(index)
 						{
 						{
-							if (this.shape != null && this.state.shape != null)
+							var colState = cols[index];
+							var nextCol = (index < cols.length - 1) ? cols[index + 1] : null;
+							
+							var shape = new mxLine(new mxRectangle(), mxConstants.NONE, 1, true);
+							shape.isDashed = sel.isDashed;
+							
+							var handle = new mxHandle(colState, 'col-resize', null, shape);
+							handle.tableHandle = true;
+							var dx = 0;
+							
+							handle.shape.node.parentNode.insertBefore(handle.shape.node,
+								handle.shape.node.parentNode.firstChild);
+	
+							handle.redraw = function()
 							{
 							{
-								var start = graph.getActualStartSize(tableState.cell);
-								this.shape.stroke = (dx == 0) ? mxConstants.NONE : sel.stroke;
-								this.shape.bounds.x = this.state.x + this.state.width +
-									dx * this.graph.view.scale;
-								this.shape.bounds.width = 1;
-								this.shape.bounds.y = tableState.y + ((index == cols.length - 1) ?
-									0 : start.y * this.graph.view.scale);
-								this.shape.bounds.height = tableState.height -
-									((index == cols.length - 1) ? 0 :
-									(start.height + start.y) * this.graph.view.scale);
-								this.shape.redraw();
-							}
-						};
-						
-						handle.setPosition = function(bounds, pt, me)
-						{
-							dx = Math.max(Graph.minTableColumnWidth - bounds.width,
-								pt.x - bounds.x - bounds.width);
+								if (this.shape != null && this.state.shape != null)
+								{
+									var start = graph.getActualStartSize(tableState.cell);
+									this.shape.stroke = (dx == 0) ? mxConstants.NONE : sel.stroke;
+									this.shape.bounds.x = this.state.x + this.state.width +
+										dx * this.graph.view.scale;
+									this.shape.bounds.width = 1;
+									this.shape.bounds.y = tableState.y + ((index == cols.length - 1) ?
+										0 : start.y * this.graph.view.scale);
+									this.shape.bounds.height = tableState.height -
+										((index == cols.length - 1) ? 0 :
+										(start.height + start.y) * this.graph.view.scale);
+									this.shape.redraw();
+								}
+							};
 							
 							
-							if (nextCol != null)
+							handle.setPosition = function(bounds, pt, me)
 							{
 							{
-								dx = Math.min(nextCol.x + nextCol.width - colState.x -
-									colState.width - Graph.minTableColumnWidth, dx);
-							}
-						};
-						
-						handle.execute = function()
-						{
-							if (dx != 0)
+								dx = Math.max(Graph.minTableColumnWidth - bounds.width,
+									pt.x - bounds.x - bounds.width);
+								
+								if (nextCol != null)
+								{
+									dx = Math.min(nextCol.x + nextCol.width - colState.x -
+										colState.width - Graph.minTableColumnWidth, dx);
+								}
+							};
+							
+							handle.execute = function(me)
 							{
 							{
-								graph.setTableColumnWidth(this.state.cell, dx);
-							}
+								if (dx != 0)
+								{
+									graph.setTableColumnWidth(this.state.cell, dx,
+										mxEvent.isShiftDown(me.getEvent()));
+								}
+								
+								dx = 0;
+							};
 							
 							
-							dx = 0;
-						};
-						
-						handle.reset = function()
-						{
-							dx = 0;
-						};
-						
-						handles.push(handle);
-					}))(i);
-				}
-				
-				// Adds row height handles
-				for (var i = 0; i < rows.length; i++)
-				{
-					(mxUtils.bind(this, function(index)
+							handle.reset = function()
+							{
+								dx = 0;
+							};
+							
+							handles.push(handle);
+						}))(i);
+					}
+					
+					// Adds row height handles
+					for (var i = 0; i < rows.length; i++)
 					{
 					{
-						var rowState = rows[index];
-
-						// Adds handle to change row height
-						var shape = new mxLine(new mxRectangle(), mxConstants.NONE, 1);
-						shape.isDashed = sel.isDashed;
-						var handle = new mxHandle(rowState, 'row-resize', null, shape);
-						handle.tableHandle = true;
-						var dy = 0;
-
-						handle.shape.node.parentNode.insertBefore(handle.shape.node,
-							handle.shape.node.parentNode.firstChild);
-						
-						handle.redraw = function()
+						(mxUtils.bind(this, function(index)
 						{
 						{
-							if (this.shape != null && this.state.shape != null)
+							var rowState = rows[index];
+	
+							var shape = new mxLine(new mxRectangle(), mxConstants.NONE, 1);
+							shape.isDashed = sel.isDashed;
+							
+							var handle = new mxHandle(rowState, 'row-resize', null, shape);
+							handle.tableHandle = true;
+							var dy = 0;
+	
+							handle.shape.node.parentNode.insertBefore(handle.shape.node,
+								handle.shape.node.parentNode.firstChild);
+							
+							handle.redraw = function()
 							{
 							{
-								this.shape.stroke = (dy == 0) ? mxConstants.NONE : sel.stroke;;
-								this.shape.bounds.x = this.state.x;
-								this.shape.bounds.width = this.state.width;
-								this.shape.bounds.y = this.state.y + this.state.height +
-									dy * this.graph.view.scale;
-								this.shape.bounds.height = 1;
-								this.shape.redraw();
-							}
-						};
-						
-						handle.setPosition = function(bounds, pt, me)
-						{
-							dy = Math.max(Graph.minTableRowHeight - bounds.height,
-								pt.y - bounds.y - bounds.height);
-						};
-						
-						handle.execute = function()
-						{
-							if (dy != 0)
+								if (this.shape != null && this.state.shape != null)
+								{
+									this.shape.stroke = (dy == 0) ? mxConstants.NONE : sel.stroke;;
+									this.shape.bounds.x = this.state.x;
+									this.shape.bounds.width = this.state.width;
+									this.shape.bounds.y = this.state.y + this.state.height +
+										dy * this.graph.view.scale;
+									this.shape.bounds.height = 1;
+									this.shape.redraw();
+								}
+							};
+							
+							handle.setPosition = function(bounds, pt, me)
 							{
 							{
-								graph.setTableRowHeight(this.state.cell, dy);
-							}
+								dy = Math.max(Graph.minTableRowHeight - bounds.height,
+									pt.y - bounds.y - bounds.height);
+							};
 							
 							
-							dy = 0;
-						};
-						
-						handle.reset = function()
-						{
-							dy = 0;
-						};
-						
-						handles.push(handle);
-					}))(i);
+							handle.execute = function(me)
+							{
+								if (dy != 0)
+								{
+									graph.setTableRowHeight(this.state.cell, dy,
+										!mxEvent.isShiftDown(me.getEvent()));
+								}
+								
+								dy = 0;
+							};
+							
+							handle.reset = function()
+							{
+								dy = 0;
+							};
+							
+							handles.push(handle);
+						}))(i);
+					}
 				}
 				}
 			}
 			}
 			
 			

文件差异内容过多而无法显示
+ 25 - 5
src/main/webapp/js/mxgraph/Sidebar.js


文件差异内容过多而无法显示
+ 548 - 545
src/main/webapp/js/viewer.min.js


+ 1 - 466
src/main/webapp/service-worker.js

@@ -3,472 +3,7 @@ importScripts('https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox
 if (workbox)
 if (workbox)
 {
 {
 	// This is replaced in build process
 	// This is replaced in build process
-	workbox.precaching.precacheAndRoute([
-  {
-    "url": "js/app.min.js",
-    "revision": "cff100af7cbd76d60113c1c8269bcda7"
-  },
-  {
-    "url": "js/extensions.min.js",
-    "revision": "ecd53ce33feef17a0ac6be648bfd056f"
-  },
-  {
-    "url": "js/stencils.min.js",
-    "revision": "be66334d69e1fc4bead3535f87dfa611"
-  },
-  {
-    "url": "js/shapes.min.js",
-    "revision": "90d8f1edfecfe931438f4cd995bed5b0"
-  },
-  {
-    "url": "index.html",
-    "revision": "c356c0a671c0e709c0b49d17dbf99cf9"
-  },
-  {
-    "url": "open.html",
-    "revision": "f7de43d2d8d04cdc6f0d923942fad4d4"
-  },
-  {
-    "url": "app.html",
-    "revision": "e28e1516e715420680ab6f3bc3fe2288"
-  },
-  {
-    "url": "styles/grapheditor.css",
-    "revision": "4b2c4b76bae7c1a7c7df74fff3cb75af"
-  },
-  {
-    "url": "styles/atlas.css",
-    "revision": "40f54334c7a62821dbf1f7c7d8ad62cc"
-  },
-  {
-    "url": "styles/dark.css",
-    "revision": "bd0e851bff9047866cfa0683fe89ac38"
-  },
-  {
-    "url": "js/croppie/croppie.min.css",
-    "revision": "fc297c9002c79c15a132f13ee3ec427e"
-  },
-  {
-    "url": "js/dropbox/Dropbox-sdk.min.js",
-    "revision": "4b9842892aa37b156db0a8364b7a83b0"
-  },
-  {
-    "url": "js/onedrive/OneDrive.js",
-    "revision": "d82b9c14d7a069efabef719a8a5f3975"
-  },
-  {
-    "url": "math/MathJax.js",
-    "revision": "c32a502b8b4a6bd6bad8ddb1b59e9e72"
-  },
-  {
-    "url": "resources/dia.txt",
-    "revision": "566c40fd69752783a093b32b28eb98cf"
-  },
-  {
-    "url": "resources/dia_cs.txt",
-    "revision": "2a181c4fd68559bab01d3db5011ed050"
-  },
-  {
-    "url": "resources/dia_my.txt",
-    "revision": "566c40fd69752783a093b32b28eb98cf"
-  },
-  {
-    "url": "resources/dia_am.txt",
-    "revision": "138b6875d12860b14ff1aaaa6bb80da8"
-  },
-  {
-    "url": "resources/dia_ml.txt",
-    "revision": "52a9e39c1e6bb367d3b764e15903c2b6"
-  },
-  {
-    "url": "resources/dia_uk.txt",
-    "revision": "52c9ee7f8fe5ded1b412454ca9b76dd5"
-  },
-  {
-    "url": "resources/dia_bg.txt",
-    "revision": "519967d576645a4e77385e5c874d7a73"
-  },
-  {
-    "url": "resources/dia_ca.txt",
-    "revision": "17025b72fd74c4c894a7909970a069e8"
-  },
-  {
-    "url": "resources/dia_th.txt",
-    "revision": "f95aab8985ae3612fc9d64e62f035098"
-  },
-  {
-    "url": "resources/dia_bs.txt",
-    "revision": "8c5cc8ed52dd99d4c4d8a2c6deedc430"
-  },
-  {
-    "url": "resources/dia_id.txt",
-    "revision": "09b0be439257a5e648c9f4b3d1ee2706"
-  },
-  {
-    "url": "resources/dia_sk.txt",
-    "revision": "e34a5eb4cab6000eccd82b7b92a2d793"
-  },
-  {
-    "url": "resources/dia_ro.txt",
-    "revision": "978ebac9f57d036b5e4d20a505f209fd"
-  },
-  {
-    "url": "resources/dia_gl.txt",
-    "revision": "097d79496f590ef346093fe117da183d"
-  },
-  {
-    "url": "resources/dia_es.txt",
-    "revision": "94be775c840c8f010f21727e37a91c3e"
-  },
-  {
-    "url": "resources/dia_eu.txt",
-    "revision": "467b2121c8e2313421f81335d8bcd646"
-  },
-  {
-    "url": "resources/dia_ko.txt",
-    "revision": "588d651ae4680fd01613b0dc4b7a009f"
-  },
-  {
-    "url": "resources/dia_si.txt",
-    "revision": "566c40fd69752783a093b32b28eb98cf"
-  },
-  {
-    "url": "resources/dia_kn.txt",
-    "revision": "51a5e373e5b17bef75c29ae2d133f2ef"
-  },
-  {
-    "url": "resources/dia_hu.txt",
-    "revision": "b4b5fc9d2202888b3b241540921180e9"
-  },
-  {
-    "url": "resources/dia_fi.txt",
-    "revision": "ff7ad12f24bcbf83feabd80d31481aa5"
-  },
-  {
-    "url": "resources/dia_da.txt",
-    "revision": "64b4d600c995997e4a6b865bf2f9297c"
-  },
-  {
-    "url": "resources/dia_de.txt",
-    "revision": "9999834cf05ea01cfa66ceb9c3132015"
-  },
-  {
-    "url": "resources/dia_sl.txt",
-    "revision": "bdedc03d6a161f0785cd3c86de9812c9"
-  },
-  {
-    "url": "resources/dia_it.txt",
-    "revision": "63a4b879a08a807d2b1f97fc7e9081e5"
-  },
-  {
-    "url": "resources/dia_hr.txt",
-    "revision": "39f9fd7bc75d0dfe797de20cdafa321a"
-  },
-  {
-    "url": "resources/dia_he.txt",
-    "revision": "a8211c153af5628ad8abb2b3c891e806"
-  },
-  {
-    "url": "resources/dia_pt.txt",
-    "revision": "e7a16acf016d9c44df55b046f646e592"
-  },
-  {
-    "url": "resources/dia_zh-tw.txt",
-    "revision": "9dcb5f9edd1ef7f3fe114d146d15e4de"
-  },
-  {
-    "url": "resources/dia_et.txt",
-    "revision": "c983ffd38089ef14f0fb4b79fba3a270"
-  },
-  {
-    "url": "resources/dia_ja.txt",
-    "revision": "deeeb9ee6b528f5c97623daf1c41abde"
-  },
-  {
-    "url": "resources/dia_hi.txt",
-    "revision": "9a0eea69b84323f98950b5d290c8649a"
-  },
-  {
-    "url": "resources/dia_eo.txt",
-    "revision": "590289f937014f78a3c119ece175d41b"
-  },
-  {
-    "url": "resources/dia_fa.txt",
-    "revision": "462f15e995c8b8f8039fbfdc68c4e7a3"
-  },
-  {
-    "url": "resources/dia_sw.txt",
-    "revision": "c2473d4373a1706a3adbd24974435ced"
-  },
-  {
-    "url": "resources/dia_pl.txt",
-    "revision": "55c8ad18b1b85174968131e6722c53d4"
-  },
-  {
-    "url": "resources/dia_pt-br.txt",
-    "revision": "a961725bf74365dbf085deaa52101cfc"
-  },
-  {
-    "url": "resources/dia_sv.txt",
-    "revision": "192d03ba5387a08af56596c8d9dcf2b1"
-  },
-  {
-    "url": "resources/dia_el.txt",
-    "revision": "a0a748122aba16a095daff7607fdd4a1"
-  },
-  {
-    "url": "resources/dia_sr.txt",
-    "revision": "1de85bc0c9a08d0c72e875db8830a85a"
-  },
-  {
-    "url": "resources/dia_fr.txt",
-    "revision": "43084b576112d2f748215c870b341c48"
-  },
-  {
-    "url": "resources/dia_ru.txt",
-    "revision": "a2118b3326b618ce00faf147d82e8ba4"
-  },
-  {
-    "url": "resources/dia_gu.txt",
-    "revision": "fba35ba1d8f467a293d77f75a816e12f"
-  },
-  {
-    "url": "resources/dia_ar.txt",
-    "revision": "2ebfed9bd7456fb9190e545c2911708f"
-  },
-  {
-    "url": "resources/dia_tr.txt",
-    "revision": "5d4bfd15d0034d048d216cbad237c7ad"
-  },
-  {
-    "url": "resources/dia_te.txt",
-    "revision": "ef46829fd7b623514cd282c1c8e7c771"
-  },
-  {
-    "url": "resources/dia_lt.txt",
-    "revision": "25ec3135ad03308bf4bdfa501756ed28"
-  },
-  {
-    "url": "resources/dia_lv.txt",
-    "revision": "3124c0bd6ccabc9abcf793275df7efeb"
-  },
-  {
-    "url": "resources/dia_mr.txt",
-    "revision": "b366684f0d5931339ee0223d0ed4a2aa"
-  },
-  {
-    "url": "resources/dia_ms.txt",
-    "revision": "857a51c45995cd311dbd706b63cb7b6b"
-  },
-  {
-    "url": "resources/dia_nl.txt",
-    "revision": "c64e6e231ae284cc926e518c880531a4"
-  },
-  {
-    "url": "resources/dia_fil.txt",
-    "revision": "98048d4062bfaea79f891b60f83629a4"
-  },
-  {
-    "url": "resources/dia_zh.txt",
-    "revision": "d868e4fd1f5acc16ad6d67d26d360653"
-  },
-  {
-    "url": "resources/dia_bn.txt",
-    "revision": "6c9a6a3e9f9ce56be1702d7423abf275"
-  },
-  {
-    "url": "resources/dia_no.txt",
-    "revision": "f7e447e358e0b19af1434d7e08accd89"
-  },
-  {
-    "url": "resources/dia_vi.txt",
-    "revision": "2341b6b28875fb87af91a8f180dbff25"
-  },
-  {
-    "url": "resources/dia_ta.txt",
-    "revision": "f3e42d2330c443134a0ae22feee81d53"
-  },
-  {
-    "url": "favicon.ico",
-    "revision": "fab2d88b37c72d83607527573de45281"
-  },
-  {
-    "url": "images/manifest.json",
-    "revision": "078a1ee03d5b64100c5b760d8e73dc88"
-  },
-  {
-    "url": "images/logo.png",
-    "revision": "89630b64b911ebe0daa3dfe442087cfa"
-  },
-  {
-    "url": "images/drawlogo.svg",
-    "revision": "4bf4d14ebcf072d8bd4c5a1c89e88fc6"
-  },
-  {
-    "url": "images/drawlogo-gray.svg",
-    "revision": "0aabacbc0873816e1e09e4736ae44c7d"
-  },
-  {
-    "url": "images/apple-touch-icon.png",
-    "revision": "73da7989a23ce9a4be565ec65658a239"
-  },
-  {
-    "url": "images/favicon-16x16.png",
-    "revision": "1a79d5461a5d2bf21f6652e0ac20d6e5"
-  },
-  {
-    "url": "images/favicon-32x32.png",
-    "revision": "e3b92da2febe70bad5372f6f3474b034"
-  },
-  {
-    "url": "images/android-chrome-192x192.png",
-    "revision": "a886234330ea25d4ba1c47f678b00ef2"
-  },
-  {
-    "url": "images/android-chrome-512x512.png",
-    "revision": "959b5fac2453963ff6d60fb85e4b73fd"
-  },
-  {
-    "url": "images/delete.png",
-    "revision": "5f2350f2fd20f1a229637aed32ed8f29"
-  },
-  {
-    "url": "images/droptarget.png",
-    "revision": "bbf7f563fb6784de1ce96f329519b043"
-  },
-  {
-    "url": "images/help.png",
-    "revision": "9266c6c3915bd33c243d80037d37bf61"
-  },
-  {
-    "url": "images/download.png",
-    "revision": "35418dd7bd48d87502c71b578cc6c37f"
-  },
-  {
-    "url": "images/logo-flat.png",
-    "revision": "038070ab43aee6e54a791211859fc67b"
-  },
-  {
-    "url": "images/google-drive-logo.svg",
-    "revision": "5d9f2f5bbc7dcc252730a0072bb23059"
-  },
-  {
-    "url": "images/onedrive-logo.svg",
-    "revision": "3645b344ec0634c1290dd58d7dc87b97"
-  },
-  {
-    "url": "images/dropbox-logo.svg",
-    "revision": "e6be408c77cf9c82d41ac64fa854280a"
-  },
-  {
-    "url": "images/github-logo.svg",
-    "revision": "a1a999b69a275eac0cb918360ac05ae1"
-  },
-  {
-    "url": "images/gitlab-logo.svg",
-    "revision": "0faea8c818899e58533e153c44b10517"
-  },
-  {
-    "url": "images/trello-logo.svg",
-    "revision": "006fd0d7d70d7e95dc691674cb12e044"
-  },
-  {
-    "url": "images/osa_drive-harddisk.png",
-    "revision": "b954e1ae772087c5b4c6ae797e1f9649"
-  },
-  {
-    "url": "images/osa_database.png",
-    "revision": "c350d9d9b95f37b6cfe798b40ede5fb0"
-  },
-  {
-    "url": "images/google-drive-logo-white.svg",
-    "revision": "f329d8b1be7778515a85b93fc35d9f26"
-  },
-  {
-    "url": "images/dropbox-logo-white.svg",
-    "revision": "4ea8299ac3bc31a16f199ee3aec223bf"
-  },
-  {
-    "url": "images/onedrive-logo-white.svg",
-    "revision": "b3602fa0fc947009cff3f33a581cff4d"
-  },
-  {
-    "url": "images/github-logo-white.svg",
-    "revision": "537b1127b3ca0f95b45782d1304fb77a"
-  },
-  {
-    "url": "images/gitlab-logo-white.svg",
-    "revision": "5fede9ac2f394c716b8c23e3fddc3910"
-  },
-  {
-    "url": "images/trello-logo-white-orange.svg",
-    "revision": "e2a0a52ba3766682f138138d10a75eb5"
-  },
-  {
-    "url": "images/logo-confluence.png",
-    "revision": "ed1e55d44ae5eba8f999aba2c93e8331"
-  },
-  {
-    "url": "images/logo-jira.png",
-    "revision": "f8d460555a0d1f87cfd901e940666629"
-  },
-  {
-    "url": "images/clear.gif",
-    "revision": "db13c778e4382e0b55258d0f811d5d70"
-  },
-  {
-    "url": "images/spin.gif",
-    "revision": "487cbb40b9ced439aa1ad914e816d773"
-  },
-  {
-    "url": "images/checkmark.gif",
-    "revision": "ba764ce62f2bf952df5bbc2bb4d381c5"
-  },
-  {
-    "url": "images/hs.png",
-    "revision": "fefa1a03d92ebad25c88dca94a0b63db"
-  },
-  {
-    "url": "mxgraph/css/common.css",
-    "revision": "b5b7280ec98671bb6c3847a36bc7ea12"
-  },
-  {
-    "url": "mxgraph/images/maximize.gif",
-    "revision": "5cd13d6925493ab51e876694cc1c2ec2"
-  },
-  {
-    "url": "mxgraph/images/minimize.gif",
-    "revision": "8957741b9b0f86af9438775f2aadbb54"
-  },
-  {
-    "url": "mxgraph/images/close.gif",
-    "revision": "8b84669812ac7382984fca35de8da48b"
-  },
-  {
-    "url": "mxgraph/images/resize.gif",
-    "revision": "a6477612b3567a34033f9cac6184eed3"
-  },
-  {
-    "url": "mxgraph/images/separator.gif",
-    "revision": "7819742ff106c97da7a801c2372bbbe5"
-  },
-  {
-    "url": "mxgraph/images/window.gif",
-    "revision": "fd9a21dd4181f98052a202a0a01f18ab"
-  },
-  {
-    "url": "mxgraph/images/window-title.gif",
-    "revision": "3fb1d6c43246cdf991a11dfe826dfe99"
-  },
-  {
-    "url": "mxgraph/images/button.gif",
-    "revision": "00759bdc3ad218fa739f584369541809"
-  },
-  {
-    "url": "mxgraph/images/point.gif",
-    "revision": "83a43717b284902442620f61bc4e9fa6"
-  }
-],
+	workbox.precaching.precacheAndRoute([],
 	{
 	{
 		// Ignore all URL parameters
 		// Ignore all URL parameters
 		// FIXME: Using this /open redirects to /open.html so
 		// FIXME: Using this /open redirects to /open.html so

+ 11 - 5
src/main/webapp/styles/dark.css

@@ -6,6 +6,10 @@ html body.geEditor div.mxPopupMenu {
 	background:#2a2a2a;
 	background:#2a2a2a;
 	box-shadow:none;
 	box-shadow:none;
 }
 }
+html .geSidebarContainer button {
+	border: 1px solid #505759;
+	border-radius: 3px;
+}
 html body.geEditor div.mxPopupMenu hr {
 html body.geEditor div.mxPopupMenu hr {
 	background-color:#505759;
 	background-color:#505759;
 }
 }
@@ -13,7 +17,7 @@ html body .geTabContainer {
 	border-top:1px solid #505759;
 	border-top:1px solid #505759;
 	background-color:#2a2a2a;
 	background-color:#2a2a2a;
 }
 }
-html body .geMenubarContainer .geItem:active {
+html body .geMenubarContainer .geItem:active, html .geSidebarContainer button:active {
 	opacity: 0.7;
 	opacity: 0.7;
 }
 }
 html body, html body .geFooterContainer, html body #geFooterItem1, html body textarea,
 html body, html body .geFooterContainer, html body #geFooterItem1, html body textarea,
@@ -27,14 +31,15 @@ html body a {
 	color:#337ab7;
 	color:#337ab7;
 }
 }
 html body div.mxRubberband {
 html body div.mxRubberband {
-	border:1px dashed #ffffff !important; background:#505759 !important;
+	border:1px dashed #ffffff !important;
+	background:#505759 !important;
 }
 }
 html body .geTemplate {
 html body .geTemplate {
 	color:#000000;
 	color:#000000;
 }
 }
 html body .geToolbarContainer, html body .geSidebar, html body .geSidebarContainer .geTitle, html body input,
 html body .geToolbarContainer, html body .geSidebar, html body .geSidebarContainer .geTitle, html body input,
 html body textarea, html body button, html body .geColorBtn, html body .geBaseButton, html body .geSidebarTooltip,
 html body textarea, html body button, html body .geColorBtn, html body .geBaseButton, html body .geSidebarTooltip,
-html body .geBaseButton {
+html body .geBaseButton, html .geSidebarContainer button {
 	background:#2a2a2a;
 	background:#2a2a2a;
 	border-color:#505759;
 	border-color:#505759;
 	box-shadow:none;
 	box-shadow:none;
@@ -108,7 +113,8 @@ html body tr.mxPopupMenuItemHover {
 	color:#cccccc;
 	color:#cccccc;
 }
 }
 html body .geSidebarContainer .geTitle:hover, html body .geSidebarContainer .geItem:hover,
 html body .geSidebarContainer .geTitle:hover, html body .geSidebarContainer .geItem:hover,
-html body .geMenubarContainer .geItem:hover, html body .geBaseButton:hover {
+html body .geMenubarContainer .geItem:hover, html body .geBaseButton:hover,
+html .geSidebarContainer button:hover {
 	background:#000000;
 	background:#000000;
 }
 }
 html body .geToolbarContainer .geSeparator {
 html body .geToolbarContainer .geSeparator {
@@ -120,7 +126,7 @@ html body .geHsplit, html body .geVsplit, html body table.mxPopupMenu hr {
 }
 }
 html body .geToolbarContainer .geButton:hover, html body .geToolbarContainer .geButton:active,
 html body .geToolbarContainer .geButton:hover, html body .geToolbarContainer .geButton:active,
 html body .geToolbarContainer .geLabel:hover, html body .geToolbarContainer .geLabel:active,
 html body .geToolbarContainer .geLabel:hover, html body .geToolbarContainer .geLabel:active,
-html body .geHsplit:hover, html body .geVsplit:hover {
+html body .geHsplit:hover, html body .geVsplit:hover, html .geSidebarContainer button:active {
 	background-color:#000;
 	background-color:#000;
 }
 }
 html body .geToolbar {
 html body .geToolbar {