Browse Source

9.2.0 release

Former-commit-id: 54508737ab5c4aecb8a4a1093d0fe6534cfcb050
Gaudenz Alder 6 years ago
parent
commit
1d7a485f64
32 changed files with 6161 additions and 4168 deletions
  1. 8 0
      ChangeLog
  2. 1 1
      VERSION
  3. 1 1
      src/main/webapp/cache.manifest
  4. 16 0
      src/main/webapp/images/all-diagrams-sel.svg
  5. BIN
      src/main/webapp/images/broken.png
  6. 16 0
      src/main/webapp/images/list-sel.svg
  7. 1 1
      src/main/webapp/images/list.svg
  8. 16 0
      src/main/webapp/images/my-diagrams-sel.svg
  9. 1 1
      src/main/webapp/images/my-diagrams.svg
  10. 16 0
      src/main/webapp/images/tiles-sel.svg
  11. 1653 1591
      src/main/webapp/js/app.min.js
  12. 591 559
      src/main/webapp/js/atlas-viewer.min.js
  13. 1417 1355
      src/main/webapp/js/atlas.min.js
  14. 109 57
      src/main/webapp/js/diagramly/App.js
  15. 823 0
      src/main/webapp/js/diagramly/Dialogs.js
  16. 334 17
      src/main/webapp/js/diagramly/Editor.js
  17. 1 1
      src/main/webapp/js/diagramly/EditorUi.js
  18. 1 0
      src/main/webapp/js/diagramly/Init.js
  19. 51 0
      src/main/webapp/js/diagramly/Menus.js
  20. 3 2
      src/main/webapp/js/embed-static.min.js
  21. 2 2
      src/main/webapp/js/mxgraph/Editor.js
  22. 2 2
      src/main/webapp/js/mxgraph/EditorUi.js
  23. 10 17
      src/main/webapp/js/mxgraph/Graph.js
  24. 3 2
      src/main/webapp/js/reader.min.js
  25. 591 559
      src/main/webapp/js/viewer.min.js
  26. 0 0
      src/main/webapp/newDiagramCats/images/icon-flowchart.svg
  27. 0 0
      src/main/webapp/newDiagramCats/images/icon-mind-map.svg
  28. 0 0
      src/main/webapp/newDiagramCats/images/icon-network.svg
  29. 0 0
      src/main/webapp/newDiagramCats/images/icon-plus.svg
  30. 0 0
      src/main/webapp/newDiagramCats/images/icon-uml-erd.svg
  31. 9 0
      src/main/webapp/newDiagramCats/index.xml
  32. 485 0
      src/main/webapp/styles/grapheditor.css

+ 8 - 0
ChangeLog

@@ -1,3 +1,11 @@
+03-OCT-2018: 9.2.0
+
+- Configurable ui and plugins for Confluence Cloud 
+- Fixes fallback for public Google Drive URLs
+- Adds configure=1 URL parameter and protocol
+- Ctrl+Shift connect sets source constraint
+- Removes #C configuration switch
+
 21-SEP-2018: 9.1.8
 
 - Fixes minor constraint check bug for new connections

+ 1 - 1
VERSION

@@ -1 +1 @@
-9.1.8
+9.2.0

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

@@ -1,7 +1,7 @@
 CACHE MANIFEST
 
 # THIS FILE WAS GENERATED. DO NOT MODIFY!
-# 09/21/2018 04:42 PM
+# 10/03/2018 01:00 PM
 
 app.html
 index.html?offline=1

File diff suppressed because it is too large
+ 16 - 0
src/main/webapp/images/all-diagrams-sel.svg


BIN
src/main/webapp/images/broken.png


+ 16 - 0
src/main/webapp/images/list-sel.svg

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="13px" height="13px" viewBox="0 0 13 13" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: sketchtool 51.2 (57519) - http://www.bohemiancoding.com/sketch -->
+    <title>366530C7-A02E-42AC-9AE7-1CEE7BA971A5</title>
+    <desc>Created with sketchtool.</desc>
+    <defs></defs>
+    <g id="Artboards" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="V5-START-grid-veiw" transform="translate(-1158.000000, -468.000000)" fill="#555555">
+            <g id="recent-diagrams" transform="translate(428.000000, 442.000000)">
+                <g id="list-view" transform="translate(723.000000, 18.000000)">
+                    <path d="M7,16 L10,16 L10,19 L7,19 L7,16 Z M7,12 L10,12 L10,15 L7,15 L7,12 Z M7,8 L10,8 L10,11 L7,11 L7,8 Z M11,16 L20,16 L20,19 L11,19 L11,16 Z M11,12 L20,12 L20,15 L11,15 L11,12 Z M11,8 L20,8 L20,11 L11,11 L11,8 Z" id="Combined-Shape"></path>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 1 - 1
src/main/webapp/images/list.svg

@@ -5,7 +5,7 @@
     <desc>Created with sketchtool.</desc>
     <defs></defs>
     <g id="Artboards" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
-        <g id="V5-START-grid-veiw" transform="translate(-1158.000000, -468.000000)" fill="#555555">
+        <g id="V5-START-grid-veiw" transform="translate(-1158.000000, -468.000000)" fill="#FFFFFF">
             <g id="recent-diagrams" transform="translate(428.000000, 442.000000)">
                 <g id="list-view" transform="translate(723.000000, 18.000000)">
                     <path d="M7,16 L10,16 L10,19 L7,19 L7,16 Z M7,12 L10,12 L10,15 L7,15 L7,12 Z M7,8 L10,8 L10,11 L7,11 L7,8 Z M11,16 L20,16 L20,19 L11,19 L11,16 Z M11,12 L20,12 L20,15 L11,15 L11,12 Z M11,8 L20,8 L20,11 L11,11 L11,8 Z" id="Combined-Shape"></path>

+ 16 - 0
src/main/webapp/images/my-diagrams-sel.svg

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="9px" height="10px" viewBox="0 0 9 10" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: sketchtool 51.2 (57519) - http://www.bohemiancoding.com/sketch -->
+    <title>6D65F322-9A72-4FE2-AABE-E0CECDCF4413</title>
+    <desc>Created with sketchtool.</desc>
+    <defs></defs>
+    <g id="Artboards" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="V5-START-grid-veiw" transform="translate(-887.000000, -468.000000)" fill="#555555">
+            <g id="recent-diagrams" transform="translate(428.000000, 442.000000)">
+                <g id="my-diagrams" transform="translate(448.000000, 18.000000)">
+                    <path d="M15.5,13 C14.1192881,13 13,11.8807119 13,10.5 C13,9.11928813 14.1192881,8 15.5,8 C16.8807119,8 18,9.11928813 18,10.5 C18,11.8807119 16.8807119,13 15.5,13 Z M14,14 L17,14 C18.6568542,14 20,15.3431458 20,17 L20,18 L11,18 L11,17 C11,15.3431458 12.3431458,14 14,14 Z" id="Combined-Shape-Copy"></path>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 1 - 1
src/main/webapp/images/my-diagrams.svg

@@ -5,7 +5,7 @@
     <desc>Created with sketchtool.</desc>
     <defs></defs>
     <g id="Artboards" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
-        <g id="V5-START-grid-veiw" transform="translate(-887.000000, -468.000000)" fill="#555555">
+        <g id="V5-START-grid-veiw" transform="translate(-887.000000, -468.000000)" fill="#FFFFFF">
             <g id="recent-diagrams" transform="translate(428.000000, 442.000000)">
                 <g id="my-diagrams" transform="translate(448.000000, 18.000000)">
                     <path d="M15.5,13 C14.1192881,13 13,11.8807119 13,10.5 C13,9.11928813 14.1192881,8 15.5,8 C16.8807119,8 18,9.11928813 18,10.5 C18,11.8807119 16.8807119,13 15.5,13 Z M14,14 L17,14 C18.6568542,14 20,15.3431458 20,17 L20,18 L11,18 L11,17 C11,15.3431458 12.3431458,14 14,14 Z" id="Combined-Shape-Copy"></path>

+ 16 - 0
src/main/webapp/images/tiles-sel.svg

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="9px" height="9px" viewBox="0 0 9 9" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: sketchtool 51.2 (57519) - http://www.bohemiancoding.com/sketch -->
+    <title>E76CEF20-2955-44AF-B8E8-5DA928C955FE</title>
+    <desc>Created with sketchtool.</desc>
+    <defs></defs>
+    <g id="Artboards" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="V5-START-grid-veiw" transform="translate(-1134.000000, -469.000000)" fill="#555555">
+            <g id="recent-diagrams" transform="translate(428.000000, 442.000000)">
+                <g id="grid-view" transform="translate(697.000000, 18.000000)">
+                    <path d="M9,9 L13,9 L13,13 L9,13 L9,9 Z M9,14 L13,14 L13,18 L9,18 L9,14 Z M14,14 L18,14 L18,18 L14,18 L14,14 Z M14,9 L18,9 L18,13 L14,13 L14,9 Z" id="Combined-Shape"></path>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

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


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


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


+ 109 - 57
src/main/webapp/js/diagramly/App.js

@@ -438,30 +438,6 @@ App.main = function(callback, createUi)
 			}
 		};
 	}
-	
-	// Adds configuration
-	if (window.location.hash != null && window.location.hash.substring(0, 2) == '#C')
-	{
-		try
-		{
-			var config = JSON.parse(decodeURIComponent(
-					window.location.hash.substring(2)));
-			Editor.configure(config, true);
-			
-			if (config.open != null)
-			{
-				window.location.hash = config.open;
-			}
-			else
-			{
-				window.location.hash = '';
-			}
-		}
-		catch (e)
-		{
-			console.log(e);
-		}
-	}
 
 	if (window.mxscript != null)
 	{
@@ -698,11 +674,72 @@ App.main = function(callback, createUi)
 		});
 	};
 	
-	// Adds required resources (disables loading of fallback properties, this can only
-	// be used if we know that all keys are defined in the language specific file)
-	mxResources.loadDefaultBundle = false;
-	doLoad(mxResources.getDefaultBundle(RESOURCE_BASE, mxLanguage) ||
-		mxResources.getSpecialBundle(RESOURCE_BASE, mxLanguage));
+	function doConfigure(config)
+	{
+		try
+		{
+			var config = JSON.parse(decodeURIComponent(
+					window.location.hash.substring(2)));
+			Editor.configure(config, true);
+			
+			if (config.open != null)
+			{
+				window.location.hash = config.open;
+			}
+		}
+		catch (e)
+		{
+			console.log(e);
+		}
+	};
+	
+	function doMain()
+	{
+		// Adds required resources (disables loading of fallback properties, this can only
+		// be used if we know that all keys are defined in the language specific file)
+		mxResources.loadDefaultBundle = false;
+		doLoad(mxResources.getDefaultBundle(RESOURCE_BASE, mxLanguage) ||
+			mxResources.getSpecialBundle(RESOURCE_BASE, mxLanguage));
+	};
+
+	// Sends load event if configuration is requested and waits for configure action
+	if (urlParams['configure'] == '1')
+	{
+		var op = window.opener || window.parent;
+		
+		var configHandler = function(evt)
+		{
+			if (evt.source == op)
+			{
+				try
+				{
+					var data = JSON.parse(evt.data);
+					
+					if (data != null && data.action == 'configure')
+					{
+						mxEvent.removeListener(window, 'message', configHandler);					
+						Editor.configure(data.config, true);
+						doMain();
+					}
+				}
+				catch (e)
+				{
+					if (window.console != null)
+					{
+						console.log('Error in configuration: ' + e);
+					}
+				}
+			}
+		};
+		
+		// Receives XML message from opener and puts it into the graph
+		mxEvent.addListener(window, 'message', configHandler);
+		op.postMessage(JSON.stringify({event: 'load'}), '*');
+	}
+	else
+	{
+		doMain();
+	}
 };
 
 //Extends EditorUi
