Browse Source

10.1.0 release

Gaudenz Alder 6 years ago
parent
commit
336e02cecf

+ 4 - 0
ChangeLog

@@ -1,3 +1,7 @@
+17-JAN-2019: 10.1.0
+
+- Adds auth token refresh for OneDrive
+
 16-JAN-2019: 10.0.43
 
 - Fixes possible NPE in hashValue

+ 1 - 1
VERSION

@@ -1 +1 @@
-10.0.43
+10.1.0

+ 151 - 79
src/main/java/com/mxgraph/online/MSGraphAuthServlet.java

@@ -20,32 +20,56 @@ import javax.servlet.http.HttpServletResponse;
 @SuppressWarnings("serial")
 public class MSGraphAuthServlet extends HttpServlet
 {
-	public static final String DEV_CLIENT_ID = "c36dee60-2c6d-4b5f-b552-a7d21798ea52";
-	public static final String CLIENT_ID = "45c10911-200f-4e27-a666-9e9fca147395";
-
-	public static final String DEV_REDIRECT_URI = "https://test.draw.io/microsoft";
-	public static final String REDIRECT_URI = "https://www.draw.io/microsoft";
 
 	/**
-	 * Path component under war/ to locate development client secret file.
+	 * 
 	 */
 	public static final String DEV_CLIENT_SECRET_FILE_PATH = "/WEB-INF/msgraph_dev_client_secret";
 
+	/**
+	 * 
+	 */
+	private static String DEV_CLIENT_SECRET = null;
 
 	/**
-	 * Path component under war/ to locate client secret file.
+	 * 
 	 */
 	public static final String CLIENT_SECRET_FILE_PATH = "/WEB-INF/msgraph_client_secret";
 
 	/**
 	 * 
 	 */
-	private static String DEV_CLIENT_SECRET = null;
+	private static String CLIENT_SECRET = null;
 
 	/**
 	 * 
 	 */
-	private static String CLIENT_SECRET = null;
+	public static final String DEV_CLIENT_ID_FILE_PATH = "/WEB-INF/msgraph_dev_client_id";
+
+	/**
+	 * 
+	 */
+	private static String DEV_CLIENT_ID = null;
+
+	/**
+	 * 
+	 */
+	public static final String CLIENT_ID_FILE_PATH = "/WEB-INF/msgraph_client_id";
+
+	/**
+	 * 
+	 */
+	private static String CLIENT_ID = null;
+
+	/**
+	 * 
+	 */
+	public static final String DEV_REDIRECT_URI = "https://test.draw.io/microsoft";
+
+	/**
+	 * 
+	 */
+	public static final String REDIRECT_URI = "https://www.draw.io/microsoft";
 
 	/**
 	 * @see HttpServlet#HttpServlet()
@@ -89,6 +113,36 @@ public class MSGraphAuthServlet extends HttpServlet
 				throw new RuntimeException("Client secret path invalid.");
 			}
 		}
+
+		if (DEV_CLIENT_ID == null)
+		{
+			try
+			{
+				DEV_CLIENT_ID = Utils
+						.readInputStream(getServletContext()
+								.getResourceAsStream(DEV_CLIENT_ID_FILE_PATH))
+						.replaceAll("\n", "");
+			}
+			catch (IOException e)
+			{
+				throw new RuntimeException("Dev client ID invalid.");
+			}
+		}
+		
+		if (CLIENT_ID == null)
+		{
+			try
+			{
+				CLIENT_ID = Utils
+						.readInputStream(getServletContext()
+								.getResourceAsStream(CLIENT_ID_FILE_PATH))
+						.replaceAll("\n", "");
+			}
+			catch (IOException e)
+			{
+				throw new RuntimeException("Client ID invalid.");
+			}
+		}
 	}
 
 	/**
@@ -123,82 +177,100 @@ public class MSGraphAuthServlet extends HttpServlet
 		}
 		else
 		{
-			String url = "https://login.microsoftonline.com/common/oauth2/v2.0/token";
-			URL obj = new URL(url);
-			HttpURLConnection con = (HttpURLConnection) obj.openConnection();
-
-			con.setRequestMethod("POST");
-			con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
-
-			boolean jsonResponse = false;
-			StringBuilder urlParameters = new StringBuilder();
-			
-			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 res = new StringBuffer();
-
-			//Call the opener callback function directly with the given json
-			if (!jsonResponse)
+			try
 			{
-				res.append("<!DOCTYPE html><html><head><script>");
-				res.append("if (window.opener != null && window.opener.onOneDriveCallback != null)"); 
-				res.append("{");
-				res.append("	window.opener.onOneDriveCallback("); //The following is a json containing access_token and redresh_token
+				String url = "https://login.microsoftonline.com/common/oauth2/v2.0/token";
+				URL obj = new URL(url);
+				HttpURLConnection con = (HttpURLConnection) obj.openConnection();
+	
+				con.setRequestMethod("POST");
+				con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+	
+				boolean jsonResponse = false;
+				StringBuilder urlParameters = new StringBuilder();
+				
+				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 res = new StringBuffer();
+	
+				//Call the opener callback function directly with the given json
+				if (!jsonResponse)
+				{
+					res.append("<!DOCTYPE html><html><head><script>");
+					res.append("if (window.opener != null && window.opener.onOneDriveCallback != null)"); 
+					res.append("{");
+					res.append("	window.opener.onOneDriveCallback("); //The following is a json containing access_token and redresh_token
+				}
+				
+				while ((inputLine = in.readLine()) != null)
+				{
+					res.append(inputLine);
+				}
+				in.close();
+	
+				if (!jsonResponse)
+				{
+					res.append("	, window);");
+					res.append("}");
+					res.append("</script></head><body></body></html>");
+				}
+	
+				response.setStatus(con.getResponseCode());
+				
+				OutputStream out = response.getOutputStream();
+	
+				PrintWriter writer = new PrintWriter(out);
+	
+				// Writes JavaScript code
+				writer.println(res.toString());
+	
+				writer.flush();
+				writer.close();
 			}
-			
-			while ((inputLine = in.readLine()) != null)
+			catch(IOException e)
 			{
-				res.append(inputLine);
+				if (e.getMessage().contains("401"))
+				{
+					response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+				}
+				else
+				{
+					response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+				}
 			}
-			in.close();
-
-			if (!jsonResponse)
+			catch (Exception e) 
 			{
-				res.append("	, window);");
-				res.append("}");
-				res.append("</script></head><body></body></html>");
+				response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
 			}
-
-			response.setStatus(con.getResponseCode());
-			
-			OutputStream out = response.getOutputStream();
-
-			PrintWriter writer = new PrintWriter(out);
-
-			// Writes JavaScript code
-			writer.println(res.toString());
-
-			writer.flush();
-			writer.close();
 		}
 	}
 

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

@@ -17,7 +17,7 @@
 	</include>
   </static-files>
 
-  <instance-class>F2</instance-class>
+  <instance-class>F1</instance-class>
   <automatic-scaling>
     <min-idle-instances>1</min-idle-instances>
     <max-idle-instances>1</max-idle-instances>

+ 1 - 0
src/main/webapp/WEB-INF/msgraph_client_id

@@ -0,0 +1 @@
+Replace_with_your_own_microsoft_graph_client_id

+ 1 - 0
src/main/webapp/WEB-INF/msgraph_client_secret

@@ -0,0 +1 @@
+Replace_with_your_own_microsoft_graph_client_secret

+ 1 - 0
src/main/webapp/WEB-INF/msgraph_dev_client_id

@@ -0,0 +1 @@
+Replace_with_your_own_microsoft_graph_client_id

+ 1 - 1
src/main/webapp/WEB-INF/msgraph_dev_client_secret

@@ -1 +1 @@
-paFCJ4371-^cdthoVFCF1^=
+Replace_with_your_own_microsoft_graph_client_secret

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

@@ -1,7 +1,7 @@
 CACHE MANIFEST
 
 # THIS FILE WAS GENERATED. DO NOT MODIFY!
-# 01/16/2019 04:40 PM
+# 01/17/2019 04:16 PM
 
 app.html
 index.html?offline=1

File diff suppressed because it is too large
+ 620 - 590
src/main/webapp/js/app.min.js


File diff suppressed because it is too large
+ 632 - 612
src/main/webapp/js/atlas-viewer.min.js


File diff suppressed because it is too large
+ 758 - 734
src/main/webapp/js/atlas.min.js


+ 5 - 4
src/main/webapp/js/diagramly/App.js

@@ -167,9 +167,10 @@ App.DROPBOX_URL = 'js/dropbox/Dropbox-sdk.min.js';
 App.DROPINS_URL = 'https://www.dropbox.com/static/api/2/dropins.js';
 
 /**
- * Sets the delay for autosave in milliseconds. Default is 2000.
+ * OneDrive Client JS (file/folder picker). This is a slightly modified version to allow using accessTokens
+ * But it doesn't work for IE11, so we fallback to the original one
  */
-App.ONEDRIVE_URL = 'https://js.live.net/v7.2/OneDrive.js';
+App.ONEDRIVE_URL = mxClient.IS_IE11? 'https://js.live.net/v7.2/OneDrive.js' : 'js/onedrive/OneDrive.js';
 
 /**
  * Trello URL
@@ -4184,7 +4185,7 @@ App.prototype.save = function(name, done)
  * if a valid folder was chosen for a mode that supports it. No callback
  * is made if no folder was chosen for a mode that supports it.
  */
-App.prototype.pickFolder = function(mode, fn, enabled)
+App.prototype.pickFolder = function(mode, fn, enabled, direct)
 {
 	enabled = (enabled != null) ? enabled : true;
 	var resume = this.spinner.pause();
@@ -4221,7 +4222,7 @@ App.prototype.pickFolder = function(mode, fn, enabled)
 				folderId = OneDriveFile.prototype.getIdOf(files.value[0]);
         		fn(folderId);
 			}
-		}));
+		}), direct);
 	}
 	else if (enabled && mode == App.MODE_GITHUB && this.gitHub != null)
 	{

+ 78 - 2
src/main/webapp/js/diagramly/Dialogs.js

@@ -193,6 +193,16 @@ var StorageDialog = function(editorUi, fn, rowLimit)
 						fn();
 					}));
 				}
+				else if (mode == App.MODE_ONEDRIVE && editorUi.spinner.spin(document.body, mxResources.get('authorizing')))
+				{
+					// Tries immediate authentication
+					editorUi.oneDrive.checkToken(mxUtils.bind(this, function()
+					{
+						editorUi.spinner.stop();
+						editorUi.setMode(mode, cb.checked);
+						fn();
+					}));
+				}
 				else
 				{
 					editorUi.setMode(mode, cb.checked);
@@ -234,9 +244,9 @@ var StorageDialog = function(editorUi, fn, rowLimit)
 				}
 			}, 30000);
 			