@@ -3490,6 +3527,42 @@ App.prototype.loadFile = function(id, sameWindow, file, success)
 			{
 				var url = decodeURIComponent(id.substring(1));
 				
+				var doFallback = mxUtils.bind(this, function()
+				{
+					// Fallback for non-public Google Drive diagrams
+					if (url.substring(0, 31) == 'https://drive.google.com/uc?id=' &&
+						(this.drive != null || typeof window.DriveClient === 'function'))
+					{
+						this.hideDialog();
+						
+						var fallback = mxUtils.bind(this, function()
+						{
+							if (this.drive != null)
+							{
+								this.spinner.stop();
+								this.loadFile('G' + url.substring(31, url.lastIndexOf('&ex')), sameWindow, success);
+								
+								return true;
+							}
+							else
+							{
+								return false;
+							}
+						});
+						
+						if (!fallback() && this.spinner.spin(document.body, mxResources.get('loading')))
+						{
+							this.addListener('clientLoaded', fallback);
+						}
+						
+						return true;
+					}
+					else
+					{
+						return false;
+					}
+				});
+				
 				this.loadTemplate(url, mxUtils.bind(this, function(text)
 				{
 					this.spinner.stop();
@@ -3532,38 +3605,17 @@ App.prototype.loadFile = function(id, sameWindow, file, success)
 						
 						if (!this.fileLoaded(tempFile))
 						{
-							// Fallback for non-public Google Drive diagrams
-							if (url.substring(0, 31) == 'https://drive.google.com/uc?id=' &&
-								(this.drive != null || typeof window.DriveClient === 'function'))
-							{
-								this.hideDialog();
-								
-								var fallback = mxUtils.bind(this, function()
-								{
-									if (this.drive != null)
-									{
-										this.spinner.stop();
-										this.loadFile('G' + url.substring(31, url.lastIndexOf('&')), sameWindow, success);
-										
-										return true;
-									}
-									else
-									{
-										return false;
-									}
-								});
-								
-								if (!fallback() && this.spinner.spin(document.body, mxResources.get('loading')))
-								{
-									this.addListener('clientLoaded', fallback);
-								}
-							}
+							doFallback();
 						}
 					}
 				}), mxUtils.bind(this, function()
 				{
-					this.spinner.stop();
-					this.handleError({message: mxResources.get('fileNotFound')}, mxResources.get('errorLoadingFile'));
+					if (!doFallback())
+					{
+						this.spinner.stop();
+						this.handleError({message: mxResources.get('fileNotFound')},
+							mxResources.get('errorLoadingFile'));
+					}
 				}));
 			}
 			else

+ 823 - 0
src/main/webapp/js/diagramly/Dialogs.js

@@ -8455,3 +8455,826 @@ var CustomDialog = function(editorUi, content, okFn, cancelFn, okButtonText, hel
 	this.okButton = okBtn;
 	this.container = div;
 };
+
+var TemplatesDialog = function()
+{
+	var dialogSkeleton = 
+	'<div class="tempDlgHeader">' + 
+		'<img src="/images/draw.io-logo.svg" class="headerLogo">' +
+		'<input type="search" class="searchBox" placeholder="'+ mxResources.get('search', null, 'Search') +'">' +
+	'</div>' +
+	'<div class="templatesList">' +
+		'<div class="newDiagramlbl">'+ mxResources.get('newDiagram', null, 'New Diagram') + '</div>' +
+		'<div class="hLine"></div>' +
+		'<div class="templatesLbl">'+ mxResources.get('templates', null, 'Templates') + '</div>' +
+	'</div>' +
+	'<div class="tempDlgContent">' +
+		'<div class="newDiagramCat">' +
+			'<div class="newDiagramCatLbl">'+ mxResources.get('newDiagram', null, 'New Diagram') +'</div>' +
+			'<div class="newDiagramCatList">' +
+			'</div>' + 
+			'<div class="newDiagramCatFooter">' + 
+				'<div class="showAllBtn">'+ mxResources.get('showAll', null, '+ Show all') +'</div>' +
+			'</div>' +
+		'</div>' + 
+		'<div class="diagramsList">' +
+			'<div class="diagramsListHeader">' +
+				'<div class="diagramsListTitle"></div>' +
+				'<div class="diagramsListBtns">' +
+					'<div class="radioBtn radioBtnLarge" data-id="myDiagramsBtn">' +
+						'<img src="/images/my-diagrams.svg" class="myDiagramsBtnImg"> <span>'+ mxResources.get('myDiagrams', null, 'My diagrams') + '</span>' +
+					'</div><div class="radioBtn radioBtnLarge radioBtnActive" data-id="allDiagramsBtn">' +
+						'<img src="/images/all-diagrams-sel.svg" class="allDiagramsBtnImg"> <span>'+ mxResources.get('allDiagrams', null, 'All diagrams') + '</span>' +
+					'</div><div class="spacer"> </div><div class="radioBtn radioBtnSmall radioBtnActive" data-id="tilesBtn">' +
+						'<img src="/images/tiles-sel.svg" class="tilesBtnImg">' +
+					'</div><div class="radioBtn radioBtnSmall" data-id="listBtn">' +
+						'<img src="/images/list.svg" class="listBtnImg">' +
+					'</div>' +
+				'</div>' +
+			'</div>' +
+			'<div class="diagramsTiles">' +
+			'</div>'+
+		'</div>' +
+	'</div>' +
+	'<br style="clear:both;"/>' +
+	'<div class="tempDlgFooter">' +
+		'<span class="linkToDiagram linkToDiagramHint">&#x1F6C8; ' + mxResources.get('linkToDiagramHint', null, 'Add a link to this diagram. The diagram can only be edited from the page that owns it.') + '</span>' +
+		'<button class="linkToDiagram linkToDiagramBtn">'+ mxResources.get('linkToDiagram', null, 'Link to Diagram') + '</button>' +
+		'<div class="createBtn">'+ mxResources.get('create', null, 'Create') + '</div>' +
+		'<div class="cancelBtn">'+ mxResources.get('cancel', null, 'Cancel') + '</div>' +
+	'</div>';
+	
+	var div = document.createElement('div');
+	div.innerHTML = dialogSkeleton;
+	div.className = "templateDlg";
+	//override default dialog size based on screen size if needed
+	var ww = window.innerWidth;
+	var wh = window.innerHeight;
+	var dw = 987, dh = 712;
+	
+	if (ww * 0.9 < dw)
+	{
+		dw = Math.max(ww * 0.9, 600);
+		div.style.width = dw + "px";
+	}
+
+	if (wh * 0.9 < dh)
+	{
+		dh = Math.max(wh * 0.9, 300);
+		div.style.height = dh + "px";
+	}
+
+	this.width = dw;
+	this.height = dh;
+	this.container = div;
+};
+
+TemplatesDialog.prototype.init = function(editorUi, callback, cancelCallback,
+		templateFile, newDiagramCatsFile, username, recentDocsCallback, searchDocsCallback,
+		openExtDocCallback, linkToDiagramCallback)
+{
+	templateFile = (templateFile != null) ? templateFile : TEMPLATE_PATH + '/index.xml';
+	newDiagramCatsFile = (newDiagramCatsFile != null) ? newDiagramCatsFile : NEW_DIAGRAM_CATS_PATH + '/index.xml';
+
+	var dlgDiv = this.container;
+	var callInitiated = false;
+	var cancelPendingCall = false;
+	var currentEntry = null;
+	var currentItem = null;
+	var currentItemInfo = null;
+	var showingAll = false;
+	var isGetAll = true;
+	var showAsList = false;
+	var curDiagList = [];
+	var lastSearchStr;
+	var categorySelected = true;
+	var showAllBtn = dlgDiv.querySelector(".showAllBtn");
+	var diagramsTiles = dlgDiv.querySelector('.diagramsTiles');
+	var diagramsListTitle = dlgDiv.querySelector('.diagramsListTitle');
+	var diagramsListBtns = dlgDiv.querySelector('.diagramsListBtns');
+	var tempDlgContent = dlgDiv.querySelector('.tempDlgContent');
+	var diagramsList = dlgDiv.querySelector('.diagramsList');
+	var newDiagramCat = dlgDiv.querySelector('.newDiagramCat');
+	var newDiagramCatList = dlgDiv.querySelector(".newDiagramCatList");
+	var createBtn = dlgDiv.querySelector('.createBtn');
+	var spinner = new Spinner({
+		lines: 12, // The number of lines to draw
+		length: 10, // The length of each line
+		width: 5, // The line thickness
+		radius: 10, // The radius of the inner circle
+		rotate: 0, // The rotation offset
+		color: '#000', // #rgb or #rrggbb
+		speed: 1.5, // Rounds per second
+		trail: 60, // Afterglow percentage
+		shadow: false, // Whether to render a shadow
+		hwaccel: false, // Whether to use hardware acceleration
+		top: '50px',
+		zIndex: 2e9 // The z-index (defaults to 2000000000)
+	});
+	
+	function deselectTempCat()
+	{
+		if (currentEntry != null)
+		{
+			currentEntry.style.fontWeight = 'normal';
+			currentEntry.style.textDecoration = 'none';
+			currentEntry = null;
+		}
+	};
+	
+	mxEvent.addListener(dlgDiv.querySelector('.newDiagramlbl'), 'click', function()
+	{
+		deselectTempCat();
+		newDiagramCat.style.display = '';
+		diagramsList.style.minHeight = 'calc(100% - 280px)';
+		getRecentDocs(isGetAll);
+	});
+	
+	function radioClick(btn, btnImgId, btnImgFile, otherBtnId, otherBtnImgId, otherBtnImgFile, isLarge)
+	{
+		if (btn.className.indexOf('radioBtnActive') > -1)
+		{
+			return false;
+		}
+		else
+		{
+			btn.className += ' radioBtnActive';
+			dlgDiv.querySelector('.radioBtn[data-id='+ otherBtnId +']').className = "radioBtn " + (isLarge? "radioBtnLarge" : "radioBtnSmall");
+			dlgDiv.querySelector('.'+ btnImgId).src = "/images/"+ btnImgFile +"-sel.svg";
+			dlgDiv.querySelector('.'+ otherBtnImgId).src = "/images/"+ otherBtnImgFile +".svg";
+			return true;
+		}
+	};
+	
+	mxEvent.addListener(dlgDiv.querySelector('.radioBtn[data-id=allDiagramsBtn]'), 'click', function()
+	{
+		if (radioClick(this, 'allDiagramsBtnImg', 'all-diagrams', 'myDiagramsBtn', 'myDiagramsBtnImg', 'my-diagrams', true))
+		{
+			isGetAll = true;
+			lastSearchStr == null? getRecentDocs(isGetAll) : doSearch(lastSearchStr);
+		}
+	});
+
+	mxEvent.addListener(dlgDiv.querySelector('.radioBtn[data-id=myDiagramsBtn]'), 'click', function()
+	{
+		if (radioClick(this, 'myDiagramsBtnImg', 'my-diagrams', 'allDiagramsBtn', 'allDiagramsBtnImg', 'all-diagrams', true))
+		{
+			isGetAll = false;
+			lastSearchStr == null? getRecentDocs(isGetAll) : doSearch(lastSearchStr);
+		}
+	});
+	
+	mxEvent.addListener(dlgDiv.querySelector('.radioBtn[data-id=listBtn]'), 'click', function()
+	{
+		if (radioClick(this, 'listBtnImg', 'list', 'tilesBtn', 'tilesBtnImg', 'tiles', false))
+		{
+			showAsList = true;
+			fillDiagramsList(curDiagList, false, showAsList);
+		}
+	});
+	
+	mxEvent.addListener(dlgDiv.querySelector('.radioBtn[data-id=tilesBtn]'), 'click', function()
+	{
+		if (radioClick(this, 'tilesBtnImg', 'tiles', 'listBtn', 'listBtnImg', 'list', false))
+		{
+			showAsList = false;
+			fillDiagramsList(curDiagList, false, showAsList);
+		}
+	});
+	
+	function createPreview(diagram)
+	{
+		var imgUrl = diagram.prevImgUrl || diagram.imgUrl || (TEMPLATE_PATH + '/' + diagram.url.substring(0, diagram.url.length - 4) + '.png');
+		var mask = document.createElement('div');
+		mask.className = "dialogMask";
+		dlgDiv.appendChild(mask);
+		var prevBox = document.createElement('div');
+		prevBox.className = "diagramPreviewBox";
+		var img = document.createElement('img');
+		img.src = imgUrl;
+		prevBox.appendChild(img);
+		var closeBtn = document.createElement('img');
+		closeBtn.src = "/images/close.png";
+		closeBtn.className = "previewCloseBtn";
+		closeBtn.setAttribute('title', mxResources.get("close"));
+		prevBox.appendChild(closeBtn);
+		var scrollTop = tempDlgContent.scrollTop;
+		
+		function closeBox(evt)
+		{
+			tempDlgContent.removeChild(prevBox);
+			dlgDiv.removeChild(mask);
+			tempDlgContent.scrollTop = scrollTop;
+		};
+
+		mxEvent.addListener(closeBtn, 'click', closeBox);
+		mxEvent.addListener(mask, 'click', closeBox);
+		tempDlgContent.appendChild(prevBox);
+		tempDlgContent.scrollTop = 0;
+		//for centering the image
+		prevBox.style.lineHeight = prevBox.clientHeight + "px";
+	};
+	
+	function swapActiveItem(newItem, activeCls, itemInfo)
+	{
+		if (currentItem != null)
+		{
+			var classes = currentItem.className.split(" ");
+
+			for (var i = 0; i < classes.length; i++)
+			{
+				if (classes[i].indexOf("Active") > -1)
+				{
+					classes.splice(i, 1);
+					break;
+				}
+			}
+			
+			currentItem.className = classes.join(" ");
+		}
+		
+		if (newItem != null)
+		{
+			currentItem = newItem;
+			currentItem.className += " " + activeCls;
+			currentItemInfo = itemInfo;
+			categorySelected = itemInfo.isCategory;
+			//activate create button
+			createBtn.className = "createBtn";
+		}
+		else 
+		{
+			currentItem = null;
+			currentItemInfo = null;
+			//disable create button
+			createBtn.className = "createBtn createBtnDisabled";
+		}
+	};
+	
+	function handleDialogOK(linkToDiagram)
+	{
+		if (currentItemInfo != null)
+		{
+			var itemInfo = currentItemInfo;
+			//disable create button
+			currentItemInfo = null;
+			createBtn.className = "createBtn createBtnDisabled createBtnBusy";
+			
+			if (itemInfo.isExternal)
+			{
+				if (linkToDiagram == true)
+				{
+					linkToDiagramCallback(itemInfo.url, itemInfo, "nameInput.value");
+				}
+				else 
+				{
+					openExtDocCallback(itemInfo.url, itemInfo, "nameInput.value");
+				}
+				
+				editorUi.hideDialog(true);
+			}
+			else
+			{
+				mxUtils.get(TEMPLATE_PATH + '/' + itemInfo.url, mxUtils.bind(this, function(req)
+				{
+					if (req.getStatus() >= 200 && req.getStatus() <= 299)
+					{
+						callback(req.getText(), "nameInput.value");
+						editorUi.hideDialog(true);
+					}
+					else
+					{
+						//TODO Handle errors	
+					}
+				}));
+			}
+		}
+	};
+	
+	function showLinkToDiagram(isShown)
+	{
+		var linkDisplay = isShown? '' : 'none';
+		
+		dlgDiv.querySelectorAll(".linkToDiagram").forEach(function(elem)
+		{
+			elem.style.display = linkDisplay;
+		});
+	};
+	
+	function fillDiagramsList(diagrams, isTemplate, asList)
+	{
+		function setSubmitBtnLbl() 
+		{
+			if (isTemplate)
+			{
+				createBtn.innerHTML = mxResources.get('create');
+			}
+			else
+			{
+				createBtn.innerHTML = mxResources.get('copy');
+			}
+			
+			showLinkToDiagram (!isTemplate);
+		};
+		
+		diagramsTiles.innerHTML = '';
+		swapActiveItem();
+		curDiagList = diagrams;
+		var grid = null;
+		
+		if (asList)
+		{
+			grid = document.createElement('table');
+			grid.className = 'diagramsListGrid';
+			//create header row
+			var hrow = document.createElement('tr');
+			var th = document.createElement('th');
+			th.style.width = "50%";
+			th.innerHTML = mxResources.get('diagram', null, 'Diagram');
+			hrow.appendChild(th);
+			th = document.createElement('th');
+			th.style.width = "25%";
+			th.innerHTML = mxResources.get('changedBy', null, 'Changed By');
+			hrow.appendChild(th);
+			th = document.createElement('th');
+			th.style.width = "25%";
+			th.innerHTML = mxResources.get('lastModifiedOn', null, 'Last modified on');
+			hrow.appendChild(th);
+			grid.appendChild(hrow);
+			diagramsTiles.appendChild(grid);
+		}
+		
+		//TODO support paging
+		for (var i = 0; i < diagrams.length; i++)
+		{
+			diagrams[i].isExternal = !isTemplate;
+			var url = diagrams[i].url;
+			var title = mxUtils.htmlEntities(diagrams[i].title);
+			var tooltip = diagrams[i].tooltip || diagrams[i].title;
+			var imgUrl = diagrams[i].imgUrl;
+			var changedBy = mxUtils.htmlEntities(diagrams[i].changedBy || "");
+			var lastModifiedOn = mxUtils.htmlEntities(diagrams[i].lastModifiedOn || "");
+			
+			if (!imgUrl)
+			{
+				imgUrl = TEMPLATE_PATH + '/' + url.substring(0, url.length - 4) + '.png';
+			}
+			
+			var titleLimit = asList? 50 : 15;
+					
+			if (title != null && title.length > titleLimit)
+			{
+				title = title.substring(0, titleLimit) + '&hellip;';
+			}
+			
+			if (asList)
+			{
+				var row = document.createElement('tr');
+				var td = document.createElement('td');
+				var prevImg = document.createElement('img');
+				prevImg.src = "/images/icon-search.svg";
+				prevImg.className = "diagramListPreviewBtn";
+				prevImg.setAttribute('title', mxResources.get("preview"));
+				td.appendChild(prevImg);
+				var titleSpan = document.createElement('span');
+				titleSpan.className = "diagramTitle";
+				titleSpan.innerHTML = title;
+				td.appendChild(titleSpan);
+				row.appendChild(td);
+				td = document.createElement('td');
+				td.innerHTML = changedBy;
+				row.appendChild(td);
+				td = document.createElement('td');
+				td.innerHTML = lastModifiedOn;
+				row.appendChild(td);
+				grid.appendChild(row);
+				
+				if (currentItem == null)
+				{
+					setSubmitBtnLbl();
+					swapActiveItem(row, "diagramsListGridActive", diagrams[i]);
+				}
+				
+				(function(diagram2, row2)
+				{
+					mxEvent.addListener(row, 'click', function()
+					{
+						if (currentItem != row2)
+						{
+							setSubmitBtnLbl();
+							swapActiveItem(row2, "diagramsListGridActive", diagram2);
+						}
+					});
+					
+					mxEvent.addListener(row, 'dblclick', handleDialogOK);
+					
+					mxEvent.addListener(prevImg, 'click', function()
+					{
+						createPreview(diagram2);
+					});
+				})(diagrams[i], row);
+			}
+			else
+			{
+				var tile = document.createElement('div');
+				tile.className = "diagramTile";
+				tile.setAttribute('title', tooltip);
+				
+				if (currentItem == null)
+				{
+					setSubmitBtnLbl();
+					swapActiveItem(tile, "diagramTileActive", diagrams[i]);
+				}
+				
+				var imgDiv = document.createElement('div');
+				imgDiv.className = "diagramTileImg diagramTileImgLoading";
+				var img = document.createElement('img');
+				img.style.display = "none";
+				
+				(function(img2, imgDiv2)
+				{
+					img.onload = function() 
+					{
+						imgDiv2.className = "diagramTileImg";
+						img2.style.display = "";	
+					}
+					
+					img.onerror = function()
+					{
+						imgDiv2.className = "diagramTileImg diagramTileImgError";
+					}
+				})(img, imgDiv);
+				
+				img.src = imgUrl;
+				imgDiv.appendChild(img);
+				tile.appendChild(imgDiv);
+	
+				var lblDiv = document.createElement('div');
+				lblDiv.className = "diagramTileLbl";
+				lblDiv.innerHTML = title != null? title : "";
+				tile.appendChild(lblDiv);
+				
+				var prevImg = document.createElement('img');
+				prevImg.src = "/images/icon-search.svg";
+				prevImg.className = "diagramPreviewBtn";
+				prevImg.setAttribute('title', mxResources.get("preview"));
+				tile.appendChild(prevImg);
+				
+				(function(diagram2, tile2)
+				{
+					mxEvent.addListener(tile, 'click', function()
+					{
+						if (currentItem != tile2)
+						{
+							setSubmitBtnLbl();
+							swapActiveItem(tile2, "diagramTileActive", diagram2);
+						}
+					});
+					
+					mxEvent.addListener(tile, 'dblclick', handleDialogOK);
+					
+					mxEvent.addListener(prevImg, 'click', function()
+					{
+						createPreview(diagram2);
+					});
+				})(diagrams[i], tile);
+				
+				diagramsTiles.appendChild(tile);
+			}
+		}
+	};
+	
+	function fillNewDiagramCats(newDiagramCats, showAll) 
+	{
+		newDiagramCatList.innerHTML = "";
+		swapActiveItem();
+		var catCount = !showAll && newDiagramCats.length > 5 ? 5 : newDiagramCats.length;
+		
+		for (var i = 0; i < catCount; i++)
+		{
+			var cat = newDiagramCats[i];
+			cat.isCategory = true;
+			var entry = document.createElement('div');
+			var label = mxResources.get(cat.title);
+			
+			if (label == null)
+			{
+				label = cat.title.substring(0, 1).toUpperCase() + cat.title.substring(1);
+			}
+			
+			entry.className = 'newDiagramCatItem';
+			entry.setAttribute('title', label);
+			
+			label = mxUtils.htmlEntities(label);
+			
+			if (label.length > 15)
+			{
+				label = label.substring(0, 15) + '&hellip;';
+			}
+
+			if (currentItem == null)
+			{
+				createBtn.innerHTML = mxResources.get('create');
+				showLinkToDiagram();
+				swapActiveItem(entry, "newDiagramCatItemActive", cat);
+			}
+			
+			var imgDiv = document.createElement('div');
+			imgDiv.className = "newDiagramCatItemImg";
+			var img = document.createElement('img');
+			img.src = NEW_DIAGRAM_CATS_PATH + '/' + cat.img;
+			imgDiv.appendChild(img);
+			entry.appendChild(imgDiv);
+
+			var lblDiv = document.createElement('div');
+			lblDiv.className = "newDiagramCatItemLbl";
+			lblDiv.innerHTML = label;
+			entry.appendChild(lblDiv);
+			
+			newDiagramCatList.appendChild(entry);
+
+			(function(cat2, entry2)
+			{
+				mxEvent.addListener(entry, 'click', function()
+				{
+					if (currentItem != entry2)
+					{
+						createBtn.innerHTML = mxResources.get('create');
+						showLinkToDiagram();
+						swapActiveItem(entry2, "newDiagramCatItemActive", cat2);
+					}
+				});
+				
+				mxEvent.addListener(entry, 'dblclick', handleDialogOK);
+			})(cat, entry);
+		}
+		
+		showAllBtn.style.display = newDiagramCats.length < 5 ? "none" : "";
+	};
+	
+	mxEvent.addListener(showAllBtn, 'click', function()
+	{
+		if (showingAll)
+		{
+			newDiagramCat.style.height = "280px";
+			newDiagramCatList.style.height = "190px";
+			showAllBtn.innerHTML = mxResources.get('showAll', null, '+ Show all');
+			fillNewDiagramCats(newDiagramCats);
+		}
+		else 
+		{
+			newDiagramCat.style.height = "440px";
+			newDiagramCatList.style.height = "355px";
+			showAllBtn.innerHTML = mxResources.get('showLess', null, '- Show less');
+			fillNewDiagramCats(newDiagramCats, true);
+		}
+
+		showingAll = !showingAll;
+	});
+	
+	function fillTemplatesList(categories)
+	{
+		var list = dlgDiv.querySelector(".templatesList");
+		
+		for (var cat in categories)
+		{
+			var entry = document.createElement('div');
+			var label = mxResources.get(cat);
+			var templateList = categories[cat];
+			
+			if (label == null)
+			{
+				label = cat.substring(0, 1).toUpperCase() + cat.substring(1);
+			}
+			
+			entry.className = 'templateCatLink';
+			entry.setAttribute('title', label + ' (' + templateList.length + ')');
+			
+			label = mxUtils.htmlEntities(label);
+			
+			if (label.length > 15)
+			{
+				label = label.substring(0, 15) + '&hellip;';
+			}
+					
+			entry.innerHTML = label + ' (' + templateList.length + ')';
+			
+			list.appendChild(entry);
+
+			(function(cat2, label2, entry2)
+			{
+				mxEvent.addListener(entry, 'click', function()
+				{
+					if (currentEntry != entry2)
+					{
+						if (currentEntry != null)
+						{
+							currentEntry.style.fontWeight = 'normal';
+							currentEntry.style.textDecoration = 'none';
+						}
+						else
+						{
+							newDiagramCat.style.display = 'none';
+							diagramsList.style.minHeight = '100%';
+						}
+						
+						currentEntry = entry2;
+						currentEntry.style.fontWeight = 'bold';
+						currentEntry.style.textDecoration = 'underline';
+						
+						tempDlgContent.scrollTop = 0;
+						
+						if (callInitiated)
+						{
+							cancelPendingCall = true;
+						}
+
+						diagramsListTitle.innerHTML = label2;
+						diagramsListBtns.style.display = 'none';
+						fillDiagramsList(categories[cat2], true);
+					}
+				});
+			})(cat, label, entry);
+		}
+	};
+	
+	var indexLoaded = false, indexLoaded2 = false;
+	var categories = {};
+	var newDiagramCats = [];
+	var categoryCount = 1;
+	
+	mxUtils.get(templateFile, function(req)
+	{
+		// Workaround for index loaded 3 times in iOS offline mode
+		if (!indexLoaded)
+		{
+			indexLoaded = true;
+			var tmpDoc = req.getXml();
+			var node = tmpDoc.documentElement.firstChild;
+
+			while (node != null)
+			{
+				if (typeof(node.getAttribute) !== 'undefined')
+				{
+					var url = node.getAttribute('url');
+					
+					if (url != null)
+					{
+						var slash = url.indexOf('/');
+						var category = url.substring(0, slash);
+						
+						var list = categories[category];
+						
+						if (list == null)
+						{
+							categoryCount++;
+							list = [];
+							categories[category] = list;
+						}
+						
+						list.push({url: node.getAttribute('url'), libs: node.getAttribute('libs'),
+							title: node.getAttribute('title'), tooltip: node.getAttribute('url'), imgUrl: node.getAttribute('imgUrl')});
+					}
+				}
+				
+				node = node.nextSibling;
+			}
+			
+			fillTemplatesList(categories);
+		}
+	});
+	
+	mxUtils.get(newDiagramCatsFile, function(req)
+	{
+		// Workaround for index loaded 3 times in iOS offline mode
+		if (!indexLoaded2)
+		{
+			indexLoaded2 = true;
+			var tmpDoc = req.getXml();
+			var node = tmpDoc.documentElement.firstChild;
+
+			while (node != null)
+			{
+				if (typeof(node.getAttribute) !== 'undefined')
+				{
+					var title = node.getAttribute('title');
+					
+					if (title != null)
+					{
+						newDiagramCats.push({img: node.getAttribute('img'), libs: node.getAttribute('libs'),
+							title: node.getAttribute('title')});
+					}
+				}
+				
+				node = node.nextSibling;
+			}
+			
+			fillNewDiagramCats(newDiagramCats);
+		}
+	});
+	
+	var extDiagramsCallback = function(list, errorMsg)
+	{
+		diagramsListBtns.style.display = '';
+		spinner.stop();
+
+		callInitiated = false;
+		
+		if (cancelPendingCall)
+		{
+			cancelPendingCall = false;
+			return;
+		}
+		
+		if (errorMsg)
+		{
+			diagramsTiles.innerHTML = errorMsg;
+		}
+		else if (list.length == 0)
+		{
+			diagramsTiles.innerHTML = mxResources.get('noDiagrams', null, 'No Diagrams Found');
+		}
+		else
+		{
+			fillDiagramsList(list, false, showAsList);
+		}
+	};
+	
+	function getRecentDocs(getAll)
+	{
+		if (recentDocsCallback)
+		{
+			tempDlgContent.scrollTop = 0;
+			diagramsTiles.innerHTML = '';
+			spinner.spin(diagramsTiles);
+			cancelPendingCall = false;
+			callInitiated = true;
+			diagramsListTitle.innerHTML = mxResources.get('recentDiag', null, 'Recent Diagrams');
+			lastSearchStr = null;
+			recentDocsCallback(extDiagramsCallback, getAll? null : username);
+		}
+	};
+	
+	getRecentDocs(isGetAll);
+
+	var delayTimer = null;
+	
+	function doSearch(searchStr)
+	{
+		deselectTempCat();
+		tempDlgContent.scrollTop = 0;
+		diagramsTiles.innerHTML = '';
+		spinner.spin(diagramsTiles);
+		cancelPendingCall = false;
+		callInitiated = true;
+		delayTimer = null;
+		diagramsListTitle.innerHTML = mxResources.get('searchResults', null, 'Search Results') + 
+								' "' + mxUtils.htmlEntities(searchStr) + '"';
+		searchDocsCallback(searchStr, extDiagramsCallback, isGetAll? null : username);
+		lastSearchStr = searchStr;
+	};
+	
+	//TODO use request id to allow only last request to show results
+	if (searchDocsCallback)
+	{
+		//Use keyup to detect delete and backspace
+		mxEvent.addListener(dlgDiv.querySelector(".searchBox"), 'keyup', function(evt)
+		{
+			var searchInout = this;
+			
+			if (delayTimer != null)
+			{
+				clearTimeout(delayTimer);
+			}
+			
+			if (evt.keyCode == 13)
+			{
+				doSearch(searchInout.value);
+			}
+			else
+			{
+				delayTimer = setTimeout(function()
+				{
+					doSearch(searchInout.value);	
+				}, 500);
+			}
+		});
+	}
+	
+	mxEvent.addListener(createBtn, 'click', handleDialogOK);
+	
+	mxEvent.addListener(dlgDiv.querySelector(".linkToDiagramBtn"), 'click', function(evt)
+	{
+		handleDialogOK(true);
+	});
+
+	mxEvent.addListener(dlgDiv.querySelector('.cancelBtn'), 'click', function()
+	{
+		if (cancelCallback != null)
+		{
+			cancelCallback();
+		}
+		
+		editorUi.hideDialog(true);
+	});
+};