-			editorUi.addListener('clientLoaded', mxUtils.bind(this, function()
+			editorUi.addListener('clientLoaded', mxUtils.bind(this, function(sender, evt)
 			{
-				if (editorUi[clientName] != null)
+				if (editorUi[clientName] != null && evt.getProperty('client') == editorUi[clientName])
 				{
 					window.clearTimeout(timeout);
 					mxUtils.setOpacity(label, 100);
@@ -9411,3 +9421,69 @@ TemplatesDialog.prototype.init = function(editorUi, callback, cancelCallback,
 		editorUi.hideDialog(true);
 	});
 };
+
+
+/**
+ * Constructs a new popup opener button dialog.
+ */
+var BtnDialog = function(editorUi, peer, btnLbl, fn)
+{
+	var div = document.createElement('div');
+	div.style.textAlign = 'center';
+	
+	var hd = document.createElement('p');
+	hd.style.fontSize = '16pt';
+	hd.style.padding = '0px';
+	hd.style.margin = '0px';
+	hd.style.color = 'gray';
+	
+	mxUtils.write(hd, mxResources.get('done'));
+	
+	var service = 'Unknown';
+	
+	var img = document.createElement('img');
+	img.setAttribute('border', '0');
+	img.setAttribute('align', 'absmiddle');
+	img.style.marginRight = '10px';
+
+	if (peer == editorUi.drive)
+	{
+		service = mxResources.get('googleDrive');
+		img.src = IMAGE_PATH + '/google-drive-logo-white.svg';
+	}
+	else if (peer == editorUi.dropbox)
+	{
+		service = mxResources.get('dropbox');
+		img.src = IMAGE_PATH + '/dropbox-logo-white.svg';
+	}
+	else if (peer == editorUi.oneDrive)
+	{
+		service = mxResources.get('oneDrive');
+		img.src = IMAGE_PATH + '/onedrive-logo-white.svg';
+	}
+	else if (peer == editorUi.gitHub)
+	{
+		service = mxResources.get('github');
+		img.src = IMAGE_PATH + '/github-logo-white.svg';
+	}
+	else if (peer == editorUi.trello)
+	{
+		service = mxResources.get('trello');
+		img.src = IMAGE_PATH + '/trello-logo-white.svg';
+	}
+	
+	var p = document.createElement('p');
+	mxUtils.write(p, mxResources.get('authorizedIn', [service], 'You are now authorized in {1}'));
+
+	var button = mxUtils.button(btnLbl, fn);
+
+	button.insertBefore(img, button.firstChild);
+	button.style.marginTop = '6px';
+	button.className = 'geBigButton';
+
+	div.appendChild(hd);
+	div.appendChild(p);
+	div.appendChild(button);
+	
+	this.container = div;
+};

+ 17 - 4
src/main/webapp/js/diagramly/DrawioClient.js

@@ -49,6 +49,7 @@ DrawioClient.prototype.clearPersistentToken = function()
 	if (isLocalStorage)
 	{
 		localStorage.removeItem('.' + this.cookieName);
+		sessionStorage.removeItem('.' + this.cookieName);
 	}
 	else if (typeof(Storage) != 'undefined')
 	{
@@ -61,13 +62,18 @@ DrawioClient.prototype.clearPersistentToken = function()
 /**
  * Authorizes the client, gets the userId and calls <open>.
  */
-DrawioClient.prototype.getPersistentToken = function()
+DrawioClient.prototype.getPersistentToken = function(trySessionStorage)
 {
 	var token = null;
 	
 	if (isLocalStorage)
 	{
 		token = localStorage.getItem('.' + this.cookieName);
+		
+		if (token == null && trySessionStorage)
+		{
+			token = sessionStorage.getItem('.' + this.cookieName);
+		}
 	}
 	
 	if (token == null && typeof(Storage) != 'undefined')
@@ -110,19 +116,26 @@ DrawioClient.prototype.getPersistentToken = function()
 /**
  * Authorizes the client, gets the userId and calls <open>.
  */
-DrawioClient.prototype.setPersistentToken = function(token)
+DrawioClient.prototype.setPersistentToken = function(token, sessionOnly)
 {
 	if (token != null)
 	{
 		if (isLocalStorage)
 		{
-			localStorage.setItem('.' + this.cookieName, token);
+			if (sessionOnly)
+			{
+				sessionStorage.setItem('.' + this.cookieName, token);
+			}
+			else 
+			{
+				localStorage.setItem('.' + this.cookieName, token);
+			}
 		}
 		else if (typeof(Storage) != 'undefined')
 		{
 			var expiration = new Date();
 			expiration.setYear(expiration.getFullYear() + 10);
-			var cookie = this.cookieName + '=' + token +'; path=/; expires=' + expiration.toUTCString();
+			var cookie = this.cookieName + '=' + token + '; path=/' + (sessionOnly? '' : '; expires=' + expiration.toUTCString());
 	
 			if (document.location.protocol.toLowerCase() == 'https')
 			{

+ 77 - 51
src/main/webapp/js/diagramly/DrawioFile.js

@@ -28,6 +28,7 @@ DrawioFile = function(ui, data)
 		lastMerge: 0, /* details of the last successful merge */
 		lastMergeTime: 0, /* timestamp of the last call to merge */
 		lastOpenTime: 0, /* timestamp of the last call to open */
+		lastIgnored: 0, /* timestamp of the last ignored mergeFile */
 		shadowState: 0, /* current etag hash for shadow */
 		opened: 0, /* number of calls to open */
 		closed: 0, /* number of calls to close */
@@ -245,68 +246,93 @@ DrawioFile.prototype.mergeFile = function(file, success, error, diffShadow)
 		this.checkPages(shadow, 'mergeFile init');
 	
 		// Loads new document as shadow document
-		this.shadowPages = this.ui.getPagesForNode(
+		var pages = this.ui.getPagesForNode(
 			mxUtils.parseXml(file.data).
 			documentElement)
-					
-		// Creates a patch for backup if the checksum fails
-		this.backupPatch = (this.isModified()) ?
-			this.ui.diffPages(shadow,
-			this.ui.pages) : null;
-		
-		// Patches the current document
-		var patches = [this.ui.diffPages((diffShadow != null) ?
-			diffShadow : shadow, this.shadowPages)];
-
-		if (!this.ignorePatches(patches))
-		{
-			// Patching previous shadow to verify checksum
-			var patched = this.ui.patchPages(shadow, patches[0]);
-			this.stats.shadowState = this.ui.hashValue(file.getCurrentEtag());
-			this.checkPages(patched, 'mergeFile patched');
 			
-			var patchedDetails = {};
-			var checksum = this.ui.getHashValueForPages(patched, patchedDetails);
-			var currentDetails = {};
-			var current = this.ui.getHashValueForPages(this.shadowPages, currentDetails);
-			
-			if (urlParams['test'] == '1')
-			{
-				EditorUi.debug('File.mergeFile', [this],
-					'backup', this.backupPatch,
-					'patches', patches,
-					'checksum', current == checksum, checksum);
-			}
+		if (pages != null && pages.length > 0)
+		{
+			this.shadowPages = pages;
+
+			// Creates a patch for backup if the checksum fails
+			this.backupPatch = (this.isModified()) ?
+				this.ui.diffPages(shadow,
+				this.ui.pages) : null;
 			
-			if (checksum != null && checksum != current)
+			// Patches the current document
+			var patches = [this.ui.diffPages((diffShadow != null) ?
+				diffShadow : shadow, this.shadowPages)];
+	
+			if (!this.ignorePatches(patches))
 			{
-				var data = this.compressReportData(this.getAnonymizedXmlForPages(patched));
+				// Patching previous shadow to verify checksum
+				var patched = this.ui.patchPages(shadow, patches[0]);
+				this.stats.shadowState = this.ui.hashValue(file.getCurrentEtag());
+				this.checkPages(patched, 'mergeFile patched');
 				
-				this.checksumError(error, patches,
-					((patchedDetails != null) ? ('Details: ' +
-						JSON.stringify(patchedDetails)) : '') +
-					'\nChecksum: ' + checksum +
-					'\nCurrent: ' + current +
-					((currentDetails != null) ? ('\nCurrent Details: ' +
-						JSON.stringify(currentDetails)) : '') +
-					'\nPatched:\n' + data);
+				var patchedDetails = {};
+				var checksum = this.ui.getHashValueForPages(patched, patchedDetails);
+				var currentDetails = {};
+				var current = this.ui.getHashValueForPages(this.shadowPages, currentDetails);
 				
-				// Abnormal termination
-				return;
+				if (urlParams['test'] == '1')
+				{
+					EditorUi.debug('File.mergeFile', [this],
+						'backup', this.backupPatch,
+						'patches', patches,
+						'checksum', current == checksum, checksum);
+				}
+				
+				if (checksum != null && checksum != current)
+				{
+					var data = this.compressReportData(this.getAnonymizedXmlForPages(patched));
+					
+					this.checksumError(error, patches,
+						((patchedDetails != null) ? ('Details: ' +
+							JSON.stringify(patchedDetails)) : '') +
+						'\nChecksum: ' + checksum +
+						'\nCurrent: ' + current +
+						((currentDetails != null) ? ('\nCurrent Details: ' +
+							JSON.stringify(currentDetails)) : '') +
+						'\nPatched:\n' + data);
+					
+					// Abnormal termination
+					return;
+				}
+				else
+				{
+					this.patch(patches,
+						(DrawioFile.LAST_WRITE_WINS) ?
+						this.backupPatch : null);
+					this.checkPages(this.ui.pages, 'mergeFile done');
+				}
 			}
 			else
 			{
-				this.patch(patches,
-					(DrawioFile.LAST_WRITE_WINS) ?
-					this.backupPatch : null);
-				this.checkPages(this.ui.pages, 'mergeFile done');
+				this.stats.shadowState = this.ui.hashValue(file.getCurrentEtag());
 			}
 		}
 		else
 		{
-			this.stats.shadowState = this.ui.hashValue(file.getCurrentEtag());
+			try
+			{
+				// Report only once per session
+				if (this.stats.lastIgnored == 0)
+				{
+					this.sendErrorReport('Ignored empty pages in mergeFile',
+						'File Data: ' + this.compressReportData(
+						this.ui.anonymizeString(file.data),
+						null, 500));
+				}
+				
+				this.stats.lastIgnored = new Date().toISOString();
+			}
+			catch (e2)
+			{
+				// ignore
+			}
 		}
-
+	
 		this.invalidChecksum = false;
 		this.inConflictState = false;
 		this.setDescriptor(file.getDescriptor());
@@ -462,7 +488,7 @@ DrawioFile.prototype.checksumError = function(error, patches, details, etag)
 				'Checksum Error',
 				((details != null) ? (details) : '') +
 				'\n\nPatches:\n' + json +
-				((remote != null) ? ('\n\nHeadRevision:\n' + remote) : ''),
+				((remote != null) ? ('\n\nMaster:\n' + remote) : ''),
 				err, 70000);
 		});
 
@@ -1690,8 +1716,8 @@ DrawioFile.prototype.fileSaved = function(savedData, lastDesc, success, error)
 		try
 		{
 			this.sendErrorReport('Error in fileSaved',
-				'SavedData:\n' + this.compressReportData(
-				this.ui.anonymizeString(savedData), 5000), e);
+				'Saved Data:\n' + this.compressReportData(
+				this.ui.anonymizeString(savedData), null, 500), e);
 		}
 		catch (e2)
 		{
@@ -1918,4 +1944,4 @@ DrawioFile.prototype.destroy = function()
 		this.sync.destroy();
 		this.sync = null;
 	}
-};
+};

+ 1 - 1
src/main/webapp/js/diagramly/DrawioFileSync.js

@@ -1100,7 +1100,7 @@ DrawioFileSync.prototype.fileSaved = function(pages, lastDesc, success, error)
 	{
 		this.start();
 
-		if (this.channelId != null)
+		if (this.channelId != null && this.isConnected())
 		{
 			// Computes diff and checksum
 			var shadow = (this.file.shadowPages != null) ?

+ 11 - 4
src/main/webapp/js/diagramly/EditorUi.js

@@ -6533,10 +6533,12 @@
 		{
 			if (!noErrorHandling)
 			{
-				this.handleError(e, mxResources.get('invalidOrMissingFile'));
+				this.handleError(e);
+			}
+			else
+			{
+				throw e;
 			}
-			
-			throw e;
 		}
 		
 		return cells;
@@ -6680,7 +6682,12 @@
 			{
 				try
 				{
-					new VsdxExport(this).exportCurrentDiagrams();
+					var expSuccess = new VsdxExport(this).exportCurrentDiagrams();
+					
+					if (!expSuccess)
+					{
+						this.handleError({message: mxResources.get('unknownError')});
+					}
 				}
 				catch (e)
 				{

+ 40 - 21
src/main/webapp/js/diagramly/Menus.js

@@ -937,7 +937,7 @@
 			mxResources.parse('testDevelop=Develop');
 			mxResources.parse('showBoundingBox=Show bounding box');
 			mxResources.parse('createSidebarEntry=Create Sidebar Entry');
-			mxResources.parse('testChecksum=Checksum');
+			mxResources.parse('testCheckFile=Check File');
 			mxResources.parse('testDiff=Diff');
 			mxResources.parse('testInspect=Inspect');
 			mxResources.parse('testShowConsole=Show Console');
@@ -965,7 +965,7 @@
 					'fillColor=none;strokeColor=red;');
 			}));
 	
-			editorUi.actions.addAction('testChecksum', mxUtils.bind(this, function()
+			editorUi.actions.addAction('testCheckFile', mxUtils.bind(this, function()
 			{
 				var xml = (editorUi.pages != null && editorUi.getCurrentFile() != null) ?
 					editorUi.getCurrentFile().getAnonymizedXmlForPages(editorUi.pages) : '';
@@ -977,34 +977,53 @@
 					{
 						try
 						{
+							if (newValue.charAt(0) != '<')
+							{
+								newValue = graph.decompress(newValue);
+								console.log('xml', newValue);
+							}
+							
 							var doc = mxUtils.parseXml(newValue);
 							var pages = editorUi.getPagesForNode(doc.documentElement, 'mxGraphModel');
-							var checksum = editorUi.getHashValueForPages(pages);
-							console.log('checksum', pages, checksum);
 							
-							// Checks for duplicates
-							var all = doc.getElementsByTagName('*');
-							var allIds = {};
-							var dups = {};
-							
-							for (var i = 0; i < all.length; i++)
+							if (pages != null && pages.length > 0)
 							{
-								var el = all[i];
+								var checksum = editorUi.getHashValueForPages(pages);
+								console.log('checksum', pages, checksum);
+							}
+							else
+							{
+								// Checks for duplicates
+								var all = doc.getElementsByTagName('*');
+								var allIds = {};
+								var dups = {};
 								
-								if (allIds[el.id] == null)
+								for (var i = 0; i < all.length; i++)
 								{
-									allIds[el.id] = el.id;
+									var el = all[i];
+									
+									if (el.id != null)
+									{
+										if (allIds[el.id] == null)
+										{
+											allIds[el.id] = el.id;
+										}
+										else
+										{
+											dups[el.id] = el.id;
+										}
+									}
+								}
+								
+								if (Object.keys(dups).length > 0)
+								{
+									console.log('duplicates', dups);
 								}
 								else
 								{
-									dups[el.id] = el.id;
+									console.log('no duplicates');
 								}
 							}
-							
-							if (dups.length > 0)
-							{
-								console.log('duplicates', dups);
-							}
 						}
 						catch (e)
 						{
@@ -1178,7 +1197,7 @@
 			this.put('testDevelop', new Menu(mxUtils.bind(this, function(menu, parent)
 			{
 				this.addMenuItems(menu, ['createSidebarEntry', 'showBoundingBox', '-',
-					'testChecksum', 'testDiff', '-', 'testInspect', '-',
+					'testCheckFile', 'testDiff', '-', 'testInspect', '-',
 					'testXmlImageExport', '-', 'testDownloadRtModel'], parent);
 
 				menu.addItem(mxResources.get('testImportRtModel') + '...', null, function()
@@ -2029,7 +2048,7 @@
 	        				editorUi.handleError(resp);
 	        			}));
 	            	}
-				}));
+				}), null, true);
 			}
 		}));
 		

+ 366 - 196
src/main/webapp/js/diagramly/OneDriveClient.js

@@ -4,9 +4,19 @@
  */
 OneDriveClient = function(editorUi)
 {
-	DrawioClient.call(this, editorUi, 'odauth');
+	DrawioClient.call(this, editorUi, 'oneDriveAuthInfo');
 	
-	this.token = this.token;
+	var authInfo = JSON.parse(this.token);
+	
+	if (authInfo != null)
+	{
+		this.token = authInfo.access_token;
+		this.endpointHint = authInfo.endpointHint;
+		this.tokenExpiresOn = authInfo.expiresOn;
+		
+		var remainingTime = (this.tokenExpiresOn - Date.now()) / 1000;
+		this.resetTokenRefresh(remainingTime < 600? 1 : remainingTime); //10 min tolerance window in case of any rounding errors
+	}
 };
 
 // Extends DrawioClient
@@ -18,17 +28,24 @@ mxUtils.extend(OneDriveClient, DrawioClient);
  * existing thumbnail with the placeholder only once.
  */
 OneDriveClient.prototype.clientId = (window.location.hostname == 'test.draw.io') ?
-	'2e598409-107f-4b59-89ca-d7723c8e00a4' : '45c10911-200f-4e27-a666-9e9fca147395';
+		'c36dee60-2c6d-4b5f-b552-a7d21798ea52' : '45c10911-200f-4e27-a666-9e9fca147395';
 
 /**
  * OAuth 2.0 scopes for installing Drive Apps.
  */
-OneDriveClient.prototype.scopes = 'user.read files.readwrite.all';
+OneDriveClient.prototype.scopes = 'user.read files.readwrite.all offline_access';
 
 /**
  * OAuth 2.0 scopes for installing Drive Apps.
  */
-OneDriveClient.prototype.redirectUri = 'https://' + window.location.hostname + '/onedrive3.html';
+OneDriveClient.prototype.redirectUri = 'https://' + window.location.hostname + '/microsoft';
+OneDriveClient.prototype.pickerRedirectUri = 'https://' + window.location.hostname + '/onedrive3.html';
+
+/**
+ * This is the default endpoint for personal accounts
+ */
+OneDriveClient.prototype.defEndpointHint = 'api.onedrive.com'; 
+OneDriveClient.prototype.endpointHint = OneDriveClient.prototype.defEndpointHint;
 
 /**
  * Executes the first step for connecting to Google Drive.
@@ -40,6 +57,11 @@ OneDriveClient.prototype.extension = '.html';
  */
 OneDriveClient.prototype.baseUrl = 'https://graph.microsoft.com/v1.0';
 
+/**
+ * Empty function used when no callback is needed
+ */
+OneDriveClient.prototype.emptyFn = function(){};
+
 /**
  * Checks if the client is authorized and calls the next step.
  */
@@ -102,10 +124,32 @@ OneDriveClient.prototype.updateUser = function(success, error, failOnAuth)
 	}), error);
 };
 