+ 334 - 17
src/main/webapp/js/diagramly/Editor.js

@@ -1198,34 +1198,289 @@
 		/**
 		 * Configures custom properties for mx2 based shapes
 		 */
+		DiagramFormatPanel.prototype.addCommonVertexProperties = function(p)
+		{
+	        p.push({type: 'separator'});
+	        p.push({name: 'fillOpacity', dispName: 'Fill Opacity', type: 'int', min: 0, max: 100, defVal: 100});
+	        p.push({name: 'strokeOpacity', dispName: 'Stroke Opacity', type: 'int', min: 0, max: 100, defVal: 100});
+	        p.push({name: 'overflow', dispName: 'Text Overflow', defVal: 'visible', type: 'enum',
+	        	enumList: [{val: 'visible', dispName: 'Visible'}, {val: 'hidden', dispName: 'Hidden'}, {val: 'fill', dispName: 'Fill'}, {val: 'width', dispName: 'Width'}]
+	        });
+	        p.push({name: 'noLabel', dispName: 'Hide Label', type: 'bool', defVal: false});
+	        p.push({name: 'labelPadding', dispName: 'Label Padding', type: 'float', defVal: 0});
+	        p.push({name: 'direction', dispName: 'Direction', type: 'enum', defVal: 'east',
+	        	enumList: [{val: 'east', dispName: 'East'}, {val: 'north', dispName: 'North'}, {val: 'south', dispName: 'South'}, {val: 'west', dispName: 'West'}]
+	        });
+	        p.push({name: 'portConstraint', dispName: 'Port Constraint', type: 'enum', defVal: 'none',
+	        	enumList: [{val: 'none', dispName: 'None'}, {val: 'east', dispName: 'East'}, {val: 'north', dispName: 'North'}, {val: 'south', dispName: 'South'}, {val: 'west', dispName: 'West'}]
+	        });
+	        p.push({name: 'portConstraintRotation', dispName: 'Port Const. Rot.', type: 'bool', defVal: false});
+	        p.push({name: 'fixDash', dispName: 'Fixed Dash', type: 'bool', defVal: false});
+	        p.push({name: 'autosize', dispName: 'Autosize', type: 'bool', defVal: false});
+	        p.push({name: 'collapsible', dispName: 'Collapsible', type: 'bool', defVal: false});
+	        p.push({name: 'editable', dispName: 'Editable', type: 'bool', defVal: true});
+	        p.push({name: 'backgroundOutline', dispName: 'Background Outline', type: 'bool', defVal: false});
+	        p.push({name: 'movable', dispName: 'Movable', type: 'bool', defVal: true});
+	        p.push({name: 'resizable', dispName: 'Resizable', type: 'bool', defVal: true});
+	        p.push({name: 'resizeWidth', dispName: 'Resize Width', type: 'bool', defVal: false});
+	        p.push({name: 'resizeHeight', dispName: 'Resize Height', type: 'bool', defVal: false});
+	        p.push({name: 'rotatable', dispName: 'Rotatable', type: 'bool', defVal: true});
+	        p.push({name: 'cloneable', dispName: 'Cloneable', type: 'bool', defVal: true});
+	        p.push({name: 'deletable', dispName: 'Deletable', type: 'bool', defVal: true});
+		};
+
+		DiagramFormatPanel.prototype.addCommonEdgeProperties = function(p)
+		{
+	        p.push({type: 'separator'});
+	        p.push({name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: mxConstants.LINE_ARCSIZE});
+	        p.push({name: 'targetPortConstraint', dispName: 'Target Port Constraint', type: 'enum', defVal: 'none',
+	        	enumList: [{val: 'none', dispName: 'None'}, {val: 'east', dispName: 'East'}, {val: 'north', dispName: 'North'}, {val: 'south', dispName: 'South'}, {val: 'west', dispName: 'West'}]
+	        });
+	        p.push({name: 'sourcePortConstraint', dispName: 'Source Port Constraint', type: 'enum', defVal: 'none',
+	        	enumList: [{val: 'none', dispName: 'None'}, {val: 'east', dispName: 'East'}, {val: 'north', dispName: 'North'}, {val: 'south', dispName: 'South'}, {val: 'west', dispName: 'West'}]
+	        });
+	        p.push({name: 'fillOpacity', dispName: 'Fill Opacity', type: 'int', min: 0, max: 100, defVal: 100});
+	        p.push({name: 'strokeOpacity', dispName: 'Stroke Opacity', type: 'int', min: 0, max: 100, defVal: 100});
+	        p.push({name: 'startFill', dispName: 'Start Fill', type: 'bool', defVal: true});
+	        p.push({name: 'endFill', dispName: 'End Fill', type: 'bool', defVal: true});
+	        p.push({name: 'sourcePerimeterSpacing', dispName: 'Source Perimeter Spacing', type: 'float', defVal: 0});
+	        p.push({name: 'targetPerimeterSpacing', dispName: 'Target Perimeter Spacing', type: 'float', defVal: 0});
+	        p.push({name: 'perimeterSpacing', dispName: 'Perimeter Spacing', type: 'float', defVal: 0});
+	        p.push({name: 'anchorPointDirection', dispName: 'Anchor Point Direction', type: 'bool', defVal: true});
+	        p.push({name: 'fixDash', dispName: 'Fixed Dash', type: 'bool', defVal: false});
+	        p.push({name: 'editable', dispName: 'Editable', type: 'bool', defVal: true});
+	        p.push({name: 'backgroundOutline', dispName: 'Background Outline', type: 'bool', defVal: false});
+	        p.push({name: 'bendable', dispName: 'Bendable', type: 'bool', defVal: true});
+	        p.push({name: 'movable', dispName: 'Movable', type: 'bool', defVal: true});
+	        p.push({name: 'cloneable', dispName: 'Cloneable', type: 'bool', defVal: true});
+	        p.push({name: 'deletable', dispName: 'Deletable', type: 'bool', defVal: true});
+	        p.push({name: 'loopStyle', dispName: 'Loop Style', type: 'bool', defVal: true});
+		};
+
+		mxCellRenderer.prototype.defaultVertexShape.prototype.customProperties = [
+	        {name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: mxConstants.LINE_ARCSIZE},
+	        {name: 'absoluteArcSize', dispName: 'Abs. Arc Size', type: 'bool', defVal: false}
+	      ];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.prototype.defaultVertexShape.prototype.customProperties);
+
+		mxCellRenderer.prototype.defaultEdgeShape.prototype.customProperties = [
+	      ];
+		DiagramFormatPanel.prototype.addCommonEdgeProperties(mxCellRenderer.prototype.defaultEdgeShape.prototype.customProperties);
+
+		mxCellRenderer.defaultShapes['link'].prototype.customProperties = [
+	        {name: 'width', dispName: 'Width', type: 'float', min:0, defVal: 4}
+		];
+		DiagramFormatPanel.prototype.addCommonEdgeProperties(mxCellRenderer.defaultShapes['link'].prototype.customProperties);
+
+		mxCellRenderer.defaultShapes['flexArrow'].prototype.customProperties = [
+	        {name: 'width', dispName: 'Width', type: 'float', min:0, defVal: 10},
+	        {name: 'startWidth', dispName: 'Start Width', type: 'float', min:0, defVal: 20},
+	        {name: 'endWidth', dispName: 'End Width', type: 'float', min:0, defVal: 20}
+		];
+		DiagramFormatPanel.prototype.addCommonEdgeProperties(mxCellRenderer.defaultShapes['flexArrow'].prototype.customProperties);
 
+		mxCellRenderer.defaultShapes['ellipse'].prototype.customProperties = [];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.defaultShapes['ellipse'].prototype.customProperties);
+
+		mxCellRenderer.defaultShapes['process'].prototype.customProperties = [
+			{name: 'size', dispName: 'Indent', type: 'float', min: 0, max: 0.5, defVal: 0.1}
+		];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.defaultShapes['process'].prototype.customProperties);
+
+		mxCellRenderer.defaultShapes['rhombus'].prototype.customProperties = [
+	        {name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, max: 50, defVal: mxConstants.LINE_ARCSIZE},
+	        {name: 'double', dispName: 'Double', type: 'bool', defVal: false}
+		];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.defaultShapes['rhombus'].prototype.customProperties);
+		
 		mxCellRenderer.defaultShapes['partialRectangle'].prototype.customProperties = [
-	        {name: 'top', dispName: 'Top Line', type: 'bool', defVal: 1},
-	        {name: 'bottom', dispName: 'Bottom Line', type: 'bool', defVal: 1},
-	        {name: 'left', dispName: 'Left Line', type: 'bool', defVal: 1},
-	        {name: 'right', dispName: 'Right Line', type: 'bool', defVal: 1}
+	        {name: 'top', dispName: 'Top Line', type: 'bool', defVal: true},
+	        {name: 'bottom', dispName: 'Bottom Line', type: 'bool', defVal: true},
+	        {name: 'left', dispName: 'Left Line', type: 'bool', defVal: true},
+	        {name: 'right', dispName: 'Right Line', type: 'bool', defVal: true}
 	      ];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.defaultShapes['partialRectangle'].prototype.customProperties);
+		
+		mxCellRenderer.defaultShapes['parallelogram'].prototype.customProperties = [
+	        {name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: mxConstants.LINE_ARCSIZE},
+	        {name: 'size', dispName: 'Slope Angle', type: 'float', min:0, max: 1, defVal: 0.2}
+		];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.defaultShapes['parallelogram'].prototype.customProperties);
+		
+		mxCellRenderer.defaultShapes['hexagon'].prototype.customProperties = [
+	        {name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: mxConstants.LINE_ARCSIZE},
+	        {name: 'size', dispName: 'Slope Angle', type: 'float', min:0, max: 1, defVal: 0.25}
+		];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.defaultShapes['hexagon'].prototype.customProperties);
+		
+		mxCellRenderer.defaultShapes['triangle'].prototype.customProperties = [
+	        {name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: mxConstants.LINE_ARCSIZE}
+		];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.defaultShapes['triangle'].prototype.customProperties);
+		
+		mxCellRenderer.defaultShapes['document'].prototype.customProperties = [
+	        {name: 'size', dispName: 'Size', type: 'float', defVal: 0.3, min:0, max:1}
+		];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.defaultShapes['document'].prototype.customProperties);
+		
+		mxCellRenderer.defaultShapes['internalStorage'].prototype.customProperties = [
+	        {name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: mxConstants.LINE_ARCSIZE},
+	        {name: 'dx', dispName: 'Left Line', type: 'float', min:0, defVal: 20},
+	        {name: 'dy', dispName: 'Top Line', type: 'float', min:0, defVal: 20}
+		];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.defaultShapes['internalStorage'].prototype.customProperties);
+		
+		mxCellRenderer.defaultShapes['cube'].prototype.customProperties = [
+	        {name: 'size', dispName: 'Size', type: 'float', min:0, defVal:20 }
+		];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.defaultShapes['cube'].prototype.customProperties);
+		
+		mxCellRenderer.defaultShapes['step'].prototype.customProperties = [
+	        {name: 'size', dispName: 'Notch Size', type: 'float', min:0, defVal:20},
+	        {name: 'fixedSize', dispName: 'Fixed Size', type: 'bool', defVal:true}
+		];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.defaultShapes['step'].prototype.customProperties);
+		
+		mxCellRenderer.defaultShapes['trapezoid'].prototype.customProperties = [
+	        {name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: mxConstants.LINE_ARCSIZE},
+	        {name: 'size', dispName: 'Slope Angle', type: 'float', min:0, max: 1, defVal: 0.2}
+		];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.defaultShapes['trapezoid'].prototype.customProperties);
+		
+		mxCellRenderer.defaultShapes['tape'].prototype.customProperties = [
+	        {name: 'size', dispName: 'Size', type: 'float', min:0, max:1, defVal:0.4 }
+		];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.defaultShapes['tape'].prototype.customProperties);
+		
+		mxCellRenderer.defaultShapes['note'].prototype.customProperties = [
+	        {name: 'size', dispName: 'Fold Size', type: 'float', min:0, defVal: 30}
+	      ];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.defaultShapes['note'].prototype.customProperties);
+		
+		mxCellRenderer.defaultShapes['card'].prototype.customProperties = [
+	        {name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: mxConstants.LINE_ARCSIZE},
+	        {name: 'size', dispName: 'Cutoff Size', type: 'float', min:0, defVal: 30}
+	      ];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.defaultShapes['card'].prototype.customProperties);
+		
+		mxCellRenderer.defaultShapes['callout'].prototype.customProperties = [
+	        {name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: mxConstants.LINE_ARCSIZE},
+	        {name: 'base', dispName: 'Callout Width', type: 'float', min:0, defVal: 20},
+	        {name: 'size', dispName: 'Callout Length', type: 'float', min:0, defVal: 30},
+	        {name: 'position', dispName: 'Callout Position', type: 'float', min:0, max:1, defVal: 0.5},
+	        {name: 'position2', dispName: 'Callout Tip Position', type: 'float', min:0, max:1, defVal: 0.5},
+	      ];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.defaultShapes['callout'].prototype.customProperties);
+		
+		mxCellRenderer.defaultShapes['folder'].prototype.customProperties = [
+	        {name: 'tabWidth', dispName: 'Tab Width', type: 'float'},
+	        {name: 'tabHeight', dispName: 'Tab Height', type: 'float'},
+	        {name: 'tabPosition', dispName: 'Tap Position', type: 'enum',
+	        	enumList: [{val: 'left', dispName: 'Left'}, {val: 'right', dispName: 'Right'}]
+	        }
+	      ];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.defaultShapes['folder'].prototype.customProperties);
 		
 		mxCellRenderer.defaultShapes['swimlane'].prototype.customProperties = [
+	        {name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: 15},
 	        {name: 'startSize', dispName: 'Header Size', type: 'float'},
-	        {name: 'swimlaneFillColor', dispName: 'Lane Color', type: 'color'}
+	        {name: 'swimlaneFillColor', dispName: 'Lane Color', type: 'color'},
+	        {name: 'horizontal', dispName: 'Horizontal', type: 'bool', defVal: true}
 	      ];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.defaultShapes['swimlane'].prototype.customProperties);
 		
 		mxCellRenderer.defaultShapes['doubleEllipse'].prototype.customProperties = [
-	        {name: 'margin', dispName: 'Indent', type: 'float'}
+	        {name: 'margin', dispName: 'Indent', type: 'float', min:0, defVal:4}
 	      ];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.defaultShapes['doubleEllipse'].prototype.customProperties);
 		
 		mxCellRenderer.defaultShapes['ext'].prototype.customProperties = [
-	        {name: 'margin', dispName: 'Indent', type: 'float'}
+	        {name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: 15},
+			{name: 'double', dispName: 'Double', type: 'bool', defVal: false},
+	        {name: 'margin', dispName: 'Indent', type: 'float', min: 0, defVal:0}
 	      ];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.defaultShapes['ext'].prototype.customProperties);
 		