+OneDriveClient.prototype.resetTokenRefresh = function(expires_in)
+{
+	if (this.tokenRefreshThread != null)
+	{
+		window.clearTimeout(this.tokenRefreshThread);
+		this.tokenRefreshThread = null;
+	}
+
+	// Starts timer to refresh token before it expires
+	if (expires_in > 0)
+	{
+		this.tokenRefreshInterval = expires_in * 1000;
+		
+		this.tokenRefreshThread = window.setTimeout(mxUtils.bind(this, function()
+		{
+			//Get a new fresh accessToken
+			this.authenticate(this.emptyFn, this.emptyFn, true);
+		}), expires_in * 900);
+	}
+};
+
+
 /**
  * Authorizes the client, gets the userId and calls <open>.
  */
-OneDriveClient.prototype.authenticate = function(success, error)
+OneDriveClient.prototype.authenticate = function(success, error, failOnAuth)
 {
 	if (window.onOneDriveCallback == null)
 	{
@@ -113,93 +157,142 @@ OneDriveClient.prototype.authenticate = function(success, error)
 		{
 			var acceptAuthResponse = true;
 			
-			this.ui.showAuthDialog(this, true, mxUtils.bind(this, function(remember, authSuccess)
+			//Retry request with refreshed token
+			var authInfo = JSON.parse(this.getPersistentToken(true));
+			
+			if (authInfo != null)
 			{
-				var url = 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize' +
-					'?client_id=' + this.clientId + '&response_type=token' +
-					'&redirect_uri=' + encodeURIComponent(this.redirectUri) +
-					'&scope=' + encodeURIComponent(this.scopes) + 
-					'&response_mode=fragment';
-
-				var width = 525,
-					height = 525,
-					screenX = window.screenX,
-					screenY = window.screenY,
-					outerWidth = window.outerWidth,
-					outerHeight = window.outerHeight;
-				
-				var left = screenX + Math.max(outerWidth - width, 0) / 2;
-				var top = screenY + Math.max(outerHeight - height, 0) / 2;
+				var req = new mxXmlRequest(this.redirectUri + '?refresh_token=' + authInfo.refresh_token, null, 'GET');
 				
-				var features = ['width=' + width, 'height=' + height,
-				                'top=' + top, 'left=' + left,
-				                'status=no', 'resizable=yes',
-				                'toolbar=no', 'menubar=no',
-				                'scrollbars=yes'];
-				var popup = window.open(url, 'odauth', features.join(','));
-				
-				if (popup != null)
+				req.send(mxUtils.bind(this, function(req)
 				{
-					window.onOneDriveCallback = mxUtils.bind(this, function(token, authWindow)
+					if (req.getStatus() >= 200 && req.getStatus() <= 299)
 					{
-						if (acceptAuthResponse)
+						var authInfo = JSON.parse(req.getText());
+						this.token = authInfo.access_token;
+						authInfo.access_token = authInfo.access_token;
+						authInfo.refresh_token = authInfo.refresh_token;
+						authInfo.expiresOn = Date.now() + authInfo.expires_in * 1000;
+						this.tokenExpiresOn = authInfo.expiresOn;
+						
+						this.setPersistentToken(JSON.stringify(authInfo), !authInfo.remember);
+						this.resetTokenRefresh(authInfo.expires_in);
+						
+						success();
+					}
+					else 
+					{
+						this.clearPersistentToken();
+						this.setUser(null);
+						this.token = null;
+
+						if (req.getStatus() == 401 && !failOnAuth) // (Unauthorized) [e.g, invalid refresh token]
 						{
-							window.onOneDriveCallback = null;
-							acceptAuthResponse = false;
-							
-							try
+							auth();
+						}
+						else
+						{
+							error({message: mxResources.get('accessDenied'), retry: auth});
+						}
+					}
+				}), error);
+			}
+			else
+			{
+				this.ui.showAuthDialog(this, true, mxUtils.bind(this, function(remember, authSuccess)
+				{
+					var url = 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize' +
+						'?client_id=' + this.clientId + '&response_type=code' +
+						'&redirect_uri=' + encodeURIComponent(this.redirectUri) +
+						'&scope=' + encodeURIComponent(this.scopes);
+	
+					var width = 525,
+						height = 525,
+						screenX = window.screenX,
+						screenY = window.screenY,
+						outerWidth = window.outerWidth,
+						outerHeight = window.outerHeight;
+					
+					var left = screenX + Math.max(outerWidth - width, 0) / 2;
+					var top = screenY + Math.max(outerHeight - height, 0) / 2;
+					
+					var features = ['width=' + width, 'height=' + height,
+					                'top=' + top, 'left=' + left,
+					                'status=no', 'resizable=yes',
+					                'toolbar=no', 'menubar=no',
+					                'scrollbars=yes'];
+					var popup = window.open(url, 'odauth', features.join(','));
+					
+					if (popup != null)
+					{
+						window.onOneDriveCallback = mxUtils.bind(this, function(authInfo, authWindow)
+						{
+							if (acceptAuthResponse)
 							{
-								if (token == null)
+								window.onOneDriveCallback = null;
+								acceptAuthResponse = false;
+								
+								try
 								{
-									error({message: mxResources.get('accessDenied'), retry: auth});
-								}
-								else
-								{
-									if (authSuccess != null)
+									if (authInfo == null)
 									{
-										authSuccess();
+										error({message: mxResources.get('accessDenied'), retry: auth});
 									}
-									
-									this.setUser(null);
-									this.token = token;
-									
-									if (remember)
+									else
 									{
-										this.setPersistentToken(token);
+										if (authSuccess != null)
+										{
+											authSuccess();
+										}
+										
+										//IE had a security issue on accessing this object outside this callback
+										//this.authInfo = authInfo;
+										this.setUser(null);
+										this.token = authInfo.access_token;
+										authInfo.expiresOn = Date.now() + authInfo.expires_in * 1000;
+										this.tokenExpiresOn = authInfo.expiresOn;
+
+										authInfo.remember = remember;
+										this.setPersistentToken(JSON.stringify(authInfo), !remember);
+										this.resetTokenRefresh(authInfo.expires_in);
+
+										//Find out the type of the account + endpoint
+										this.getAccountTypeAndEndpoint(mxUtils.bind(this, function()
+										{
+											success();
+										}), error);
 									}
-									
-									success();
 								}
-							}
-							catch (e)
-							{
-								error(e);
-							}
-							finally
-							{
-								if (authWindow != null)
+								catch (e)
 								{
-									authWindow.close();
+									error(e);
+								}
+								finally
+								{
+									if (authWindow != null)
+									{
+										authWindow.close();
+									}
 								}
 							}
-						}
-						else if (authWindow != null)
-						{
-							authWindow.close();
-						}
-					});
-				
-					popup.focus();
-				}
-			}), mxUtils.bind(this, function()
-			{
-				if (acceptAuthResponse)
+							else if (authWindow != null)
+							{
+								authWindow.close();
+							}
+						});
+					
+						popup.focus();
+					}
+				}), mxUtils.bind(this, function()
 				{
-					window.onOneDriveCallback = null;
-					acceptAuthResponse = false;
-					error({message: mxResources.get('accessDenied'), retry: auth});
-				}
-			}));
+					if (acceptAuthResponse)
+					{
+						window.onOneDriveCallback = null;
+						acceptAuthResponse = false;
+						error({message: mxResources.get('accessDenied'), retry: auth});
+					}
+				}));
+			}
 		});
 		
 		auth();