-		mxCellRenderer.defaultShapes['rhombus'].prototype.customProperties = [
-	        {name: 'margin', dispName: 'Indent', type: 'float'}
+		mxCellRenderer.defaultShapes['curlyBracket'].prototype.customProperties = [
+			{name: 'rounded', dispName: 'Rounded', type: 'bool', defVal: true},
+	        {name: 'size', dispName: 'Size', type: 'float', min:0, max: 1, defVal: 0.5}
 	      ];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.defaultShapes['curlyBracket'].prototype.customProperties);
 		
-		mxCellRenderer.defaultShapes['note'].prototype.customProperties = [
-	        {name: 'size', dispName: 'Fold Size', type: 'float'}
+		mxCellRenderer.defaultShapes['image'].prototype.customProperties = [
+			{name: 'imageAspect', dispName: 'Fixed Image Aspect', type: 'bool', defVal:true}
 	      ];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.defaultShapes['image'].prototype.customProperties);
+		
+		mxCellRenderer.defaultShapes['label'].prototype.customProperties = [
+			{name: 'imageAspect', dispName: 'Fixed Image Aspect', type: 'bool', defVal:true},
+			{name: 'imageAlign', dispName: 'Image Align', type: 'enum',
+				enumList: [{val: 'left', dispName: 'Left'}, {val: 'center', dispName: 'Center'}, {val: 'right', dispName: 'Right'}], defVal: 'left'},
+			{name: 'imageVerticalAlign', dispName: 'Image Vertical Align', type: 'enum',
+				enumList: [{val: 'top', dispName: 'Top'}, {val: 'middle', dispName: 'Middle'}, {val: 'bottom', dispName: 'Bottom'}], defVal: 'middle'},
+	        {name: 'imageWidth', dispName: 'Image Width', type: 'float', min:0, defVal: 24},
+	        {name: 'imageHeight', dispName: 'Image Height', type: 'float', min:0, defVal: 24},
+	        {name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: 12},
+	        {name: 'absoluteArcSize', dispName: 'Abs. Arc Size', type: 'bool', defVal: false}
+	      ];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.defaultShapes['label'].prototype.customProperties);
+		
+		mxCellRenderer.defaultShapes['dataStorage'].prototype.customProperties = [
+	        {name: 'size', dispName: 'Size', type: 'float', min:0, max:1, defVal:0.1 }
+		];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.defaultShapes['dataStorage'].prototype.customProperties);
+		
+		mxCellRenderer.defaultShapes['manualInput'].prototype.customProperties = [
+	        {name: 'size', dispName: 'Size', type: 'float', min:0, defVal:30 },
+	        {name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: 20}
+		];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.defaultShapes['manualInput'].prototype.customProperties);
+		
+		mxCellRenderer.defaultShapes['loopLimit'].prototype.customProperties = [
+	        {name: 'size', dispName: 'Size', type: 'float', min:0, defVal:20 },
+	        {name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: 20}
+		];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.defaultShapes['loopLimit'].prototype.customProperties);
+		
+		mxCellRenderer.defaultShapes['offPageConnector'].prototype.customProperties = [
+	        {name: 'size', dispName: 'Size', type: 'float', min:0, defVal:38 },
+	        {name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: 20}
+		];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.defaultShapes['offPageConnector'].prototype.customProperties);
+		
+		mxCellRenderer.defaultShapes['display'].prototype.customProperties = [
+	        {name: 'size', dispName: 'Size', type: 'float', min: 0, max: 1, defVal: 0.25 }
+		];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.defaultShapes['display'].prototype.customProperties);
+		
+		mxCellRenderer.defaultShapes['singleArrow'].prototype.customProperties = [
+	        {name: 'arrowWidth', dispName: 'Arrow Width', type: 'float', min: 0, max: 1, defVal: 0.3 },
+	        {name: 'arrowSize', dispName: 'Arrowhead Length', type: 'float', min: 0, max: 1, defVal: 0.2 }
+		];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.defaultShapes['singleArrow'].prototype.customProperties);
+		
+		mxCellRenderer.defaultShapes['doubleArrow'].prototype.customProperties = [
+	        {name: 'arrowWidth', dispName: 'Arrow Width', type: 'float', min: 0, max: 1, defVal: 0.3 },
+	        {name: 'arrowSize', dispName: 'Arrowhead Length', type: 'float', min: 0, max: 1, defVal: 0.2 }
+		];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.defaultShapes['doubleArrow'].prototype.customProperties);
+		
+		mxCellRenderer.defaultShapes['cross'].prototype.customProperties = [
+	        {name: 'size', dispName: 'Size', type: 'float', min: 0, max: 1, defVal: 0.2 }
+		];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.defaultShapes['cross'].prototype.customProperties);
+		
+		mxCellRenderer.defaultShapes['corner'].prototype.customProperties = [
+	        {name: 'dx', dispName: 'Width1', type: 'float', min: 0, defVal: 20 },
+	        {name: 'dy', dispName: 'Width2', type: 'float', min: 0, defVal: 20 },
+		];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.defaultShapes['corner'].prototype.customProperties);
+		
+		mxCellRenderer.defaultShapes['tee'].prototype.customProperties = [
+	        {name: 'dx', dispName: 'Width1', type: 'float', min: 0, defVal: 20 },
+	        {name: 'dy', dispName: 'Width2', type: 'float', min: 0, defVal: 20 },
+		];
+		DiagramFormatPanel.prototype.addCommonVertexProperties(mxCellRenderer.defaultShapes['tee'].prototype.customProperties);
 		
 		/**
 		 * Configures global color schemes.
@@ -1285,6 +1540,14 @@
 			
 			if (state != null)
 			{
+				//Add common properties to all xml stencils shapes
+				if (state.shape != null && state.shape.stencil != null && !state.shape.customPropAdded)
+				{
+					state.shape.customPropAdded = true;
+					state.shape.customProperties = state.shape.customProperties || [];
+					DiagramFormatPanel.prototype.addCommonVertexProperties(state.shape.customProperties);
+				}
+				
 				handleCustomProp(state.shape.customProperties);
 			}
 			
@@ -1394,6 +1657,9 @@
 				graph.getModel().beginUpdate();
 				try
 				{
+					var changedProps = [];
+					var changedVals = [];
+
 					if (prop.index != null)
 					{
 						var allVals = [];
@@ -1431,14 +1697,44 @@
 						if (prop.countProperty != null)
 						{
 							graph.setCellStyles(prop.countProperty, allVals.length, graph.getSelectionCells());
-							that.editorUi.fireEvent(new mxEventObject('styleChanged', 'keys', [prop.countProperty],
-									'values', [allVals.length], 'cells', graph.getSelectionCells()));
+							
+							changedProps.push(prop.countProperty);
+							changedVals.push(allVals.length);
 						}
 					}
 
 					graph.setCellStyles(pName, newVal, graph.getSelectionCells());
-					that.editorUi.fireEvent(new mxEventObject('styleChanged', 'keys', [pName],
-							'values', [newVal], 'cells', graph.getSelectionCells()));
+					changedProps.push(pName);
+					changedVals.push(newVal);
+					
+					if (prop.dependentProps != null)
+					{
+						for (var i = 0; i < prop.dependentProps.length; i++)
+						{
+							var defVal = prop.dependentPropsDefVal[i];
+							var vals = prop.dependentPropsVals[i];
+							
+							if (vals.length > newVal)
+							{
+								vals = vals.slice(0, newVal);
+							}
+							else
+							{
+								for (var j = vals.length; j < newVal; j++)
+								{
+									vals.push(defVal);
+								}
+							}
+							
+							vals = vals.join(',');
+							graph.setCellStyles(prop.dependentProps[i], vals, graph.getSelectionCells());
+							changedProps.push(prop.dependentProps[i]);
+							changedVals.push(vals);
+						}
+					}
+					
+					that.editorUi.fireEvent(new mxEventObject('styleChanged', 'keys', changedProps,
+						'values', changedVals, 'cells', graph.getSelectionCells()));
 				}
 				finally
 				{
@@ -1540,7 +1836,7 @@
 					{
 						vals[i] = curVals[i] != null? curVals[i] : (defVal != null? defVal : "");
 					}
-						
+					
 					secondLevel.push({name: pName, values: vals, type: subType, defVal: defVal, parentRow: myRow, flipBkg: flipBkg, size: size});
 				}
 				
@@ -1684,8 +1980,12 @@
 							document.body.removeChild(input);
 						});
 						
+						var dontSet = false;
+						
 						function setInputVal()
 						{
+							if (dontSet) return;
+							
 							var inputVal = input.value;
 							
 							if (prop.min != null && inputVal < prop.min)
@@ -1712,6 +2012,7 @@
 								
 								try
 								{
+									dontSet = true;
 									document.body.removeChild(input);
 								}
 								catch(e){}
@@ -1776,7 +2077,23 @@
 				}
 				else if (prop.type == 'staticArr') //if dynamic values are needed, a more elegant technique is needed to replace such values
 				{
-					prop.size = parseInt(state.style[prop.sizeProperty]) || 0;
+					prop.size = parseInt(state.style[prop.sizeProperty] || properties[prop.sizeProperty].defVal) || 0;
+				}
+				else if (prop.dependentProps != null)
+				{
+					var dependentProps = prop.dependentProps;
+					var dependentPropsVals = [];
+					var dependentPropsDefVal = [];
+					
+					for (var i = 0; i < dependentProps.length; i++)
+					{
+						var propVal = state.style[dependentProps[i]];
+						dependentPropsDefVal.push(properties[dependentProps[i]].subDefVal);
+						dependentPropsVals.push(propVal != null? propVal.split(',') : []);
+					}
+					
+					prop.dependentPropsDefVal = dependentPropsDefVal;
+					prop.dependentPropsVals = dependentPropsVals;
 				}
 				
 				grid.appendChild(createPropertyRow(key, pValue, prop, isOdd, flipBkg));

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

@@ -7826,7 +7826,7 @@
 		}
 		
 		// Adds an element to edit the style in the footer in test mode
-		if (urlParams['test'] == '1')
+		if (urlParams['styledev'] == '1')
 		{
 			var footer = document.getElementById('geFooter');
 

+ 1 - 0
src/main/webapp/js/diagramly/Init.js

@@ -23,6 +23,7 @@ window.GRAPH_IMAGE_PATH = window.GRAPH_IMAGE_PATH || 'img';
 window.ICONSEARCH_PATH = window.ICONSEARCH_PATH || ((navigator.userAgent.indexOf('MSIE') >= 0 ||
 	urlParams['dev']) && window.location.protocol != 'file:' ? 'iconSearch' : 'https://www.draw.io/iconSearch');
 window.TEMPLATE_PATH = window.TEMPLATE_PATH || 'templates';
+window.NEW_DIAGRAM_CATS_PATH = window.NEW_DIAGRAM_CATS_PATH || 'newDiagramCats';
 
 // Directory for i18 files and basename for main i18n file
 window.RESOURCES_PATH = window.RESOURCES_PATH || 'resources';

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

@@ -2558,6 +2558,57 @@
 
 			menu.addSeparator(parent);
 			this.addMenuItem(menu, 'tags', parent);
+			
+			if (urlParams['newTempDlg'] == '1')
+			{
+				editorUi.actions.addAction('templates', function()
+				{
+					var tempDlg = new TemplatesDialog();
+					editorUi.showDialog(tempDlg.container, tempDlg.width, tempDlg.height, true, false, null, false, true);
+					tempDlg.init(editorUi, function(xml){console.log(xml)}, null,
+							null, null, "user", function(callback, username)
+					{
+						setTimeout(function(){
+							username? callback([
+								{url: '123', title: 'Test 1Test 1Test 1Test 1Test 1Test 1Test 11Test 1Test 11Test 1Test 1dgdsgdfg fdg dfgdfg dfg dfg'},
+								{url: '123', title: 'Test 2', imgUrl: 'https://www.google.com.eg/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png'},
+								{url: '123', title: 'Test 3', changedBy: 'Ashraf Teleb', lastModifiedOn: 'Yesterday'},
+								{url: '123', title: 'Test 4'},
+								{url: '123', title: 'Test 5'},
+								{url: '123', title: 'Test 6'}
+							]) : callback([
+								{url: '123', title: 'Test 4', imgUrl: 'https://images.pexels.com/photos/459225/pexels-photo-459225.jpeg'},
+								{url: '123', title: 'Test 5'},
+								{url: '123', title: 'Test 6'},
+								{url: '123', title: 'Test 1Test 1Test 1Test 1Test 1Test 1Test 11Test 1Test 11Test 1Test 1dgdsgdfg fdg dfgdfg dfg dfg'},
+								{url: '123', title: 'Test 2', imgUrl: 'https://www.google.com.eg/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png'},
+								{url: '123', title: 'Test 3', changedBy: 'Ashraf Teleb', lastModifiedOn: 'Yesterday'}
+							]);
+							console.log(username);
+						}, 1000);
+					}, function(str, callback, username)
+					{
+						setTimeout(function(){
+							callback(username? [
+								{url: '123', title: str +'Test 1Test 1Test 1Test 1Test 1Test 1Test 1'},
+								{url: '123', title: str +'Test 2'},
+								{url: '123', title: str +'Test 3'},
+								{url: '123', title: str +'Test 4'},
+								{url: '123', title: str +'Test 5'},
+								{url: '123', title: str +'Test 6'}
+							]: [
+								{url: '123', title: str +'Test 5'},
+								{url: '123', title: str +'Test 6'},
+								{url: '123', title: str +'Test 1Test 1Test 1Test 1Test 1Test 1Test 1'},
+								{url: '123', title: str +'Test 2'},
+								{url: '123', title: str +'Test 3'},
+								{url: '123', title: str +'Test 4'}
+							]);
+						}, 2000);						
+					}, null);
+				});
+				this.addMenuItem(menu, 'templates', parent);
+			}
 		})));
 
 		this.put('file', new Menu(mxUtils.bind(this, function(menu, parent)

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


+ 2 - 2
src/main/webapp/js/mxgraph/Editor.js

@@ -725,7 +725,7 @@ OpenFile.prototype.cancel = function(cancel)
 /**
  * Basic dialogs that are available in the viewer (print dialog).
  */