@@ -210,6 +303,46 @@ OneDriveClient.prototype.authenticate = function(success, error)
 	}
 };
 
+
+OneDriveClient.prototype.getAccountTypeAndEndpoint = function(success, error)
+{
+	this.get(this.baseUrl + '/me/drive/root', mxUtils.bind(this, function(req)
+	{
+		try
+		{
+			if (req.getStatus() >= 200 && req.getStatus() <= 299)
+			{
+				var resp = JSON.parse(req.getText());
+				
+				if (resp.webUrl.indexOf('.sharepoint.com') > 0) 
+			 	{
+					this.endpointHint = resp.webUrl;
+				}
+				else
+				{
+					this.endpointHint = this.defEndpointHint;
+				}
+				
+			 	//Update authInfo with endpointHint
+			 	var authInfo = JSON.parse(this.getPersistentToken(true));
+			 	
+			 	if (authInfo != null)
+		 		{
+				 	authInfo.endpointHint = this.endpointHint;
+				 	this.setPersistentToken(JSON.stringify(authInfo), !authInfo.remember);
+		 		}
+			 	
+				success();
+				return;
+			}
+		}
+		catch(e) {}
+		//It is expected to work as this call immediately follows getting a fresh access token
+		error({message: mxResources.get('unknownError') + ' (Code: ' + req.getStatus() + ')'});
+		
+	}), error);
+};
+
 /**
  * Checks if the client is authorized and calls the next step.
  */
@@ -234,32 +367,21 @@ OneDriveClient.prototype.executeRequest = function(url, success, error)
 				// 404 (file not found) is a valid response for checkExists
 				if ((req.getStatus() >= 200 && req.getStatus() <= 299) || req.getStatus() == 404)
 				{
+					if (this.user == null)
+					{
+						this.updateUser(this.emptyFn, this.emptyFn, true);
+					}
+					
 					success(req);
 				}
 				// 400 is returns if wrong user for this file
 				else if (req.getStatus() === 401 || req.getStatus() === 400)
 				{
-					this.clearPersistentToken();
-					this.setUser(null);
-					this.token = null;
-					
-					if (!failOnAuth)
-					{
-						this.authenticate(function()
-						{
-							doExecute(true);
-						}, error);
-					}
-					else
+					//Authorize again using the refresh token
+					this.authenticate(function()
 					{
-						error({message: mxResources.get('accessDenied'), retry: mxUtils.bind(this, function()
-						{
-							this.authenticate(function()
-							{
-								fn(true);
-							}, error);
-						})});
-					}
+						doExecute(true);
+					}, error, failOnAuth);
 				}
 				else
 				{
@@ -269,31 +391,31 @@ OneDriveClient.prototype.executeRequest = function(url, success, error)
 		}), error);
 	});
 	
-	var fn = mxUtils.bind(this, function(failOnAuth)
-	{
-		if (this.user == null)
-		{
-			this.updateUser(function()
-			{
-				fn(true);
-			}, error, failOnAuth);
-		}
-		else
-		{
-			doExecute(failOnAuth);
-		}
-	});
-
-	if (this.token == null)
+	if (this.token == null || this.tokenExpiresOn - Date.now() < 60000) //60 sec tolerance window
 	{
 		this.authenticate(function()
 		{
-			fn(true);
+			doExecute(true);
 		}, error);
 	}
 	else
 	{
-		fn(false);
+		doExecute(false);
+	}
+};
+
+/**
+ * Checks if the client is authorized and calls the next step.
+ */
+OneDriveClient.prototype.checkToken = function(fn)
+{
+	if (this.token == null || this.tokenRefreshThread == null || this.tokenExpiresOn - Date.now() < 60000)
+	{
+		this.authenticate(fn, this.emptyFn);
+	}
+	else
+	{
+		fn();
 	}
 };
 
@@ -653,31 +775,19 @@ OneDriveClient.prototype.writeFile = function(url, data, method, contentType, su
 		    	{
 			    	if (req.getStatus() >= 200 && req.getStatus() <= 299)
 					{
+			    		if (this.user == null)
+						{
+							this.updateUser(this.emptyFn, this.emptyFn, true);
+						}
+			    		
 						success(JSON.parse(req.getText()));
 					}
 					else if (req.getStatus() === 401)
 					{
-						this.clearPersistentToken();
-						this.setUser(null);
-						this.token = null;
-						
-						if (!failOnAuth)
-						{
-							this.authenticate(function()
-							{
-								doExecute(true);
-							}, error);
-						}
-						else
+						this.authenticate(function()
 						{
-							error({message: mxResources.get('accessDenied'), retry: mxUtils.bind(this, function()
-							{
-								this.authenticate(function()
-								{
-									fn(true);
-								}, error);
-							})});
-						}
+							doExecute(true);
+						}, error, failOnAuth);
 					}
 					else
 					{
@@ -695,31 +805,16 @@ OneDriveClient.prototype.writeFile = function(url, data, method, contentType, su
 			}));
 		});
 		
-		var fn = mxUtils.bind(this, function(failOnAuth)
-		{
-			if (this.user == null)
-			{
-				this.updateUser(function()
-				{
-					fn(true);
-				}, error, failOnAuth);
-			}
-			else
-			{
-				doExecute(failOnAuth);
-			}
-		});
-
-		if (this.token == null)
+		if (this.token == null || this.tokenExpiresOn - Date.now() < 60000) //60 sec tolerance window
 		{
 			this.authenticate(function()
 			{
-				fn(true);
+				doExecute(true);
 			}, error);
 		}
 		else
 		{
-			fn(false);
+			doExecute(false);
 		}
 	}
 	else
@@ -762,33 +857,76 @@ OneDriveClient.prototype.pickLibrary = function(fn)
 /**
  * Checks if the client is authorized and calls the next step.
  */
-OneDriveClient.prototype.pickFolder = function(fn)
+OneDriveClient.prototype.pickFolder = function(fn, direct)
 {
-	OneDrive.save(
+	var odSaveDlg = mxUtils.bind(this, function(direct)
 	{
-		clientId: this.clientId,
-		action: 'query',
-		openInNewWindow: true,
-		advanced:
+		var openSaveDlg = mxUtils.bind(this, function()
 		{
-			'redirectUri': this.redirectUri,
-			'queryParameters': 'select=id,name,parentReference'
-		},
-		success: mxUtils.bind(this, function(files)
+			OneDrive.save(
+			{
+				clientId: this.clientId,
+				action: 'query',
+				openInNewWindow: true,
+				advanced:
+				{
+					'endpointHint': mxClient.IS_IE11? null : this.endpointHint, //IE11 doen't work with our modified version, so, setting endpointHint disable using our token BUT will force relogin!
+					'redirectUri': this.pickerRedirectUri,
+					'queryParameters': 'select=id,name,parentReference',
+					'accessToken': this.token,
+					isConsumerAccount: false
+				},
+				success: mxUtils.bind(this, function(files)
+				{
+					fn(files);
+					
+					//Update the token in case a login with a different user
+					if (mxClient.IS_IE11)
+					{
+						this.token = files.accessToken;
+					}
+				}),
+				cancel: mxUtils.bind(this, function()
+				{
+					// do nothing
+				}),
+				error: mxUtils.bind(this, function(e)
+				{
+					this.ui.showError(mxResources.get('error'), e);
+				})
+			});
+		});
+		
+		if (direct)
 		{
-			// KNOWN: Token should be per I/O operation
-			this.token = files.accessToken;
-			fn(files);
-		}),
-		cancel: function()
+			openSaveDlg();
+		}
+		else
 		{
-			// do nothing
-		},
-		error: mxUtils.bind(this, function(e)
+			this.ui.confirm(mxResources.get('useRootFolder'), mxUtils.bind(this, function()
+			{
+				fn({value: [{id: 'root', name: 'root', parentReference: {driveId: 'me'}}]});
+				
+			}), openSaveDlg, mxResources.get('yes'), mxResources.get('no'));
+		}
+		
+		if (this.user == null)
 		{
-			this.ui.showError(mxResources.get('error'), e);
-		})
+			this.updateUser(this.emptyFn, this.emptyFn, true);
+		}
 	});
+	
+	if (this.token == null || this.tokenExpiresOn - Date.now() < 60000) //60 sec tolerance window
+	{
+		this.authenticate(mxUtils.bind(this, function()
+		{
+			odSaveDlg(false);
+		}), this.emptyFn);
+	}
+	else
+	{
+		odSaveDlg(direct);
+	}
 };
 
 /**
@@ -801,34 +939,66 @@ OneDriveClient.prototype.pickFile = function(fn)
 		this.ui.loadFile('W' + encodeURIComponent(id));
 	});
 	
-	OneDrive.open(
+	var odOpenDlg = mxUtils.bind(this, function()
 	{
-		clientId: this.clientId,
-		action: 'query',
-		multiSelect: false,
-		advanced:
-		{
-			'redirectUri': this.redirectUri,
-			'queryParameters': 'select=id,name,parentReference' //We can also get @microsoft.graph.downloadUrl within this request but it will break the normal process
-		},
-		success: mxUtils.bind(this, function(files)
+		OneDrive.open(
 		{
-			if (files != null && files.value != null && files.value.length > 0)
+			clientId: this.clientId,
+			action: 'query',
+			multiSelect: false,
+			advanced:
 			{
-				// KNOWN: Token should be per I/O operation
-				this.token = files.accessToken;
-				fn(OneDriveFile.prototype.getIdOf(files.value[0]), files);
-			}
-		}),
-		cancel: function()
-		{
-			// do nothing
-		},
-		error: mxUtils.bind(this, function(e)
+				'endpointHint': mxClient.IS_IE11? null : this.endpointHint, //IE11 doen't work with our modified version, so, setting endpointHint disable using our token BUT will force relogin!
+				'redirectUri': this.pickerRedirectUri,
+				'queryParameters': 'select=id,name,parentReference', //We can also get @microsoft.graph.downloadUrl within this request but it will break the normal process
+				'accessToken': this.token,
+				isConsumerAccount: false
+			},
+			success: mxUtils.bind(this, function(files)
+			{
+				if (files != null && files.value != null && files.value.length > 0)
+				{
+					//Update the token in case a login with a different user
+					if (mxClient.IS_IE11)
+					{
+						this.token = files.accessToken;
+					}
+					
+					fn(OneDriveFile.prototype.getIdOf(files.value[0]), files);
+				}
+			}),
+			cancel: mxUtils.bind(this, function()
+			{
+				// do nothing
+			}),
+			error: mxUtils.bind(this, function(e)
+			{
+				this.ui.showError(mxResources.get('error'), e);
+			})
+		});
+		
+		if (this.user == null)
 		{
-			this.ui.showError(mxResources.get('error'), e);
-		})
+			this.updateUser(this.emptyFn, this.emptyFn, true);
+		}
 	});
+	
+	if (this.token == null || this.tokenExpiresOn - Date.now() < 60000) //60 sec tolerance window
+	{
+		this.authenticate(mxUtils.bind(this, function()
+		{
+			this.ui.showDialog(new BtnDialog(this.ui, this, mxResources.get('open'), mxUtils.bind(this, function()
+			{
+				odOpenDlg();
+				this.ui.hideDialog();
+							
+			})).container, 300, 140, true, true);
+		}), this.emptyFn);
+	}
+	else
+	{
+		odOpenDlg();
+	}
 };
 
 /**

+ 12 - 4
src/main/webapp/js/diagramly/vsdx/VsdxExport.js

@@ -392,7 +392,7 @@ function VsdxExport(editorUi)
 	{
 		var state = graph.view.getState(cell, true);
 		
-		if (state == null)
+		if (state == null || state.absolutePoints == null || state.cellBounds == null)
 		{
 			return null;
 		}
@@ -531,16 +531,23 @@ function VsdxExport(editorUi)
 				var subShape = convertMxCell2Shape(cell, graph, xmlDoc, geo.height, geo, true);
 				cell.treatAsSingle = false;
 				cell.setGeometry(geo);
-				gShapes.appendChild(subShape);
+				
+				if (subShape != null)
+				{
+					gShapes.appendChild(subShape);
+				}
 				
 				//add group children
-				for (var i = 0; i < cell.children.length; i++)
+				for (var i = 0; i < cell.getChildCount(); i++)
 				{
 					var child = cell.children[i];
 					
 					var subShape = convertMxCell2Shape(child, graph, xmlDoc, geo.height, geo, true);
 					
-					gShapes.appendChild(subShape);
+					if (subShape != null)
+					{
+						gShapes.appendChild(subShape);
+					}
 				}
 				
 				shape.appendChild(gShapes);
@@ -889,6 +896,7 @@ function VsdxExport(editorUi)
 		catch(e) 
 		{
 			console.log(e);
+			editorUi.spinner.stop();
 			return false;
 		}
 	};	

File diff suppressed because it is too large
+ 133 - 114
src/main/webapp/js/embed-static.min.js


File diff suppressed because it is too large
+ 7 - 7
src/main/webapp/js/extensions.min.js


+ 265 - 16
src/main/webapp/js/mxgraph/Shapes.js

@@ -3825,8 +3825,54 @@
 
 	// Defines connection points for all shapes
 	IsoRectangleShape.prototype.constraints = [];
-	IsoCubeShape.prototype.constraints = [];
-	CalloutShape.prototype.constraints = [];
+	
+	IsoCubeShape.prototype.getConstraints = function(style, w, h)
+	{
+		var constr = [];
+		var tan30 = Math.tan(mxUtils.toRadians(30));
+		var tan30Dx = (0.5 - tan30) / 2;
+		var m = Math.min(w, h / (0.5 + tan30));
+		var dx = (w - m) / 2;
+		var dy = (h - m) / 2;
+
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx, dy + 0.25 * m));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx + 0.5 * m, dy + m * tan30Dx));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx + m, dy + 0.25 * m));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx + m, dy + 0.75 * m));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx + 0.5 * m, dy + (1 - tan30Dx) * m));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx, dy + 0.75 * m));
+
+		return (constr);
+	};
+
+	CalloutShape.prototype.getConstraints = function(style, w, h)
+	{
+		var constr = [];
+		var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2;
+		var s = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'size', this.size))));
+		var dx = w * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'position', this.position))));
+		var dx2 = w * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'position2', this.position2))));
+		var base = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'base', this.base))));
+		
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false));
+		constr.push(new mxConnectionConstraint(new mxPoint(0.25, 0), false));
+		constr.push(new mxConnectionConstraint(new mxPoint(0.5, 0), false));
+		constr.push(new mxConnectionConstraint(new mxPoint(0.75, 0), false));
+		constr.push(new mxConnectionConstraint(new mxPoint(1, 0), false));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w, (h - s) * 0.5));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w, h - s));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx2, h));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, h - s));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, (h - s) * 0.5));
+		
+		if (w >= s * 2)
+		{
+			constr.push(new mxConnectionConstraint(new mxPoint(0.5, 0), false));
+		}
+
+		return (constr);
+	};
+	
 	mxRectangleShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0.25, 0), true),
 	                                          new mxConnectionConstraint(new mxPoint(0.5, 0), true),
 	                                          new mxConnectionConstraint(new mxPoint(0.75, 0), true),
@@ -3847,9 +3893,76 @@
 	mxImageShape.prototype.constraints = mxRectangleShape.prototype.constraints;
 	mxSwimlane.prototype.constraints = mxRectangleShape.prototype.constraints;
 	PlusShape.prototype.constraints = mxRectangleShape.prototype.constraints;
-	NoteShape.prototype.constraints = mxRectangleShape.prototype.constraints;
-	CardShape.prototype.constraints = mxRectangleShape.prototype.constraints;
-	CubeShape.prototype.constraints = mxRectangleShape.prototype.constraints;
+
+	NoteShape.prototype.getConstraints = function(style, w, h)
+	{
+		var constr = [];
+		var s = Math.max(0, Math.min(w, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))));
+		
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w - s) * 0.5, 0));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - s, 0));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - s * 0.5, s * 0.5));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w, s));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w, (h + s) * 0.5 ));
+		constr.push(new mxConnectionConstraint(new mxPoint(1, 1), false));
+		constr.push(new mxConnectionConstraint(new mxPoint(0.5, 1), false));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 1), false));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0.5), false));
+		
+		if (w >= s * 2)
+		{
+			constr.push(new mxConnectionConstraint(new mxPoint(0.5, 0), false));
+		}
+
+		return (constr);
+	};
+	
+	CardShape.prototype.getConstraints = function(style, w, h)
+	{
+		var constr = [];
+		var s = Math.max(0, Math.min(w, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))));
+		
+		constr.push(new mxConnectionConstraint(new mxPoint(1, 0), false));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w + s) * 0.5, 0));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, s, 0));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, s * 0.5, s * 0.5));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, s));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, (h + s) * 0.5 ));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 1), false));
+		constr.push(new mxConnectionConstraint(new mxPoint(0.5, 1), false));
+		constr.push(new mxConnectionConstraint(new mxPoint(1, 1), false));
+		constr.push(new mxConnectionConstraint(new mxPoint(1, 0.5), false));
+		
+		if (w >= s * 2)
+		{
+			constr.push(new mxConnectionConstraint(new mxPoint(0.5, 0), false));
+		}
+
+		return (constr);
+	};
+	
+	CubeShape.prototype.getConstraints = function(style, w, h)
+	{
+		var constr = [];
+		var s = Math.max(0, Math.min(w, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))));
+		
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w - s) * 0.5, 0));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - s, 0));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - s * 0.5, s * 0.5));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w, s));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w, (h + s) * 0.5));
+		constr.push(new mxConnectionConstraint(new mxPoint(1, 1), false));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w + s) * 0.5, h));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, s, h));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, s * 0.5, h - s * 0.5));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, h - s));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, (h - s) * 0.5));
+		
+		return (constr);
+	};
+	
 	FolderShape.prototype.constraints = mxRectangleShape.prototype.constraints;
 	InternalStorageShape.prototype.constraints = mxRectangleShape.prototype.constraints;
 	DataStorageShape.prototype.constraints = mxRectangleShape.prototype.constraints;
@@ -3859,7 +3972,25 @@
 	LineEllipseShape.prototype.constraints = mxEllipse.prototype.constraints;
 	ManualInputShape.prototype.constraints = mxRectangleShape.prototype.constraints;
 	DelayShape.prototype.constraints = mxRectangleShape.prototype.constraints;
-	DisplayShape.prototype.constraints = mxRectangleShape.prototype.constraints;
+
+	DisplayShape.prototype.getConstraints = function(style, w, h)
+	{
+		var constr = [];
+		var dx = Math.min(w, h / 2);
+		var s = Math.min(w - dx, Math.max(0, parseFloat(mxUtils.getValue(this.style, 'size', this.size))) * w);
+		
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0.5), false, null));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, s, 0));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (s + w - dx) * 0.5, 0));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, 0));
+		constr.push(new mxConnectionConstraint(new mxPoint(1, 0.5), false, null));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, h));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (s + w - dx) * 0.5, h));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, s, h));
+		
+		return (constr);
+	};
+	
 	LoopLimitShape.prototype.constraints = mxRectangleShape.prototype.constraints;
 	OffPageConnectorShape.prototype.constraints = mxRectangleShape.prototype.constraints;
 	mxCylinder.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0.15, 0.05), false),
@@ -3981,8 +4112,56 @@
 	        	            		 new mxConnectionConstraint(new mxPoint(1, 0.5), true),
 	        	            		 new mxConnectionConstraint(new mxPoint(1, 0.75), true)];
 	mxArrow.prototype.constraints = null;
-	TeeShape.prototype.constraints = null;
-	CornerShape.prototype.constraints = null;
+
+	TeeShape.prototype.getConstraints = function(style, w, h)
+	{
+		var constr = [];
+		var dx = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx', this.dx))));
+		var dy = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'dy', this.dy))));
+		var w2 = Math.abs(w - dx) / 2;
+		
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false));
+		constr.push(new mxConnectionConstraint(new mxPoint(0.5, 0), false));
+		constr.push(new mxConnectionConstraint(new mxPoint(1, 0), false));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w, dy * 0.5));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w, dy));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.75 + dx * 0.25, dy));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w + dx) * 0.5, dy));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w + dx) * 0.5, (h + dy) * 0.5));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w + dx) * 0.5, h));
+		constr.push(new mxConnectionConstraint(new mxPoint(0.5, 1), false));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w - dx) * 0.5, h));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w - dx) * 0.5, (h + dy) * 0.5));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w - dx) * 0.5, dy));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.25 - dx * 0.25, dy));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, dy));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, dy * 0.5));
+		
+		return (constr);
+	};
+
+	CornerShape.prototype.getConstraints = function(style, w, h)
+	{
+		var constr = [];
+		var dx = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx', this.dx))));
+		var dy = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'dy', this.dy))));
+		
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false));
+		constr.push(new mxConnectionConstraint(new mxPoint(0.5, 0), false));
+		constr.push(new mxConnectionConstraint(new mxPoint(1, 0), false));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w, dy * 0.5));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w, dy));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w + dx) * 0.5, dy));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx, dy));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx, (h + dy) * 0.5));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx, h));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx * 0.5, h));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0.5), false));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 1), false));
+		
+		return (constr);
+	};
+
 	CrossbarShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0, 0), false),
         new mxConnectionConstraint(new mxPoint(0, 0.5), false),
         new mxConnectionConstraint(new mxPoint(0, 1), false),
@@ -3993,14 +4172,84 @@
         new mxConnectionConstraint(new mxPoint(1, 0.5), false),
         new mxConnectionConstraint(new mxPoint(1, 1), false)];
 
-	SingleArrowShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0, 0.5), false),
-	                                    new mxConnectionConstraint(new mxPoint(1, 0.5), false)];
-	DoubleArrowShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0, 0.5), false),
-	  	                                    new mxConnectionConstraint(new mxPoint(1, 0.5), false)];
-	CrossShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0, 0.5), false),
-	                                    new mxConnectionConstraint(new mxPoint(1, 0.5), false),
-	                                    new mxConnectionConstraint(new mxPoint(0.5, 0), false),
-	                                    new mxConnectionConstraint(new mxPoint(0.5, 1), false)];
+	SingleArrowShape.prototype.getConstraints = function(style, w, h)
+	{
+		var constr = [];
+		var aw = h * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'arrowWidth', this.arrowWidth))));
+		var as = w * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'arrowSize', this.arrowSize))));
+		var at = (h - aw) / 2;
+		var ab = at + aw;
+		
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0.5), false));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, at));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w - as) * 0.5, at));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - as, 0));
+		constr.push(new mxConnectionConstraint(new mxPoint(1, 0.5), false));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - as, h));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w - as) * 0.5, h - at));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, h - at));
+		
+		return (constr);
+	};
+	
+	DoubleArrowShape.prototype.getConstraints = function(style, w, h)
+	{
+		var constr = [];
+		var aw = h * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'arrowWidth', SingleArrowShape.prototype.arrowWidth))));
+		var as = w * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'arrowSize', SingleArrowShape.prototype.arrowSize))));
+		var at = (h - aw) / 2;
+		var ab = at + aw;
+		
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0.5), false));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, as, 0));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.5, at));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - as, 0));
+		constr.push(new mxConnectionConstraint(new mxPoint(1, 0.5), false));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - as, h));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.5, h - at));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, as, h));
+		
+		return (constr);
+	};
+	
+	CrossShape.prototype.getConstraints = function(style, w, h)
+	{
+		var constr = [];
+		var m = Math.min(h, w);
+		var size = Math.max(0, Math.min(m, m * parseFloat(mxUtils.getValue(this.style, 'size', this.size))));
+		var t = (h - size) / 2;
+		var b = t + size;
+		var l = (w - size) / 2;
+		var r = l + size;
+		
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, l, t * 0.5));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, l, 0));
+		constr.push(new mxConnectionConstraint(new mxPoint(0.5, 0), false));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, r, 0));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, r, t * 0.5));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, r, t));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, l, h - t * 0.5));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, l, h));
+		constr.push(new mxConnectionConstraint(new mxPoint(0.5, 1), false));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, r, h));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, r, h - t * 0.5));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, r, b));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w + r) * 0.5, t));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w, t));
+		constr.push(new mxConnectionConstraint(new mxPoint(1, 0.5), false));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w, b));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w + r) * 0.5, b));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, l, b));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, l * 0.5, t));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, t));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0.5), false));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, b));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, l * 0.5, b));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, l, t));
+
+		return (constr);
+	};
+	
 	UmlLifeline.prototype.constraints = null;
 	OrShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0, 0.25), false),
 	  	                             new mxConnectionConstraint(new mxPoint(0, 0.5), false),

File diff suppressed because it is too large
+ 133 - 114
src/main/webapp/js/reader.min.js


File diff suppressed because it is too large
+ 73 - 17
src/main/webapp/js/shapes.min.js


File diff suppressed because it is too large
+ 632 - 612
src/main/webapp/js/viewer.min.js


+ 503 - 12
src/main/webapp/shapes/mxArrows.js

@@ -124,8 +124,6 @@ mxShapeArrows2Arrow.prototype.getLabelBounds = function(rect)
 
 mxCellRenderer.registerShape(mxShapeArrows2Arrow.prototype.cst.ARROW, mxShapeArrows2Arrow);
 
-mxShapeArrows2Arrow.prototype.constraints = null;
-
 Graph.handleFactory[mxShapeArrows2Arrow.prototype.cst.ARROW] = function(state)
 {
 	var handles = [Graph.createHandle(state, ['dx', 'dy'], function(bounds)
@@ -156,6 +154,27 @@ Graph.handleFactory[mxShapeArrows2Arrow.prototype.cst.ARROW] = function(state)
 
 }
 
+mxShapeArrows2Arrow.prototype.getConstraints = function(style, w, h)
+{
+	var constr = [];
+	var dy = h * 0.5 * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'dy', this.dy))));
+	var dx = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx', this.dx))));
+	var notch = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'notch', this.notch))));
+
+	constr.push(new mxConnectionConstraint(new mxPoint(1, 0.5), false));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0.5), false, null, notch, 0));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, h - dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, 0));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, h - dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, h));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w - dx) * 0.5, dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w - dx) * 0.5, h - dy));
+
+	return (constr);
+}
+
 //**********************************************************************************************************************************************************
 //Two Way Arrow
 //**********************************************************************************************************************************************************
@@ -271,6 +290,28 @@ Graph.handleFactory[mxShapeArrows2TwoWayArrow.prototype.cst.TWO_WAY_ARROW] = fun
 
 }
 
+mxShapeArrows2TwoWayArrow.prototype.getConstraints = function(style, w, h)
+{
+	var constr = [];
+	var dy = h * 0.5 * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'dy', this.dy))));
+	var dx = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx', this.dx))));
+
+	constr.push(new mxConnectionConstraint(new mxPoint(1, 0.5), false));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx, 0));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx, dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx, h));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx, h - dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0.5), false));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, 0));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, h));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, h - dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0.5, 0), false, null, 0, dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0.5, 0), false, null, 0, h - dy));
+
+	return (constr);
+}
+
 //**********************************************************************************************************************************************************
 //Stylised Arrow
 //**********************************************************************************************************************************************************
@@ -379,6 +420,28 @@ Graph.handleFactory[mxShapeArrows2StylisedArrow.prototype.cst.STYLISED_ARROW] =
 
 }
 
+mxShapeArrows2StylisedArrow.prototype.getConstraints = function(style, w, h)
+{
+	var constr = [];
+	var dy = h * 0.5 * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'dy', this.dy))));
+	var dx = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx', this.dx))));
+	var notch = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'notch', this.notch))));
+	var feather = h * 0.5 * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'feather', this.feather))));
+
+	constr.push(new mxConnectionConstraint(new mxPoint(1, 0.5), false));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0.5), false, null, notch, 0));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, feather));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, h - feather));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx - 10, 0));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, h - dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx - 10, h));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w - dx) * 0.5, (dy + feather) * 0.5));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w - dx) * 0.5, h - (dy + feather) * 0.5));
+
+	return (constr);
+}
+
 //**********************************************************************************************************************************************************
 //Sharp Arrow
 //**********************************************************************************************************************************************************
@@ -429,7 +492,6 @@ mxShapeArrows2SharpArrow.prototype.paintVertexShape = function(c, x, y, w, h)
 	var notch = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'notch', this.notch))));
 	var dx1a = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx1', this.dx1))));
 	var dy1a = h * 0.5 * Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'dy1', this.dy1))));
-
 	var x2 = 0;
 	
 	if (h != 0)
@@ -498,6 +560,38 @@ Graph.handleFactory[mxShapeArrows2SharpArrow.prototype.cst.SHARP_ARROW] = functi
 
 }
 
+mxShapeArrows2SharpArrow.prototype.getConstraints = function(style, w, h)
+{
+	var constr = [];
+	var dy1 = h * 0.5 * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'dy1', this.dy1))));
+	var dx1 = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx1', this.dx1))));
+	var dx2 = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx2', this.dx2))));
+	var notch = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'notch', this.notch))));
+	var dx1a = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx1', this.dx1))));
+	var dy1a = h * 0.5 * Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'dy1', this.dy1))));
+	var x2 = 0;
+	
+	if (h != 0)
+	{
+		x2 = dx1a + dx2 * dy1a * 2 / h;
+	}
+	
+	constr.push(new mxConnectionConstraint(new mxPoint(1, 0.5), false));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0.5), false, null, notch, 0));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, dy1));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx1, dy1));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - x2, 0));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx2, 0));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, h - dy1));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx1, h - dy1));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - x2, h));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx2, h));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w - dx1) * 0.5, dy1));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w - dx1) * 0.5, h - dy1));
+
+	return (constr);
+}
+
 //**********************************************************************************************************************************************************
 //Sharp Arrow2
 //**********************************************************************************************************************************************************
@@ -549,14 +643,10 @@ mxShapeArrows2SharpArrow2.prototype.paintVertexShape = function(c, x, y, w, h)
 
 	var dy1 = h * 0.5 * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'dy1', this.dy1))));
 	var dx1 = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx1', this.dx1))));
-
 	var dx2 = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx2', this.dx2))));
-	
 	var dy3 = h * 0.5 * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'dy3', this.dy3))));
 	var dx3 = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx3', this.dx3))));
-	
 	var notch = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'notch', this.notch))));
-
 	var dx1a = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx1', this.dx1))));
 	var dy1a = h * 0.5 * Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'dy1', this.dy1))));
 
@@ -634,6 +724,34 @@ Graph.handleFactory[mxShapeArrows2SharpArrow2.prototype.cst.SHARP_ARROW2] = func
 	return handles;
 }
 
+mxShapeArrows2SharpArrow2.prototype.getConstraints = function(style, w, h)
+{
+	var constr = [];
+	var dy1 = h * 0.5 * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'dy1', this.dy1))));
+	var dx1 = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx1', this.dx1))));
+	var dx2 = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx2', this.dx2))));
+	var dy3 = h * 0.5 * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'dy3', this.dy3))));
+	var dx3 = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx3', this.dx3))));
+	var notch = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'notch', this.notch))));
+	var dx1a = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx1', this.dx1))));
+	var dy1a = h * 0.5 * Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'dy1', this.dy1))));
+
+	constr.push(new mxConnectionConstraint(new mxPoint(1, 0.5), false));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0.5), false, null, notch, 0));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, dy1));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx1, dy1));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx3, dy3));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx2, 0));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, h - dy1));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx1, h - dy1));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx3, h - dy3));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx2, h));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w - dx1) * 0.5, dy1));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w - dx1) * 0.5, h - dy1));
+
+	return (constr);
+}
+
 //**********************************************************************************************************************************************************
 //Callout Arrow
 //**********************************************************************************************************************************************************
@@ -748,6 +866,33 @@ Graph.handleFactory[mxShapeArrows2CalloutArrow.prototype.cst.CALLOUT_ARROW] = fu
 	return handles;
 }
 
+mxShapeArrows2CalloutArrow.prototype.getConstraints = function(style, w, h)
+{
+	var constr = [];
+	var dy = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'dy', this.dy))));
+	var dx = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx', this.dx))));
+	var notch = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'notch', this.notch))));
+	var arrowHead = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'arrowHead', this.arrowHead))));
+
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, notch, 0));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null,notch, h * 0.5 - dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, h * 0.5 - dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, h * 0.5 - dy - arrowHead));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w, h * 0.5));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, h * 0.5 + dy + arrowHead));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, h * 0.5 + dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, notch, h * 0.5 + dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, notch, h));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, h));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, notch * 0.5 , 0));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, notch * 0.5 , h));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0.5), false, null, (notch + w - dx) * 0.5, -dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0.5), false, null, (notch + w - dx) * 0.5, dy));
+
+	return (constr);
+}
+
 //**********************************************************************************************************************************************************
 //Bend Arrow
 //**********************************************************************************************************************************************************
@@ -804,6 +949,7 @@ mxShapeArrows2BendArrow.prototype.paintVertexShape = function(c, x, y, w, h)
 	c.lineTo(w, arrowHead * 0.5);
 	c.lineTo(w - dx, arrowHead);
 	c.lineTo(w - dx, arrowHead / 2 + dy);
+	
 	if (rounded == '1')
 	{
 		c.lineTo(dy * 2.2, arrowHead / 2 + dy);
@@ -884,6 +1030,42 @@ Graph.handleFactory[mxShapeArrows2BendArrow.prototype.cst.BEND_ARROW] = function
 	return handles;
 }
 
+mxShapeArrows2BendArrow.prototype.getConstraints = function(style, w, h)
+{
+	var constr = [];
+	var dy = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'dy', this.dy))));
+	var dx = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx', this.dx))));
+	var notch = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'notch', this.notch))));
+	var arrowHead = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'arrowHead', this.arrowHead))));
+	var rounded = mxUtils.getValue(this.style, 'rounded', '0');
+
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w - dx + dy * 2) * 0.5, arrowHead / 2 - dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, arrowHead / 2 - dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, 0));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w, arrowHead * 0.5));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, arrowHead));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, arrowHead / 2 + dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w - dx + dy * 2) * 0.5, arrowHead / 2 + dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dy * 2, (h - arrowHead / 2 - dy) * 0.5 + arrowHead / 2 + dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dy * 2, h));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dy, h - notch));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, h));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, (h - arrowHead / 2 - dy) * 0.5 + arrowHead / 2 + dy));
+
+	if (rounded == '1')
+	{
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dy * 0.586, arrowHead / 2 - dy * 0.414));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 2 * dy  + dy * 0.0586, arrowHead / 2 + dy + dy * 0.0586));
+	}
+	else
+	{
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, arrowHead / 2 - dy));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dy * 2, arrowHead / 2 + dy));
+	}
+	
+	return (constr);
+}
+
 //**********************************************************************************************************************************************************
 //Bend Double Arrow
 //**********************************************************************************************************************************************************
@@ -938,6 +1120,7 @@ mxShapeArrows2BendDoubleArrow.prototype.paintVertexShape = function(c, x, y, w,
 	c.lineTo(w, arrowHead * 0.5);
 	c.lineTo(w - dx, arrowHead);
 	c.lineTo(w - dx, arrowHead / 2 + dy);
+
 	if (rounded == '1')
 	{
 		c.lineTo(arrowHead / 2 + dy * 1.2, arrowHead / 2 + dy);
@@ -1006,6 +1189,42 @@ Graph.handleFactory[mxShapeArrows2BendDoubleArrow.prototype.cst.BEND_DOUBLE_ARRO
 
 }
 
+mxShapeArrows2BendDoubleArrow.prototype.getConstraints = function(style, w, h)
+{
+	var constr = [];
+	var dy = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'dy', this.dy))));
+	var dx = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx', this.dx))));
+	var arrowHead = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'arrowHead', this.arrowHead))));
+	var rounded = mxUtils.getValue(this.style, 'rounded', '0');
+
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx , 0));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w, arrowHead * 0.5));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, arrowHead));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, arrowHead / 2 + dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (arrowHead / 2 + dy + w - dx) * 0.5, arrowHead / 2 + dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (arrowHead / 2 + dy + w - dx) * 0.5, arrowHead / 2 - dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, arrowHead / 2 + dy, (arrowHead / 2 + dy + h - dx) * 0.5));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, arrowHead / 2 - dy, (arrowHead / 2 + dy + h - dx) * 0.5));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, arrowHead / 2 + dy, h - dx));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, arrowHead, h - dx));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, arrowHead / 2, h));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, h - dx));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, arrowHead / 2 - dy, h - dx));
+
+	if (rounded == '1')
+	{
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, arrowHead / 2 - dy * 0.414, arrowHead / 2 - dy * 0.414));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, arrowHead / 2 + dy + dy * 0.0586, arrowHead / 2 + dy + dy * 0.0586));
+	}
+	else
+	{
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, arrowHead / 2 - dy, arrowHead / 2 - dy));
+		constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, arrowHead / 2 + dy, arrowHead / 2 + dy));
+	}
+	
+	return (constr);
+}
+
 //**********************************************************************************************************************************************************
 //Callout Double Arrow
 //**********************************************************************************************************************************************************
@@ -1127,6 +1346,34 @@ Graph.handleFactory[mxShapeArrows2CalloutDoubleArrow.prototype.cst.CALLOUT_DOUBL
 	return handles;
 }
 
+mxShapeArrows2CalloutDoubleArrow.prototype.getConstraints = function(style, w, h)
+{
+	var constr = [];
+	var dy = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'dy', this.dy))));
+	var dx = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx', this.dx))));
+	var notch = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'notch', this.notch))));
+	var arrowHead = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'arrowHead', this.arrowHead))));
+
+	constr.push(new mxConnectionConstraint(new mxPoint(0.5, 0), false));
+	constr.push(new mxConnectionConstraint(new mxPoint(0.5, 1), false));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0.5), false));
+	constr.push(new mxConnectionConstraint(new mxPoint(1, 0.5), false));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w / 2 - notch, 0));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w / 2 + notch, 0));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 1), false, null, w / 2 - notch, 0));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 1), false, null, w / 2 + notch, 0));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, h * 0.5 - dy - arrowHead));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, h * 0.5 + dy + arrowHead));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx, h * 0.5 - dy - arrowHead));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx, h * 0.5 + dy + arrowHead));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w * 1.5 - dx + notch) * 0.5, h * 0.5 - dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w * 1.5 - dx + notch) * 0.5, h * 0.5 + dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w * 0.5 + dx - notch) * 0.5, h * 0.5 - dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w * 0.5 + dx - notch) * 0.5, h * 0.5 + dy));
+	
+	return (constr);
+}
+
 //**********************************************************************************************************************************************************
 //Callout Quad Arrow
 //**********************************************************************************************************************************************************
@@ -1262,6 +1509,50 @@ Graph.handleFactory[mxShapeArrows2CalloutQuadArrow.prototype.cst.CALLOUT_QUAD_AR
 	return handles;
 }
 
+mxShapeArrows2CalloutQuadArrow.prototype.getConstraints = function(style, w, h)
+{
+	var constr = [];
+	var dy = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'dy', this.dy))));
+	var dx = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx', this.dx))));
+	var notch = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'notch', this.notch))));
+	var arrowHead = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'arrowHead', this.arrowHead))));
+
+	constr.push(new mxConnectionConstraint(new mxPoint(0.5, 0), false));
+	constr.push(new mxConnectionConstraint(new mxPoint(0.5, 1), false));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0.5), false));
+	constr.push(new mxConnectionConstraint(new mxPoint(1, 0.5), false));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.5 + dy, h * 0.5 - notch));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.5 + notch, h * 0.5 - notch));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.5 + notch, h * 0.5 - dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.5 + dy, h * 0.5 + notch));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.5 + notch, h * 0.5 + notch));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.5 + notch, h * 0.5 + dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.5 - dy, h * 0.5 + notch));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.5 - notch, h * 0.5 + notch));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.5 - notch, h * 0.5 + dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.5 - dy, h * 0.5 - notch));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.5 - notch, h * 0.5 - notch));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.5 - notch, h * 0.5 - dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, h * 0.5 - dy - arrowHead));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, h * 0.5 + dy + arrowHead));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.5 - dy - arrowHead, h - dx));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.5 + dy + arrowHead, h - dx));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx, h * 0.5 - dy - arrowHead));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx, h * 0.5 + dy + arrowHead));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.5 - dy - arrowHead, dx));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.5 + dy + arrowHead, dx));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.75 + (notch - dx) * 0.5, h * 0.5 - dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.75 + (notch - dx) * 0.5, h * 0.5 + dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.5 - dy, h * 0.75 + (notch - dx) * 0.5));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.5 + dy, h * 0.75 + (notch - dx) * 0.5));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.25 - (notch - dx) * 0.5, h * 0.5 - dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.25 - (notch - dx) * 0.5, h * 0.5 + dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.5 - dy, h * 0.25 - (notch - dx) * 0.5));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.5 + dy, h * 0.25 - (notch - dx) * 0.5));
+	
+	return (constr);
+}
+
 //**********************************************************************************************************************************************************
 //Callout Double 90 Arrow
 //**********************************************************************************************************************************************************
@@ -1388,6 +1679,34 @@ Graph.handleFactory[mxShapeArrows2CalloutDouble90Arrow.prototype.cst.CALLOUT_DOU
 	return handles;
 }
 
+mxShapeArrows2CalloutDouble90Arrow.prototype.getConstraints = function(style, w, h)
+{
+	var constr = [];
+	var dy1 = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'dy1', this.dy1))));
+	var dx1 = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx1', this.dx1))));
+	var dx2 = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx2', this.dx2))));
+	var dy2 = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dy2', this.dy2))));
+	var arrowHead = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'arrowHead', this.arrowHead))));
+
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx2 * 0.5, 0));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx2, 0));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w - dx1 + dx2) * 0.5, dy2 * 0.5 - dy1));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx1, dy2 * 0.5 - dy1 - arrowHead));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w, dy2 * 0.5));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx1, dy2 * 0.5 + dy1 + arrowHead));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w - dx1 + dx2) * 0.5, dy2 * 0.5 + dy1));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx2, dy2));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx2 * 0.5 + dy1, (h - dx1 + dy2) * 0.5));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx2 * 0.5 - dy1, (h - dx1 + dy2) * 0.5));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx2 / 2 + dy1 + arrowHead, h - dx1));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx2 / 2, h));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx2 / 2 - dy1 - arrowHead, h - dx1));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, dy2));
+	
+	return (constr);
+}
+
 //**********************************************************************************************************************************************************
 //Quad Arrow
 //**********************************************************************************************************************************************************
@@ -1501,6 +1820,37 @@ Graph.handleFactory[mxShapeArrows2QuadArrow.prototype.cst.QUAD_ARROW] = function
 	return handles;
 }
 
+mxShapeArrows2QuadArrow.prototype.getConstraints = function(style, w, h)
+{
+	var constr = [];
+	var dy = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'dy', this.dy))));
+	var dx = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx', this.dx))));
+	var arrowHead = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'arrowHead', this.arrowHead))));
+
+	constr.push(new mxConnectionConstraint(new mxPoint(0.5, 0), false));
+	constr.push(new mxConnectionConstraint(new mxPoint(0.5, 1), false));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0.5), false));
+	constr.push(new mxConnectionConstraint(new mxPoint(1, 0.5), false));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, h * 0.5 - dy - arrowHead));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, h * 0.5 + dy + arrowHead));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx, h * 0.5 - dy - arrowHead));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx, h * 0.5 + dy + arrowHead));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.5 - dy - arrowHead, dx));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.5 + dy + arrowHead, dx));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.5 - dy - arrowHead, h - dx));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.5 + dy + arrowHead, h - dx));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.5 - dy, (dx - dy) * 0.5 + h * 0.25));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.5 + dy, (dx - dy) * 0.5 + h * 0.25));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.5 - dy, (dy - dx) * 0.5 + h * 0.75));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.5 + dy, (dy - dx) * 0.5 + h * 0.75));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (dx - dy) * 0.5 + w * 0.25, h * 0.5 - dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (dx - dy) * 0.5 + w * 0.25, h * 0.5 + dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (dy - dx) * 0.5 + w * 0.75, h * 0.5 - dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (dy - dx) * 0.5 + w * 0.75, h * 0.5 + dy));
+	
+	return (constr);
+}
+
 //**********************************************************************************************************************************************************
 //Triad Arrow
 //**********************************************************************************************************************************************************
@@ -1606,6 +1956,33 @@ Graph.handleFactory[mxShapeArrows2TriadArrow.prototype.cst.TRIAD_ARROW] = functi
 	return handles;
 }
 
+mxShapeArrows2TriadArrow.prototype.getConstraints = function(style, w, h)
+{
+	var constr = [];
+	var dy = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'dy', this.dy))));
+	var dx = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx', this.dx))));
+	var arrowHead = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'arrowHead', this.arrowHead))));
+
+	constr.push(new mxConnectionConstraint(new mxPoint(0.5, 0), false, null, - arrowHead * 0.5, dx));
+	constr.push(new mxConnectionConstraint(new mxPoint(0.5, 0), false));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.5, h - dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0.5, 0), false, null, arrowHead * 0.5, dx));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, h - arrowHead));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w, h - arrowHead * 0.5));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, h));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx, h - arrowHead));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, h - arrowHead * 0.5));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx, h));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w * 1.5 - dx + arrowHead * 0.5 - dy) * 0.5, h - arrowHead + dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w * 1.5 - dx + arrowHead * 0.5 - dy) * 0.5, h - dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w * 0.5 + dx - arrowHead * 0.5 + dy) * 0.5, h - arrowHead + dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w * 0.5 + dx - arrowHead * 0.5 + dy) * 0.5, h - dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.5 - arrowHead * 0.5 + dy, (dx + h - arrowHead + dy) * 0.5));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w * 0.5 + arrowHead * 0.5 - dy, (dx + h - arrowHead + dy) * 0.5));
+	
+	return (constr);
+}
+
 //**********************************************************************************************************************************************************
 //Tailed Arrow
 //**********************************************************************************************************************************************************
@@ -1658,7 +2035,6 @@ mxShapeArrows2TailedArrow.prototype.paintVertexShape = function(c, x, y, w, h)
 	var dx2 = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx2', this.dx2))));
 	var notch = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'notch', this.notch))));
 	var arrowHead = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'arrowHead', this.arrowHead))));
-
 	var x2 = 0;
 	
 	if (dy2 != 0)
@@ -1747,6 +2123,36 @@ Graph.handleFactory[mxShapeArrows2TailedArrow.prototype.cst.TAILED_ARROW] = func
 	return handles;
 }
 
+mxShapeArrows2TailedArrow.prototype.getConstraints = function(style, w, h)
+{
+	var constr = [];
+	var dy1 = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'dy1', this.dy1))));
+	var dx1 = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx1', this.dx1))));
+	var dy2 = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'dy2', this.dy2))));
+	var dx2 = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx2', this.dx2))));
+	var notch = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'notch', this.notch))));
+	var arrowHead = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'arrowHead', this.arrowHead))));
+	var x2 = 0;
+	
+	if (dy2 != 0)
+	{
+		x2 = dx2 + dy2 * (dy2 - dy1) / dy2;
+	}
+
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0.5), false, null, notch, 0));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, h * 0.5 - dy2));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx2, h * 0.5 - dy2));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w - dx1 + x2) * 0.5, h * 0.5 - dy1));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx1, h * 0.5 - dy1 - arrowHead));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, h * 0.5 + dy2));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx2, h * 0.5 + dy2));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w - dx1 + x2) * 0.5, h * 0.5 + dy1));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx1, h * 0.5 + dy1 + arrowHead));
+	constr.push(new mxConnectionConstraint(new mxPoint(1, 0.5), false));
+	
+	return (constr);
+}
+
 //**********************************************************************************************************************************************************
 //Tailed Arrow with Notch
 //**********************************************************************************************************************************************************
@@ -1799,7 +2205,6 @@ mxShapeArrows2TailedNotchedArrow.prototype.paintVertexShape = function(c, x, y,
 	var dx2 = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx2', this.dx2))));
 	var notch = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'notch', this.notch))));
 	var arrowHead = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'arrowHead', this.arrowHead))));
-
 	var x2 = 0;
 	
 	if (dy2 != 0)
@@ -1888,6 +2293,36 @@ Graph.handleFactory[mxShapeArrows2TailedNotchedArrow.prototype.cst.TAILED_NOTCHE
 	return handles;
 }
 
+mxShapeArrows2TailedNotchedArrow.prototype.getConstraints = function(style, w, h)
+{
+	var constr = [];
+	var dy1 = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'dy1', this.dy1))));
+	var dx1 = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx1', this.dx1))));
+	var dy2 = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'dy2', this.dy2))));
+	var dx2 = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx2', this.dx2))));
+	var notch = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'notch', this.notch))));
+	var arrowHead = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'arrowHead', this.arrowHead))));
+	var x2 = 0;
+	
+	if (dy2 != 0)
+	{
+		x2 = dx2 + notch * (dy2 - dy1) / dy2;
+	}
+
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0.5), false, null, notch, 0));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, h * 0.5 - dy2));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx2, h * 0.5 - dy2));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w - dx1 + x2) * 0.5, h * 0.5 - dy1));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx1, h * 0.5 - dy1 - arrowHead));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, h * 0.5 + dy2));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx2, h * 0.5 + dy2));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w - dx1 + x2) * 0.5, h * 0.5 + dy1));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx1, h * 0.5 + dy1 + arrowHead));
+	constr.push(new mxConnectionConstraint(new mxPoint(1, 0.5), false));
+	
+	return (constr);
+}
+
 //**********************************************************************************************************************************************************
 //Striped Arrow
 //**********************************************************************************************************************************************************
@@ -1954,7 +2389,6 @@ mxShapeArrows2StripedArrow.prototype.paintVertexShape = function(c, x, y, w, h)
 	c.lineTo(notch * 0.32, dy);
 	c.close();
 	c.fillAndStroke();
-	
 };
 
 mxCellRenderer.registerShape(mxShapeArrows2StripedArrow.prototype.cst.STRIPED_ARROW, mxShapeArrows2StripedArrow);
@@ -1991,6 +2425,27 @@ Graph.handleFactory[mxShapeArrows2StripedArrow.prototype.cst.STRIPED_ARROW] = fu
 
 }
 
+mxShapeArrows2StripedArrow.prototype.getConstraints = function(style, w, h)
+{
+	var constr = [];
+	var dy = h * 0.5 * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'dy', this.dy))));
+	var dx = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx', this.dx))));
+	var notch = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'notch', this.notch))));
+
+	constr.push(new mxConnectionConstraint(new mxPoint(1, 0.5), false));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0.5), false, null, 0, 0));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, h - dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, 0));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, h - dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, h));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w - dx) * 0.5, dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (w - dx) * 0.5, h - dy));
+	
+	return (constr);
+}
+
 //**********************************************************************************************************************************************************
 //Jump-In Arrow
 //**********************************************************************************************************************************************************
@@ -2084,6 +2539,21 @@ Graph.handleFactory[mxShapeArrows2JumpInArrow.prototype.cst.JUMP_IN_ARROW] = fun
 	return handles;
 }
 
+mxShapeArrows2JumpInArrow.prototype.getConstraints = function(style, w, h)
+{
+	var constr = [];
+	var dy = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'dy', this.dy))));
+	var dx = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx', this.dx))));
+	var arrowHead = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'arrowHead', this.arrowHead))));
+
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 1), false));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, 0));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w, arrowHead * 0.5));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, w - dx, arrowHead));
+	
+	return (constr);
+}
+
 //**********************************************************************************************************************************************************
 //U Turn Arrow
 //**********************************************************************************************************************************************************
@@ -2128,9 +2598,7 @@ mxShapeArrows2UTurnArrow.prototype.paintVertexShape = function(c, x, y, w, h)
 
 	var dy = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'dy', this.dy))));
 	var arrowHead = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'arrowHead', this.arrowHead))));
-
 	var dx = (h - arrowHead / 2 + dy) / 2;
-
 	var dx2 = Math.max(0, parseFloat(mxUtils.getValue(this.style, 'dx2', this.dx2)));
 	
 	c.begin();
@@ -2202,3 +2670,26 @@ Graph.handleFactory[mxShapeArrows2UTurnArrow.prototype.cst.U_TURN_ARROW] = funct
 	return handles;
 }
 
+mxShapeArrows2UTurnArrow.prototype.getConstraints = function(style, w, h)
+{
+	var constr = [];
+	var dy = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'dy', this.dy))));
+	var arrowHead = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'arrowHead', this.arrowHead))));
+	var dx = (h - arrowHead / 2 + dy) / 2;
+	var dx2 = Math.max(0, parseFloat(mxUtils.getValue(this.style, 'dx2', this.dx2)));
+	
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx, 0));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx + dx2, arrowHead * 0.5));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx, arrowHead));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (dx + w) * 0.5, h - 2 * dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, Math.max(w, dx), h - 2 * dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, Math.max(w, dx), h - dy));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, Math.max(w, dx), h));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, (dx + w) * 0.5, h));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, dx, h));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, 0, (h + arrowHead * 0.5 - dy) * 0.5));
+	constr.push(new mxConnectionConstraint(new mxPoint(0, 0), false, null, arrowHead - 2 * dy, (h + arrowHead * 0.5 - dy) * 0.5));
+	
+	return (constr);
+}
+