-function Dialog(editorUi, elt, w, h, modal, closable, onClose, noScroll)
+function Dialog(editorUi, elt, w, h, modal, closable, onClose, noScroll, transparent)
 {
 	var dx = 0;
 	
@@ -791,7 +791,7 @@ function Dialog(editorUi, elt, w, h, modal, closable, onClose, noScroll)
 		document.body.appendChild(this.bg);
 	}
 	
-	var div = editorUi.createDiv('geDialog');
+	var div = editorUi.createDiv(transparent? 'geTransDialog' : 'geDialog');
 	var pos = this.getPosition(left, top, w, h);
 	left = pos.x;
 	top = pos.y;

+ 2 - 2
src/main/webapp/js/mxgraph/EditorUi.js

@@ -3280,7 +3280,7 @@ EditorUi.prototype.addSplitHandler = function(elt, horizontal, dx, onChange)
 /**
  * Displays a print dialog.
  */
-EditorUi.prototype.showDialog = function(elt, w, h, modal, closable, onClose, noScroll)
+EditorUi.prototype.showDialog = function(elt, w, h, modal, closable, onClose, noScroll, trasparent)
 {
 	this.editor.graph.tooltipHandler.hideTooltip();
 	
@@ -3289,7 +3289,7 @@ EditorUi.prototype.showDialog = function(elt, w, h, modal, closable, onClose, no
 		this.dialogs = [];
 	}
 	
-	this.dialog = new Dialog(this, elt, w, h, modal, closable, onClose, noScroll);
+	this.dialog = new Dialog(this, elt, w, h, modal, closable, onClose, noScroll, trasparent);
 	this.dialogs.push(this.dialog);
 };
 

+ 10 - 17
src/main/webapp/js/mxgraph/Graph.js

@@ -2285,11 +2285,6 @@ Graph.prototype.connectVertex = function(source, direction, length, evt, forceCl
 		
 		if (edge != null)
 		{
-			// Uses elbow edges with vertical or horizontal direction
-//			var elbowValue = (direction == mxConstants.DIRECTION_NORTH || direction == mxConstants.DIRECTION_SOUTH) ? 'vertical' : 'horizontal';
-//			edge.style = mxUtils.setStyle(edge.style, 'edgeStyle', 'elbowEdgeStyle');
-//			edge.style = mxUtils.setStyle(edge.style, 'elbow', elbowValue);
-//			edge.style = mxUtils.setStyle(edge.style, 'sourcePortConstraint', direction);
 			result.push(edge);
 		}
 		
@@ -3438,18 +3433,16 @@ HoverIcons.prototype.drag = function(evt, x, y)
 			handler.setHandlesVisible(false);
 		}
 		
-		// Uses elbow edges with vertical or horizontal direction
-//		var direction = this.getDirection();
-//		var es = this.graph.connectionHandler.edgeState;
-//		es.cell.style = mxUtils.setStyle(es.cell.style, 'sourcePortConstraint', direction);
-//		es.style['sourcePortConstraint'] = direction;
-//		var elbowValue = (direction == mxConstants.DIRECTION_NORTH || direction == mxConstants.DIRECTION_SOUTH) ? 'vertical' : 'horizontal';
-//		
-//		var es = this.graph.connectionHandler.edgeState;
-//		es.style['edgeStyle'] = 'elbowEdgeStyle';
-//		es.style['elbow'] = elbowValue;
-//		es.cell.style = mxUtils.setStyle(es.cell.style, 'edgeStyle', es.style['edgeStyle']);
-//		es.cell.style = mxUtils.setStyle(es.cell.style, 'elbow', es.style['elbow']);
+		// Ctrl+shift drag sets source constraint
+		var es = this.graph.connectionHandler.edgeState;
+
+		if (evt != null && mxEvent.isShiftDown(evt) && mxEvent.isControlDown(evt) && es != null &&
+			mxUtils.getValue(es.style, mxConstants.STYLE_EDGE, null) === 'orthogonalEdgeStyle')
+		{
+			var direction = this.getDirection();
+			es.cell.style = mxUtils.setStyle(es.cell.style, 'sourcePortConstraint', direction);
+			es.style['sourcePortConstraint'] = direction;
+		}
 	}
 };
 

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


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


src/main/webapp/images/icon-flowchart.svg → src/main/webapp/newDiagramCats/images/icon-flowchart.svg


src/main/webapp/images/icon-mind-map.svg → src/main/webapp/newDiagramCats/images/icon-mind-map.svg


src/main/webapp/images/icon-network.svg → src/main/webapp/newDiagramCats/images/icon-network.svg


src/main/webapp/images/icon-plus.svg → src/main/webapp/newDiagramCats/images/icon-plus.svg


src/main/webapp/images/icon-uml-erd.svg → src/main/webapp/newDiagramCats/images/icon-uml-erd.svg


+ 9 - 0
src/main/webapp/newDiagramCats/index.xml

@@ -0,0 +1,9 @@
+<categories>
+	<category img="images/icon-plus.svg" title="basic" libs="general"/>
+	<category img="images/icon-flowchart.svg" title="flowcharts" libs="general;flowchart"/>
+	<category img="images/icon-uml-erd.svg" title="uml" libs="general;uml"/>
+	<category img="images/icon-network.svg" title="network" libs="general;network"/>
+	<category img="images/icon-mind-map.svg" title="maps" libs="general"/>
+		<category img="images/icon-network.svg" title="network" libs="general;network"/>
+	<category img="images/icon-mind-map.svg" title="maps" libs="general"/>
+</categories>

+ 485 - 0
src/main/webapp/styles/grapheditor.css

@@ -449,6 +449,14 @@ div.mxWindow .geButton {
 	_filter:progid:DXImageTransform.Microsoft.DropShadow(OffX=2, OffY=2, Color='#d5d5d5', Positive='true');
 	z-index: 2;
 }
+
+.geTransDialog {
+	position:absolute;
+	overflow:hidden;
+	padding:30px;
+	z-index: 2;
+}
+
 .geDialogClose {
 	position:absolute;
 	width:9px;
@@ -774,3 +782,480 @@ html td.mxWindowTitle {
   border: 1px solid red;
 }
 
+/* Templates dialog css */
+.templateDlg {
+	width: 987px;
+	height: 712px;
+}
+.templateDlg ::-webkit-scrollbar {
+    width:12px;
+    height:12px;
+}
+.templateDlg ::-webkit-scrollbar-track {
+	background:whiteSmoke;
+	-webkit-box-shadow:inset 0 0 4px rgba(0,0,0,0.1);
+}
+.templateDlg ::-webkit-scrollbar-thumb {
+	background:#c5c5c5;
+    border-radius:10px;
+	border:whiteSmoke solid 3px;
+}
+.templateDlg ::-webkit-scrollbar-thumb:hover {
+	background:#b5b5b5;
+}
+
+.tempDlgHeader {
+	box-sizing: border-box;
+	height: 62px;
+	width: 100%;
+	border: 1px solid #CCCCCC;
+	border-radius: 5px 5px 0 0;
+	background-color: #F5F5F5;
+}
+.headerLogo {
+	height: 34px;
+	margin: 14px 14px 14px 20px;
+}
+.searchBox {
+    color:#888888;
+    background:url("/images/icon-search.svg") no-repeat;
+	background-color: #FFFFFF;
+	background-position: 15px;
+	height: 40px;
+	width: 40%;
+	max-width: 400px;
+	border: 1px solid #CCCCCC;
+	border-radius: 3px;
+    float:right;
+    font-family:Arial,Helvetica,sans-serif;
+    font-size:15px;
+    line-height:36px;
+    margin: 11px 36px 0 0;
+    outline:medium none;
+    padding:0 0 0 36px;
+    text-shadow:1px 1px 0 white;
+}
+.templatesList {
+	box-sizing: border-box;
+	float: left;
+	height: calc(100% - 118px);
+	width: 20%;
+	border: 1px solid #CCCCCC;
+	background-color: #FFFFFF;
+	display: inline-block;
+	overflow-x: hidden;
+	overflow-y: auto;
+}
+.tempDlgContent {
+	box-sizing: border-box;
+	float: right;
+	height: calc(100% - 118px);
+	width: 80%;
+	border: 1px solid #CCCCCC;
+	background-color: #FFFFFF;
+	display: inline-block;
+	overflow-x: hidden;
+	overflow-y: auto;
+	position: relative;
+}
+.tempDlgFooter {
+	box-sizing: border-box;
+	height: 52px;
+	width: 100%;
+	border: 1px solid #CCCCCC;
+	border-radius: 0 0 5px 5px;
+	background-color: #F5F5F5;
+	text-align: right;
+	font-family: Helvetica;
+	font-size: 14px;
+	line-height: 17px;
+	padding-top: 11px;
+}
+.createBtn {
+	display: inline-block;
+	width: 67px;
+    border-radius: 3px;
+    background-color: #3D72AD;
+    padding: 6px;
+    text-align: center;
+    color: #fff;
+    cursor: pointer;
+}
+.cancelBtn {
+	display: inline-block;
+	width: 67px;
+    padding: 6px;
+    text-align: center;
+    color: #3D72AD;
+    cursor: pointer;
+}
+.cancelBtn:active, .createBtn:active, .showAllBtn:active {
+	transform: translateY(2px);
+}
+.createBtnDisabled {
+    background-color: #9fbddd;
+}
+.createBtnDisabled:active {
+    transform: translateY(0px);
+}
+
+.createBtnBusy {
+	background-image: url(/images/aui-wait.gif);
+    background-repeat: no-repeat;
+    background-position: 62px 7px;
+}
+
+
+.newDiagramlbl {
+	height: 17px;
+	color: #333333;
+	font-family: Helvetica;
+	font-size: 14px;
+	font-weight: bold;
+	line-height: 17px;
+	padding: 25px 0 0 20px;
+	cursor: pointer;
+}
+.hLine {
+	height: 1px;
+	width: calc(100% - 22px);
+	background-color: #CCCCCC;
+	margin: 20px 0 0 11px;
+}
+.templatesLbl {
+	height: 17px;
+	color: #6D6D6D;
+	font-family: Helvetica;
+	font-size: 14px;
+	font-weight: bold;
+	line-height: 17px;
+	text-transform: uppercase;
+	margin: 20px 0 3px 20px;
+}
+.templateCatLink {
+	height: 17px;
+	color: #3D72AD;
+	font-family: Helvetica;
+	font-size: 14px;
+	line-height: 17px;
+	margin: 12px 0 0 20px;
+	cursor: pointer;
+}
+.newDiagramCat {
+	height: 280px;
+	width: 100%;
+	background-color: #555555;
+}
+.newDiagramCatLbl {
+	height: 17px;
+	color: #FFFFFF;
+	font-family: Helvetica;
+	font-size: 14px;
+	font-weight: bold;
+	line-height: 17px;
+	padding: 25px 0 0 20px;
+	text-transform: uppercase;
+}
+.newDiagramCatList {
+	width: 100%;
+	height: 190px;
+	padding-left: 9px;
+	box-sizing: border-box;
+	overflow-y: auto;
+	overflow-x: hidden; 
+}
+.newDiagramCatFooter {
+	width: 100%;
+}
+.showAllBtn {
+	width: 78px;
+	border: 1px solid #777777;
+	border-radius: 3px;
+	cursor: pointer;
+	text-align: center;
+	color: #DDDDDD;
+	font-family: Helvetica;
+	font-size: 14px;
+	line-height: 17px;
+	padding: 4px;
+	float: right;
+	margin-right: 30px;
+}
+.newDiagramCatItem {
+	height: 155px;
+	width: 134px;
+	padding: 18px 6px 0 9px;
+	display: inline-block;
+}
+
+.newDiagramCatItemImg {
+	box-sizing: border-box;
+	height: 134px;
+	width: 134px;
+	border: 1px solid #CCCCCC;
+	border-radius: 3px;
+	background-color: #FFFFFF;
+	display:table-cell;
+	vertical-align:middle;
+	text-align:center;
+	cursor: pointer;
+}
+
+.newDiagramCatItemActive > .newDiagramCatItemImg {
+	border: 4px solid #3D72AD;
+}
+
+.newDiagramCatItemLbl {
+	height: 17px;
+	width: 100%;
+	color: #FFFFFF;
+	font-family: Helvetica;
+	font-size: 14px;
+	line-height: 17px;
+	text-align: center;
+	padding-top: 4px;
+	cursor: pointer;
+}
+
+.diagramsList {
+	box-sizing: border-box;
+	width: 100%;
+	min-height: calc(100% - 280px);
+	padding-left: 9px;
+	box-sizing: border-box;
+	background-color: #E5E5E5;
+}
+
+.diagramsListHeader {
+	width: 100%;
+    height: 45px;
+	padding: 18px 20px 0 11px;
+	box-sizing: border-box;
+}
+.diagramsListTitle {
+	box-sizing: border-box;
+	height: 17px;
+	color: #666666;
+	font-family: Helvetica;
+	font-size: 14px;
+	font-weight: bold;
+	line-height: 17px;
+	text-transform: uppercase;
+	padding-top: 5px;
+	display: inline-block;
+}
+.diagramsListBtns {
+	float: right;
+	margin-top: -9px;
+}		
+.radioBtn {
+	box-sizing: border-box;
+	border: 1px solid #CCCCCC;
+	border-radius: 3px;
+	background-color: #555555;
+	display: inline-block;
+	color: #FFFFFF;
+	font-family: Helvetica;
+	font-size: 14px;
+	line-height: 17px;
+	text-align: center;
+	padding: 4px;
+	cursor: pointer;
+}
+.radioBtnActive {
+	background-color: #FFFFFF;
+	color: #333333;
+}
+.radioBtnLarge {
+	height: 27px;
+	width: 120px;
+}
+/* TODO is there a better way for these buttons */
+.radioBtnSmall {
+	position: relative;
+	top: 9px;
+	height: 27px;
+	width: 27px;
+}
+.radioBtnSmall img {
+	position: absolute;
+	top: 6px;
+	left: 6px;
+	height: 13px;
+	width: 13px;
+}
+.spacer {
+    display: inline-block;
+	width: 10px;
+}
+
+.diagramsListGrid {
+	width: 100%;
+	white-space: nowrap;
+	font-size: 13px;
+	padding: 0px 20px 20px 10px;
+    box-sizing: border-box;
+    border-spacing: 0;
+}
+
+.diagramsListGrid tr {
+	height: 40px;
+}
+
+.diagramsListGrid th {
+	background-color: #E5E5E5;
+	color: #8E8E8E;
+	font-weight: bold;
+	text-align: left;
+	padding: 5px;
+	border-bottom: 1px solid #CCCCCC;
+	font-size: 14px;
+}
+
+.diagramsListGrid td {
+	background-color: #FFFFFF;
+	color: #888888;
+	padding: 5px;
+	border-bottom: 1px solid #CCCCCC;
+	overflow: hidden;
+}
+
+.diagramsListGridActive td {
+	border-bottom: 2px solid #3D72AD;
+	border-top: 2px solid #3D72AD;
+}
+
+.diagramsListGridActive td:first-child  {
+	border-left: 2px solid #3D72AD;
+}
+
+.diagramsListGridActive td:last-child  {
+	border-right: 2px solid #3D72AD;
+}
+
+.diagramTitle {
+	font-weight: bold;
+	color: #666666 !important;
+}
+
+.diagramsTiles {
+	position: relative;
+	min-height: 100px;
+}
+
+.diagramTile {
+	height: 152px;
+	width: 130px;
+	padding: 20px 7px 0 10px;
+	display: inline-block;
+	position: relative;
+}
+
+.diagramTileImg {
+	box-sizing: border-box;
+	height: 130px;
+	width: 130px;
+	border: 1px solid #CCCCCC;
+	border-radius: 3px;
+	background-color: #FFFFFF;
+	display:table-cell;
+	vertical-align:middle;
+	text-align:center;
+}
+
+.diagramTileImgLoading {
+	background-image: url(/images/aui-wait.gif);
+    background-repeat: no-repeat;
+    background-position: center;
+}
+
+.diagramTileImgError {
+	background-image: url(/images/broken.png);
+    background-repeat: no-repeat;
+    background-position: center;
+    background-color: #be3730;
+}
+
+.diagramTileImg img{
+	max-width: 117px;
+    max-height: 117px;
+    cursor: pointer;
+}
+
+.diagramTileActive > .diagramTileImg{
+	border: 4px solid #3D72AD;
+}
+
+.diagramTileLbl {
+	height: 17px;
+	width: 100%;
+	color: #333333;
+	font-family: Helvetica;
+	font-size: 14px;
+	line-height: 17px;
+	text-align: center;
+	padding-top: 5px;
+	cursor: pointer;
+}
+
+.diagramPreviewBtn {
+	position: absolute;
+	top: 28px;
+	right: 15px;
+	cursor: pointer;
+}
+
+.diagramListPreviewBtn {
+	cursor: pointer;
+	padding-left: 5px;
+	padding-right: 15px;
+}
+
+.diagramPreviewBox {
+	position: absolute;
+    top: 3%;
+    left: 10%;
+    width: 80%;
+    height: 94%;
+    background: white;
+    border: 4px solid #3D72AD;
+    border-radius: 6px;
+    box-sizing: border-box;
+	display:table-cell;
+	vertical-align:middle;
+	text-align:center;
+    z-index: 2;
+}
+
+.dialogMask {
+	position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    z-index: 1;
+}
+
+.diagramPreviewBox img {
+	max-width: 95%;
+    max-height: 95%;
+    vertical-align: middle;
+}
+
+.previewCloseBtn {
+	position: absolute;
+	top: 5px;
+	right: 5px;
+	cursor: pointer;
+}
+
+.linkToDiagramHint {
+	color: #555;
+}
+
+.linkToDiagramBtn {
+	color: #555;
+    margin: 0 10px 0 10px;
+    height: 27px;
+    font-size: 14px;
+}