瀏覽代碼

5.7.0.8.2 release

Former-commit-id: 5ad7d27418361ae30064f04c5146bba425acd430
David Benson 8 年之前
父節點
當前提交
f9cb415722

+ 4 - 0
ChangeLog

@@ -1,3 +1,7 @@
+03-SEP-2016: 5.7.0.8.2
+
+- Adds tolerance, uses fewer cells in scissors tool
+
 27-SEP-2016: 5.7.0.7
 
 - Adds filename for editors in Atlassian cloud plugins

+ 1 - 1
VERSION

@@ -1 +1 @@
-5.7.0.7
+5.7.0.8.2

+ 73 - 51
src/com/mxgraph/io/gliffy/importer/GliffyDiagramConverter.java

@@ -79,15 +79,16 @@ public class GliffyDiagramConverter {
 		this.gliffyDiagram = new GsonBuilder().create().fromJson(diagramString, Diagram.class);
 
 		collectVerticesAndConvert(vertices, gliffyDiagram.stage.getObjects(), null);
+		
+		//sort objects by the order specified in the Gliffy diagram
+		sortObjectsByOrder(gliffyDiagram.stage.getObjects());
 
 		drawioDiagram.getModel().beginUpdate();
 
 		try {
-			// sort objects by the order specified in the Gliffy diagram
-			sortObjectsByOrder(gliffyDiagram.stage.getObjects());
 
 			for (Object obj : gliffyDiagram.stage.getObjects()) {
-				importObject(obj, null);
+				importObject(obj, obj.parent);
 			}
 
 		} finally {
@@ -118,24 +119,26 @@ public class GliffyDiagramConverter {
 	}
 
 	/**
-	 * Imports the objects into the draw.io diagram. Recursively adds the
-	 * children of groups and swimlanes
-	 * 
+	 * Imports the objects into the draw.io diagram. Recursively adds the children 
 	 */
-	private void importObject(Object obj, mxCell parent) {
+	private void importObject(Object obj, Object gliffyParent) {
+		
+		mxCell parent = gliffyParent != null ? gliffyParent.mxObject : null;
 		
-		if (obj.isGroup() || obj.isMindmap() || obj.isShape() || obj.isText() || obj.isImage() || obj.isSwimlane() || obj.isSvg()) {
+		if (!obj.isLine()) {
 			drawioDiagram.addCell(obj.mxObject, parent);
 
-			if (obj.isGroup() || obj.isSwimlane()) {
+			if (obj.hasChildren()) {
 				if (!obj.isSwimlane())// sort the children except for swimlanes, // their order value is "auto"
 					sortObjectsByOrder(obj.children);
 
-				for (Object go : obj.children) {
-					importObject(go, go.parent.mxObject);
+				for (Object child : obj.children) {
+					//do not import text as a child object, use inline text
+					if(!child.isText())
+						importObject(child, obj);
 				}
 			}
-		} else if(obj.isLine()) {
+		} else {
 			// gets the terminal cells for the edge
 			mxCell startTerminal = getTerminalCell(obj, true);
 			mxCell endTerminal = getTerminalCell(obj, false);
@@ -144,10 +147,6 @@ public class GliffyDiagramConverter {
 
 			applyControlPoints(obj, startTerminal, endTerminal);
 		}
-		else 
-		{
-			logger.warning("Unrecognized object, uid : " + obj.uid);
-		}
 	}
 
 	private void sortObjectsByOrder(Collection<Object> values) {
@@ -225,17 +224,19 @@ public class GliffyDiagramConverter {
 	 * up terminal cells for edges
 	 */
 	private void collectVerticesAndConvert(Map<Integer, Object> vertices, Collection<Object> objects, Object parent) {
+		
 		for (Object object : objects) {
-			object.mxObject = convertGliffyObject(object, null);
+			
 			object.parent = parent;
-
-			if (object.isGroup())// only do this recursively for groups, swimlanes have children w/o uid
-			{
+			
+			convertGliffyObject(object, parent);
+			
+			if(!object.isLine())
 				vertices.put(object.id, object);
+			
+			// don't collect for swimlanes and mindmaps, their children are treated differently
+			if (object.hasChildren() && !object.isSwimlane() && !object.isMindmap())
 				collectVerticesAndConvert(vertices, object.children, object);
-			} else if (object.isShape() || object.isText() || object.isImage() || object.isSwimlane() || object.isSvg() || object.isMindmap()) {
-				vertices.put(object.id, object);
-			} 
 		}
 	}
 
@@ -261,9 +262,9 @@ public class GliffyDiagramConverter {
 	 * 
 	 * 
 	 */
-	private mxCell convertGliffyObject(Object gliffyObject, mxCell parent) {
+	private mxCell convertGliffyObject(Object gliffyObject, Object parent) {
 		mxCell cell = new mxCell();
-		cell.setParent(parent);
+		
 		StringBuilder style = new StringBuilder();
 
 		mxGeometry geometry = new mxGeometry((int) gliffyObject.x, (int) gliffyObject.y, (int) gliffyObject.width, (int) gliffyObject.height);
@@ -279,12 +280,11 @@ public class GliffyDiagramConverter {
 		}
 
 		String text = null;
-		Object textObject = null;
+		Object textObject = gliffyObject.getTextObject();
 		
 		String link = null;
 
 		if (graphic != null) {
-			textObject = gliffyObject.getTextObject();
 			link = gliffyObject.getLink();
 
 			if (gliffyObject.isShape()) {
@@ -305,11 +305,10 @@ public class GliffyDiagramConverter {
 					style.append("opacity=" + shape.opacity * 100).append(";");
 
 				style.append(DashStyleMapping.get(shape.dashStyle));
-				style.append("whiteSpace=wrap;");
-				text = gliffyObject.getTextRecursively();
 
 			} else if (gliffyObject.isLine()) {
-				GliffyLine line = graphic.getLine();
+				GliffyLine line = graphic.Line;
+				
 				cell.setEdge(true);
 				style.append("strokeWidth=" + line.strokeWidth).append(";");
 				style.append("strokeColor=" + line.strokeColor).append(";");
@@ -320,20 +319,27 @@ public class GliffyDiagramConverter {
 
 				geometry.setX(0);
 				geometry.setY(0);
-
-				text = gliffyObject.getText();
+				
 			} else if (gliffyObject.isText()) {
+				
 				textObject = gliffyObject;
 				cell.setVertex(true);
-				style.append("text;whiteSpace=wrap;");
-				text = gliffyObject.getText();
+				style.append("text;whiteSpace=wrap;html=1;nl2Br=0;");
+				cell.setValue(gliffyObject.getText());
+				
+				//if text is a child of a cell, use relative geometry and set X and Y to 0
+				if(gliffyObject.parent != null && !gliffyObject.parent.isGroup()) 
+				{
+					mxGeometry parentGeometry = gliffyObject.parent.mxObject.getGeometry();
+					cell.setGeometry(new mxGeometry(0, 0, parentGeometry.getWidth(), parentGeometry.getHeight()));
+					cell.getGeometry().setRelative(true);
+				}
+				
 			} else if (gliffyObject.isImage()) {
 				GliffyImage image = graphic.getImage();
 				cell.setVertex(true);
 				style.append("shape=" + StencilTranslator.translate(gliffyObject.uid)).append(";");
 				style.append("image=" + image.getUrl()).append(";");
-
-				text = gliffyObject.getText();
 			}
 			else if (gliffyObject.isSvg()) {
 				GliffySvg svg = graphic.Svg;
@@ -350,7 +356,7 @@ public class GliffyDiagramConverter {
 			style.append(StencilTranslator.translate(gliffyObject.uid)).append(";");
 
 			boolean vertical = true;
-			gliffyObject.rotation = 0;
+			
 			if (gliffyObject.uid.startsWith(Object.H_SWIMLANE)) {
 				vertical = false;
 				cell.getGeometry().setWidth(gliffyObject.height);
@@ -359,6 +365,8 @@ public class GliffyDiagramConverter {
 			}
 
 			Object header = gliffyObject.children.get(0);// first child is the header of the swimlane
+			Object headerText = header.children.get(0);
+			
 			gliffyObject.children.remove(header);
 
 			GliffyShape shape = header.graphic.getShape();
@@ -368,7 +376,7 @@ public class GliffyDiagramConverter {
 			style.append("strokeColor=" + shape.strokeColor).append(";");
 			style.append("whiteSpace=wrap;");
 
-			text = header.getText();
+			text = headerText.getText();
 
 			for (int i = 0; i < gliffyObject.children.size(); i++) // rest of the children are lanes
 			{
@@ -387,15 +395,27 @@ public class GliffyDiagramConverter {
 				mxCell mxLane = new mxCell();
 				mxLane.setVertex(true);
 				cell.insert(mxLane);
-				mxLane.setValue(gLane.getText());
+				mxLane.setValue(gLane.children.get(0).getText());
 				mxLane.setStyle(laneStyle.toString());
 				mxGeometry childGeometry = new mxGeometry(gLane.x, gLane.y, vertical ? gLane.width : gLane.height, vertical ? gLane.height : gLane.width);
 				mxLane.setGeometry(childGeometry);
 				gLane.mxObject = mxLane;
 			}
-		} else if (gliffyObject.isMindmap()) {
-			Object child = gliffyObject.children.get(0);
-			GliffyMindmap mindmap = child.graphic.Mindmap;
+		}
+		/* Gliffy mindmap objects have a 3 level hierarchy
+		 * 
+		 * 	mindmap
+		 * 	rectangle
+		 * 	text
+		 * 
+		 * 	Since mindmap object already converts to rectangle, rectangle object is removed and text object is put in it's place 
+		 * 
+		 */	
+		else if (gliffyObject.isMindmap()) {
+			Object rectangle = gliffyObject.children.get(0);
+			Object textObj = rectangle.children.get(0);
+			
+			GliffyMindmap mindmap = rectangle.graphic.Mindmap;
 			
 			style.append("shape=" + StencilTranslator.translate(gliffyObject.uid)).append(";");
 			style.append("shadow=" + (mindmap.dropShadow ? 1 : 0)).append(";");
@@ -409,7 +429,12 @@ public class GliffyDiagramConverter {
 
 			cell.setVertex(true);
 			
-			text = child.getTextRecursively();
+			mxCell textObjMx = convertGliffyObject(textObj, gliffyObject);
+			textObjMx.setGeometry(new mxGeometry(0, 0, gliffyObject.width, gliffyObject.height));
+			textObjMx.getGeometry().setRelative(true);
+			
+			//sets the grandchild as a child 
+			gliffyObject.children.set(0, textObj);
 		}
 
 		if (gliffyObject.rotation != 0) {
@@ -419,9 +444,11 @@ public class GliffyDiagramConverter {
 		if (!gliffyObject.isLine() && textObject != null) {
 			style.append(textObject.graphic.getText().getStyle());
 		}
-
-		if (text != null && !text.equals("")) {
-			style.append("html=1;nl2Br=0;");// nl2Br=0 stops newline from becoming <br>
+		
+		if(textObject != null) 
+		{
+			cell.setValue(textObject.getText());
+			style.append("html=1;nl2Br=0;whiteSpace=wrap");
 		}
 		
 		if(link != null) 
@@ -434,15 +461,10 @@ public class GliffyDiagramConverter {
 			if(text != null && !text.equals(""))
 				uo.setAttribute("label", text);
 		}
-		else if(text != null && !text.equals("")) 
-		{
-			cell.setValue(text);
-		}
 
 		cell.setStyle(style.toString());
 		gliffyObject.mxObject = cell;
 
 		return cell;
 	}
-
 }

+ 32 - 34
src/com/mxgraph/io/gliffy/model/GliffyText.java

@@ -9,6 +9,9 @@ public class GliffyText
 	private String html;
 
 	private String valign;
+	
+	//extracted from html
+	private String halign;
 
 	private String vposition;
 
@@ -28,8 +31,7 @@ public class GliffyText
 
 	private static Pattern pattern = Pattern.compile("<p(.*?)<\\/p>");
 
-	private static Pattern textAlignPattern = Pattern.compile(
-			".*text-align: ?(left|center|right).*", Pattern.DOTALL);
+	private static Pattern textAlign = Pattern.compile(".*(text-align: ?(left|center|right);).*", Pattern.DOTALL);
 
 	public GliffyText()
 	{
@@ -37,50 +39,40 @@ public class GliffyText
 
 	public String getHtml()
 	{
+		halign = halign == null ? getHorizontalTextAlignment() : halign;
 		return replaceParagraphWithDiv(html);
 	}
 
+	//this is never invoked by Gson builder
 	public void setHtml(String html)
 	{
-		this.html = html;
 	}
 
 	public String getStyle()
 	{
 		StringBuilder sb = new StringBuilder();
 
+		//vertical label position
 		if (vposition.equals("above"))
-		{
-			sb.append("verticalLabelPosition=top;").append(
-					"verticalAlign=bottom;");
-		}
+			sb.append("verticalLabelPosition=top;");
 		else if (vposition.equals("below"))
-		{
-			sb.append("verticalLabelPosition=bottom;").append(
-					"verticalAlign=top;");
-		}
+			sb.append("verticalLabelPosition=bottom;");
 		else if (vposition.equals("none"))
-		{
-			sb.append("verticalAlign=").append(valign).append(";");
-		}
-
-		if (hposition.equals("left"))
-		{
-			sb.append("labelPosition=left;").append("align=right;");
-		}
-		else if (hposition.equals("right"))
-		{
-			sb.append("labelPosition=right;").append("align=left;");
-		}
-		else if (hposition.equals("none"))
-		{
-			String hAlign = getHorizontalTextAlignment();
-			if (hAlign != null)
-			{
-				sb.append("align=").append(hAlign).append(";");
-			}
-		}
-
+			sb.append("verticalLabelPosition=middle;");
+		
+		//vertical label align
+		sb.append("verticalAlign=").append(valign).append(";");
+		
+		//horizontal label position
+		if (hposition.equals("none"))
+			sb.append("labelPosition=center;");
+		else 
+			sb.append("labelPosition=").append(hposition).append(";");
+		
+		//horizontal label align
+		if (halign != null)
+			sb.append("align=").append(halign).append(";");
+		
 		sb.append("spacingLeft=").append(paddingLeft).append(";");
 		sb.append("spacingRight=").append(paddingRight).append(";");
 		sb.append("spacingTop=").append(paddingTop).append(";");
@@ -101,13 +93,19 @@ public class GliffyText
 		return sb.length() > 0 ? sb.toString() : html;
 	}
 
+	/**
+	 * Extracts horizontal text alignment from html and removes it
+	 * so it does not interfere with alignment set in mxCell style
+	 * @return horizontal text alignment or null if there is none
+	 */
 	private String getHorizontalTextAlignment()
 	{
-		Matcher m = textAlignPattern.matcher(html);
+		Matcher m = textAlign.matcher(html);
 
 		if (m.matches())
 		{
-			return m.group(1);
+			html = html.replaceAll("text-align: ?\\w*;", "");
+			return m.group(2);
 		}
 
 		return null;

+ 1 - 40
src/com/mxgraph/io/gliffy/model/Object.java

@@ -180,48 +180,9 @@ public class Object
 		return null;
 	}
 
-	public String getTextRecursively()
-	{
-		StringBuilder sb = new StringBuilder();
-
-		List<Object> textObjs = new ArrayList<Object>();
-		getTextObjects(this, textObjs);
-
-		Iterator<Object> it = textObjs.iterator();
-
-		while (it.hasNext())
-		{
-			Object to = it.next();
-			sb.append(to.graphic.getText().getHtml());
-			if (it.hasNext())
-				sb.append("<hr>");
-		}
-
-		return sb.toString();
-	}
-
 	public String getText()
 	{
-		if (isText())
-			return graphic.getText().getHtml();
-
-		Object to = getTextObject();
-
-		return to != null ? to.graphic.getText().getHtml() : null;
-
-	}
-
-	private void getTextObjects(Object obj, List<Object> objects)
-	{
-		if (obj.isText())
-			objects.add(obj);
-		else if (obj.children != null)
-		{
-			for (Object ob : obj.children)
-			{
-				getTextObjects(ob, objects);
-			}
-		}
+		return graphic.getText().getHtml();
 	}
 	
 	public String getLink() 

+ 0 - 113
src/com/mxgraph/online/IconSearchServlet.java

@@ -1,113 +0,0 @@
-/**
- * Copyright (c) 2006-2016, JGraph Ltd
- * Copyright (c) 2006-2016, Gaudenz Alder
- */
-package com.mxgraph.online;
-
-import java.io.IOException;
-import java.net.URL;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * Servlet implementation class OpenServlet
- */
-public class IconSearchServlet extends HttpServlet
-{
-	/**
-	 * 
-	 */
-	private static final long serialVersionUID = 1L;
-
-	/**
-	 * Path component under war/ to locate iconfinder_key file.
-	 */
-	public static final String API_KEY_FILE_PATH = "/WEB-INF/iconfinder_key";
-
-	/**
-	 * API key for iconfinder.
-	 */
-	public static String API_KEY = null;
-
-	private static final Logger log = Logger.getLogger(IconSearchServlet.class
-			.getName());
-
-	/**
-	 * @see HttpServlet#HttpServlet()
-	 */
-	public IconSearchServlet()
-	{
-		super();
-	}
-
-	/**
-	 * Loads the key.
-	 */
-	protected void updateKey()
-	{
-		if (API_KEY == null)
-		{
-			try
-			{
-				API_KEY = Utils.readInputStream(
-						getServletContext().getResourceAsStream(
-								getAPIKeyFilePath())).replaceAll("\n", "");
-			}
-			catch (IOException e)
-			{
-				throw new RuntimeException("API key file path invalid.");
-			}
-		}
-		
-		if (API_KEY.equals("Replace_with_your_own_iconfinder_key"))
-		{
-			throw new RuntimeException("Iconfinder API key template used, replace it with your own.");
-		}
-	}
-
-	public void doGet(HttpServletRequest request, HttpServletResponse response)
-	{
-		doPost(request, response);
-	}
-
-	public void doPost(HttpServletRequest request, HttpServletResponse response)
-	{
-		updateKey();
-
-		try
-		{
-			String query = request.getParameter("q");
-			URL url = new URL("https://www.iconfinder.com/xml/search/?q="
-					+ Utils.encodeURIComponent(query,
-							Utils.CHARSET_FOR_URL_ENCODING) + "&p="
-					+ request.getParameter("p") + "&c="
-					+ request.getParameter("c") + "&l="
-					+ request.getParameter("l")
-					+ "&price=nonpremium&min=4&max=130&api_key=" + API_KEY);
-
-			log.log(Level.CONFIG, "iconsearch=" + query);
-
-			response.addHeader("Access-Control-Allow-Origin", "*");
-			response.addHeader("Access-Control-Allow-Methods",
-					"POST, GET, OPTIONS, PUT, DELETE, HEAD");
-
-			Utils.copy(url.openStream(), response.getOutputStream());
-			response.getOutputStream().flush();
-			response.getOutputStream().close();
-		}
-		catch (Exception e)
-		{
-			System.out.println(e.getMessage());
-			e.printStackTrace();
-		}
-	}
-
-	protected String getAPIKeyFilePath()
-	{
-		return API_KEY_FILE_PATH;
-	}
-}

+ 11 - 2
src/com/mxgraph/online/OpenServlet.java

@@ -168,7 +168,7 @@ public class OpenServlet extends HttpServlet
 				{
 					// Creates a graph that contains a model but does not validate
 					// since that is not needed for the model and not allowed on GAE
-					mxGraph graph = new mxGraph()
+					mxGraph graph = new mxGraphHeadless()
 					{
 						public mxRectangle graphModelChanged(mxIGraphModel sender,
 								List<mxUndoableChange> changes)
@@ -176,12 +176,21 @@ public class OpenServlet extends HttpServlet
 							return null;
 						}
 					};
+
 					mxGraphMlCodec.decode(mxXmlUtils.parseXml(upfile), graph);
 					xml = mxXmlUtils.getXml(new mxCodec().encode(graph.getModel()));
 				}
 				else if (ENABLE_VDX_SUPPORT && (vdx || vsdx))
 				{
-					mxGraph graph = new mxGraphHeadless();
+					mxGraph graph = new mxGraphHeadless()
+					{
+						public mxRectangle graphModelChanged(mxIGraphModel sender,
+								List<mxUndoableChange> changes)
+						{
+							return null;
+						}
+					};
+
 					graph.setConstrainChildren(false);
 					mxVsdxCodec vdxCodec = new mxVsdxCodec();
 

+ 1 - 1
war/WEB-INF/appengine-web.xml

@@ -2,7 +2,7 @@
 <appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
 	<application>drawdotio</application>
 	<!-- IMPORTANT! DO NOT CHANGE THIS VALUE IN SOURCE CONTROL! -->
-	<version>5-7-0-7</version>
+	<version>5-7-0-8-2</version>
 	
 	<!-- Configure java.util.logging -->
 	<system-properties>

+ 1 - 1
war/cache.manifest

@@ -1,7 +1,7 @@
 CACHE MANIFEST
 
 # THIS FILE WAS GENERATED. DO NOT MODIFY!
-# 09/27/2016 11:52 AM
+# 10/03/2016 03:57 PM
 
 /app.html
 /index.html?offline=1

+ 6 - 1
war/electron.js

@@ -1,4 +1,9 @@
-const electron = require('electron')
+const electron = require('electron');
+const remote = electron.remote;
+const dialog = electron.dialog;
+var fs = require('fs');
+var path = require('path');
+
 // Module to control application life.
 const app = electron.app
 // Module to create native browser window.

二進制
war/images/sidebar-veeam.png


+ 17 - 4
war/index.html

@@ -29,7 +29,6 @@
 		 * - test=1: For developers only
 		 * - drawdev=1: For developers only
 		 * - export=URL for export: For developers only
-		 * - pages=1: For developers only
 		 * - page=n: For developers only
 		 * - ignoremime=1: For developers only (see DriveClient.js). Use Cmd-S to override mime.
 		 * - createindex=1: For developers only (see etc/build/README)
@@ -322,6 +321,12 @@
 			mxscript('js/app.min.js');
 		}
 
+		if (window && window.process && window.process.type)
+		{
+			// Electron
+			mxscript('js/diagramly/ElectronApp.js');
+		}
+		
 		// Adds basic error handling
 		window.onerror = function()
 		{
@@ -332,6 +337,7 @@
 				status.innerHTML = 'Page could not be loaded. Please try refreshing.';
 			}
 		};
+
 	</script>
 </head>
 <body class="geEditor">
@@ -370,12 +376,12 @@
 		<tr>
 			<td id="geFooterItem2">
 				<a title="HTML5 JavaScript Diagramming" target="_blank" href="https://github.com/jgraph/draw.io">
-					<img border="0" align="absmiddle" style="margin-top:-4px;padding-right:10px;"
-						src="images/glyphicons_star.png"/>Source code now on Github
+					<img border="0" align="absmiddle" style="margin-top:-2px;padding-right:14px;"
+						src="images/glyphicons_github.png"/>Fork us on Github
 				</a>
 			</td>
 			<td id="geFooterItem1">
-				<a title="#1 Rated Confluence Add-on" target="_blank"
+				<a id="geFooterLink1" title="#1 Rated Confluence Add-on" target="_blank"
 					href="https://marketplace.atlassian.com/plugins/com.mxgraph.confluence.plugins.diagramly/server/overview">
 					<img border="0" width="27" height="24" align="absmiddle" style="padding-right:16px;"
 						src="images/logo-confluence.png"/>#1 Rated Confluence Add-on
@@ -390,6 +396,13 @@
  */
 App.main();
 
+// Logs footer1 clicks
+document.getElementById("geFooterLink1").onclick = function()
+{
+	var img = new Image();
+	img.src = 'log?msg=geFooterLink1:' + '&v=' + encodeURIComponent(EditorUi.VERSION);
+}
+
 /**
  * Analytics
  */

File diff suppressed because it is too large
+ 197 - 62
war/js/app.min.js


File diff suppressed because it is too large
+ 38 - 38
war/js/atlas-viewer.min.js


File diff suppressed because it is too large
+ 194 - 59
war/js/atlas.min.js


+ 1 - 0
war/js/diagramly/Devel.js

@@ -56,6 +56,7 @@ mxscript(drawDevUrl + 'js/diagramly/sidebar/Sidebar-Office.js');
 mxscript(drawDevUrl + 'js/diagramly/sidebar/Sidebar-PID.js');
 mxscript(drawDevUrl + 'js/diagramly/sidebar/Sidebar-Rack.js');
 mxscript(drawDevUrl + 'js/diagramly/sidebar/Sidebar-Sysml.js');
+mxscript(drawDevUrl + 'js/diagramly/sidebar/Sidebar-Veeam.js');
 
 mxscript(drawDevUrl + 'js/diagramly/DrawioUser.js');
 mxscript(drawDevUrl + 'js/diagramly/DrawioFile.js');

+ 5 - 0
war/js/diagramly/EditorUi.js

@@ -14,6 +14,11 @@
 	 */
 	EditorUi.compactUi = uiTheme != 'atlas';
 
+	/**
+	 * Overrides compact UI setting.
+	 */
+	EditorUi.isElectronApp = window && window.process && window.process.type; // https://github.com/electron/electron/issues/2288
+
 	/**
 	 * 
 	 */

+ 448 - 0
war/js/diagramly/ElectronApp.js

@@ -0,0 +1,448 @@
+window.TEMPLATE_PATH = 'templates';
+
+(function()
+{
+	// Overrides default mode
+	App.mode = App.MODE_DEVICE;
+	
+	// Redirects printing to iframe to avoid document.write
+	PrintDialog.showPreview = function(preview, print)
+	{
+		var iframe = document.createElement('iframe');
+		document.body.appendChild(iframe);
+		
+		// Workaround for lost gradients in print output
+		var getBaseUrl = mxSvgCanvas2D.prototype.getBaseUrl;
+		
+		mxSvgCanvas2D.prototype.getBaseUrl = function()
+		{
+			return '';
+		};
+
+		// Renders print output into iframe and prints
+		var result = preview.open(null, iframe.contentWindow);
+		iframe.contentWindow.print();
+		iframe.parentNode.removeChild(iframe);
+		
+		mxSvgCanvas2D.prototype.getBaseUrl = getBaseUrl;
+		
+		return result;
+	};
+	
+	var menusInit = Menus.prototype.init;
+	Menus.prototype.init = function()
+	{
+		menusInit.apply(this, arguments);
+
+		var editorUi = this.editorUi;
+		
+		editorUi.actions.addAction('online...', function()
+		{
+			window.open('https://www.draw.io/');
+		});
+
+		// Replaces file menu to replace openFrom menu with open and rename downloadAs to export
+		this.put('file', new Menu(mxUtils.bind(this, function(menu, parent)
+		{
+			this.addMenuItems(menu, ['new', 'open', '-', 'save', 'saveAs', '-', 'import'], parent);
+			this.addSubmenu('exportAs', menu, parent);
+			this.addSubmenu('embed', menu, parent);
+			this.addMenuItems(menu, ['-', 'newLibrary', 'openLibrary', '-', 'documentProperties', 'print'], parent);
+		})));
+		
+		this.put('extras', new Menu(mxUtils.bind(this, function(menu, parent)
+		{
+			this.addMenuItems(menu, ['copyConnect', 'collapseExpand', '-', 'gridColor', 'autosave', '-',
+			                         'createShape', 'editDiagram', '-', 'online'], parent);
+		})));
+	};
+	
+	// Uses local picker
+	App.prototype.pickFile = function()
+	{
+		this.chooseFileEntry(mxUtils.bind(this, function(fileEntry, data)
+		{
+			var file = new LocalFile(this, data, '');
+			file.fileObject = fileEntry;
+			this.fileLoaded(file);
+		}));
+	};
+	
+	/**
+	 * Selects a library to load from a picker
+	 * 
+	 * @param mode the device mode, ignored in this case
+	 */
+	App.prototype.pickLibrary = function(mode)
+	{
+		this.chooseFileEntry(mxUtils.bind(this, function(fileEntry, data)
+		{
+			var library = new LocalLibrary(this, data, fileEntry.name);
+			library.fileObject = fileEntry;
+			this.loadLibrary(library);
+		}));
+	};
+	
+	// Uses local picker
+	App.prototype.chooseFileEntry = function(fn)
+	{
+		const electron = require('electron');
+		var remote = electron.remote;
+		var dialog = remote.dialog;
+
+        var paths = dialog.showOpenDialog({properties: [ 'openFile' ] });
+	           
+        if (paths !== undefined && paths[0] != null)
+        {
+        	var fs = require('fs');
+        	var path = paths[0];
+        	var index = path.lastIndexOf('.png');
+        	var isPng = index > -1 && index == path.length - 4;
+        	var encoding = isPng ? 'base64' : 'utf-8'
+
+        	fs.readFile(path, encoding, mxUtils.bind(this, function (e, data)
+        	{
+        		if (e)
+        		{
+        			this.handleError(e);
+        		}
+        		else
+        		{
+        			if (isPng)
+        			{
+        				// Detecting png by extension. Would need https://github.com/mscdex/mmmagic
+        				// to do it by inspection
+        				data = this.extractGraphModelFromPng(data, true);
+        			}
+
+        			var fileEntry = new Object();
+        			fileEntry.path = path;
+        			fileEntry.name = path.replace(/^.*[\\\/]/, '');
+        			fileEntry.type = encoding;
+        			fn(fileEntry, data);
+        		}
+        	}));
+        }
+	};
+	
+	LocalFile.prototype.isAutosave = function()
+	{
+		return this.ui.editor.autosave;
+	};
+	
+	LocalFile.prototype.isAutosaveOptional = function()
+	{
+		return true;
+	};
+	
+	LocalLibrary.prototype.isAutosave = function()
+	{
+		return true;
+	};
+	
+	LocalFile.prototype.getTitle = function()
+	{
+		return (this.fileObject != null) ? this.fileObject.name : null;
+	};
+
+	LocalFile.prototype.isRenamable = function()
+	{
+		return false;
+	};
+	
+	// Restores default implementation of open with autosave
+	LocalFile.prototype.open = DrawioFile.prototype.open;
+
+	LocalFile.prototype.save = function(revision, success, error)
+	{
+		DrawioFile.prototype.save.apply(this, arguments);
+		
+		this.saveFile(revision, success, error);
+	};
+	
+	LocalLibrary.prototype.save = function(revision, success, error)
+	{
+		this.saveFile(revision, success, error);
+	};
+	
+	LocalFile.prototype.saveFile = function(revision, success, error)
+	{
+		var fn = mxUtils.bind(this, function()
+		{
+			var doSave = mxUtils.bind(this, function(data)
+			{
+				if (!this.savingFile)
+				{
+					this.savingFile = true;
+					
+					// Makes sure no changes get lost while the file is saved
+					var prevModified = this.isModified;
+					var modified = this.isModified();
+					this.setModified(false);
+					var fs = require('fs');
+					
+					fs.writeFile(this.fileObject.path, data, this.fileObject.encoding, mxUtils.bind(this, function (e)
+				    {
+		        		if (e)
+		        		{
+		        			this.savingFile = false;
+							this.isModified = prevModified;
+							this.setModified(modified || this.isModified());
+							
+							if (error != null)
+							{
+		        				error();
+							}
+		        		}
+		        		else
+		        		{
+							this.savingFile = false;
+							this.isModified = prevModified;
+							
+							this.contentChanged();
+							
+							if (success != null)
+							{
+								success();
+							}
+		        		}
+		        	}));
+				}
+				else
+				{
+					// TODO, already saving. Need a better error
+					error();
+				}
+			});
+
+			if (!/(\.png)$/i.test(this.fileObject.name))
+			{
+				doSave(this.getData());
+			}
+			else
+			{
+			   	this.ui.exportToCanvas(mxUtils.bind(this, function(canvas)
+			   	{
+			   		try
+			   		{
+			   	   	    var data = canvas.toDataURL('image/png');
+		   	   	   		data = this.ui.writeGraphModelToPng(data, 'zTXt', 'mxGraphModel',
+		   	   	   			atob(this.ui.editor.graph.compress(mxUtils.getXml(this.ui.editor.getGraphXml()))));
+			   	   	    doSave(data, 'base64');
+			   		}
+			   		catch (e)
+			   		{
+			   			if (error != null)
+			   			{
+			   				error(e);
+			   			}
+			   		}
+			   	}), null, null, null, mxUtils.bind(this, function(e)
+			   	{
+			   		if (error != null)
+		   			{
+		   				error(e);
+		   			}
+			   	}));
+			}
+		});
+		
+		if (this.fileObject == null)
+		{
+			const electron = require('electron');
+			var remote = electron.remote;
+			var dialog = remote.dialog;
+
+	        var path = dialog.showSaveDialog();
+
+//			chrome.fileSystem.chooseEntry({type: 'saveFile',
+//				accepts: [(this.constructor == LocalFile) ? {description: 'Draw.io Diagram (.xml)',
+//				extensions: ['xml']} : {description: 'Draw.io Library (.xml)',
+//				extensions: ['xml']}]}, mxUtils.bind(this, function(xmlFile)
+
+	        if (path != null)
+	        {
+				this.fileObject = new Object();
+				this.fileObject.path = path;
+				this.fileObject.name = path.replace(/^.*[\\\/]/, '');
+				this.fileObject.type = 'utf-8';
+				fn();
+			}
+		}
+		else
+		{
+			fn();
+		}
+	};
+	
+	LocalFile.prototype.saveAs = function(title, success, error)
+	{
+		const electron = require('electron');
+		var remote = electron.remote;
+		var dialog = remote.dialog;
+
+        var path = dialog.showSaveDialog();
+        
+//		chrome.fileSystem.chooseEntry({type: 'saveFile',
+//			accepts: [(this.constructor == LocalFile) ? {description: 'Draw.io Diagram (.xml)',
+//			extensions: ['xml']} : {description: 'Draw.io Library (.xml)',
+//			extensions: ['xml']}]}, mxUtils.bind(this, function(f)
+
+        if (path != null)
+        {
+			this.fileObject = new Object();
+			this.fileObject.path = path;
+			this.fileObject.name = path.replace(/^.*[\\\/]/, '');
+			this.fileObject.type = 'utf-8';
+			this.save(false, success, error);
+		}
+	};
+
+	App.prototype.saveFile = function(forceDialog)
+	{
+		var file = this.getCurrentFile();
+		
+		if (file != null)
+		{
+			if (!forceDialog && file.getTitle() != null)
+			{
+				file.save(true, mxUtils.bind(this, function(resp)
+				{
+					this.spinner.stop();
+					this.editor.setStatus(mxResources.get('allChangesSaved'));
+				}), mxUtils.bind(this, function(resp)
+				{
+					this.editor.setStatus('');
+					this.handleError(resp, (resp != null) ? mxResources.get('errorSavingFile') : null);
+				}));
+			}
+			else
+			{
+				file.saveAs(null, mxUtils.bind(this, function(resp)
+				{
+					this.spinner.stop();
+					this.editor.setStatus(mxResources.get('allChangesSaved'));
+				}), mxUtils.bind(this, function(resp)
+				{
+					this.editor.setStatus('');
+					this.handleError(resp, (resp != null) ? mxResources.get('errorSavingFile') : null);
+				}));
+			}
+		}
+	};
+	
+	/**
+	 * Translates this point by the given vector.
+	 * 
+	 * @param {number} dx X-coordinate of the translation.
+	 * @param {number} dy Y-coordinate of the translation.
+	 */
+	App.prototype.saveLibrary = function(name, images, file, mode, noSpin, noReload, fn)
+	{
+		mode = (mode != null) ? mode : this.mode;
+		noSpin = (noSpin != null) ? noSpin : false;
+		noReload = (noReload != null) ? noReload : false;
+		var xml = this.createLibraryDataFromImages(images);
+		
+		var error = mxUtils.bind(this, function(resp)
+		{
+			this.spinner.stop();
+			
+			if (fn != null)
+			{
+				fn();
+			}
+			
+			// Null means cancel by user and is ignored
+			if (resp != null)
+			{
+				this.handleError(resp, mxResources.get('errorSavingFile'));
+			}
+		});
+	
+		// Handles special case for local libraries
+		if (file == null)
+		{
+			file = new LocalLibrary(this, xml, name);
+		}
+		
+		if (noSpin || this.spinner.spin(document.body, mxResources.get('saving')))
+		{
+			file.setData(xml);
+			
+			var doSave = mxUtils.bind(this, function()
+			{
+				file.save(true, mxUtils.bind(this, function(resp)
+				{
+					this.spinner.stop();
+					this.hideDialog(true);
+					
+					if (!noReload)
+					{
+						this.libraryLoaded(file, images)
+					}
+					
+					if (fn != null)
+					{
+						fn();
+					}
+				}), error);
+			});
+			
+			if (name != file.getTitle())
+			{
+				var oldHash = file.getHash();
+				
+				file.rename(name, mxUtils.bind(this, function(resp)
+				{
+					// Change hash in stored settings
+					if (file.constructor != LocalLibrary && oldHash != file.getHash())
+					{
+						mxSettings.removeCustomLibrary(oldHash);
+						mxSettings.addCustomLibrary(file.getHash());
+					}
+	
+					// Workaround for library files changing hash so
+					// the old library cannot be removed from the
+					// sidebar using the updated file in libraryLoaded
+					this.removeLibrarySidebar(oldHash);
+	
+					doSave();
+				}), error)
+			}
+			else
+			{
+				doSave();
+			}
+		}
+	};
+	
+	App.prototype.doSaveLocalFile = function(data, filename, mimeType, base64Encoded)
+	{
+		chrome.fileSystem.chooseEntry({type: 'saveFile', suggestedName: filename, acceptsAllTypes: true}, mxUtils.bind(this, function(fileEntry)
+		{
+			if (!chrome.runtime.lastError)
+			{
+				fileEntry.createWriter(mxUtils.bind(this, function(writer)
+				{
+					writer.onwriteend = mxUtils.bind(this, function()
+					{
+						writer.onwriteend = null;
+						writer.write((base64Encoded) ? this.base64ToBlob(data, mimeType) : new Blob([data], {type: mimeType}));
+					});
+					
+					writer.onerror = mxUtils.bind(this, function(e)
+					{
+						this.handleError(e);
+					});
+					
+					writer.truncate(0);
+				}));
+			}
+			else if (chrome.runtime.lastError.message != 'User cancelled')
+			{
+				this.handleError(chrome.runtime.lastError);
+			}
+		}));
+	};
+})();

+ 486 - 0
war/js/diagramly/sidebar/Sidebar-Veeam.js

@@ -0,0 +1,486 @@
+(function()
+{
+	// Adds mockup shapes
+	Sidebar.prototype.addVeeamPalette = function()
+	{
+		this.addVeeam2DPalette();
+		this.addVeeam3DPalette();
+	};
+	
+	Sidebar.prototype.addVeeam2DPalette = function()
+	{
+		var sn = 'shadow=0;dashed=0;html=1;strokeColor=none;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;shape=mxgraph.veeam.2d.';
+		var s = 'shadow=0;dashed=0;html=1;strokeColor=none;fillColor=#4495D1;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;shape=mxgraph.veeam.2d.';
+		var s2 = 'shadow=0;dashed=0;html=1;strokeColor=none;fillColor=#EF8F21;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;shape=mxgraph.veeam.2d.';
+
+		// Space savers
+		var sb = this;
+		var gn = 'mxgraph.veeam.2d';
+		var dt = 'veeam 2d two dimension vmware virtual machine ';
+		
+		var w = 2.0;
+		var h = 2.0;
+		
+		var fns =
+		[
+			this.createVertexTemplateEntry(s + '1ftvm;',
+					w * 35, h * 35, '', '1FTVM', null, null, this.getTagsForStencil(gn, '1ftvm', dt).join(' ')),
+			this.createVertexTemplateEntry(s + '1ftvm_error;',
+					w * 35, h * 39, '', '1FTVM Error', null, null, this.getTagsForStencil(gn, '1ftvm error', dt).join(' ')),
+			this.createVertexTemplateEntry(s + '1ftvm_running;',
+					w * 35, h * 39, '', '1FTVM Running', null, null, this.getTagsForStencil(gn, '1frvm running', dt).join(' ')),
+			this.createVertexTemplateEntry(s + '1ftvm_unavailable;',
+					w * 35, h * 39, '', '1FTVM Unavailable', null, null, this.getTagsForStencil(gn, '1ftvm unavailable', dt).join(' ')),
+			this.createVertexTemplateEntry(s + '1ftvm_warning;',
+					w * 35, h * 39, '', '1FTVM Warning', null, null, this.getTagsForStencil(gn, '1ftvm warning', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + '1_click_failover_orchestration;',
+					w * 22, h * 22, '', '1 Click Failover Orchestration', null, null, this.getTagsForStencil(gn, 'one click failover orchestration', dt).join(' ')),
+			this.createVertexTemplateEntry(s + '2ftvm;',
+					w * 35, h * 35, '', '2FTVM', null, null, this.getTagsForStencil(gn, '2ftvm', dt).join(' ')),
+			this.createVertexTemplateEntry(s + '2ftvm_error;',
+					w * 35, h * 39, '', '2FTVM Error', null, null, this.getTagsForStencil(gn, '2ftvm error', dt).join(' ')),
+			this.createVertexTemplateEntry(s + '2ftvm_running;',
+					w * 35, h * 39, '', '2FTVM Running', null, null, this.getTagsForStencil(gn, '2ftvm running', dt).join(' ')),
+			this.createVertexTemplateEntry(s + '2ftvm_unavailable;',
+					w * 35, h * 39, '', '2FTVM Unavailable', null, null, this.getTagsForStencil(gn, '2ftvm unavailable', dt).join(' ')),
+			this.createVertexTemplateEntry(s + '2ftvm_warning;',
+					w * 35, h * 39, '', '2FTVM Warning', null, null, this.getTagsForStencil(gn, '2ftvm warning', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'agent;',
+					w * 19, h * 19, '', 'Agent', null, null, this.getTagsForStencil(gn, 'agent', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'alarm;',
+					w * 31, h * 23, '', 'Alarm', null, null, this.getTagsForStencil(gn, 'alarm', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'alert;',
+					w * 15, h * 15, '', 'Alert', null, null, this.getTagsForStencil(gn, 'alert', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'assisted_failover_and_failback;',
+					w * 23, h * 23, '', 'Assisted Failover and Failback', null, null, this.getTagsForStencil(gn, 'assisted failover and failback', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'backup_browser;',
+					w * 23, h * 23, '', 'Backup Browser', null, null, this.getTagsForStencil(gn, 'backup browser', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'backup_from_storage_snapshots;',
+					w * 23, h * 23, '', 'Backup from Storage Snapshots', null, null, this.getTagsForStencil(gn, 'backup from storage snapshots', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'backup_repository;',
+					w * 26, h * 24, '', 'Backup Repository', null, null, this.getTagsForStencil(gn, 'backup repository', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'backup_repository_2;',
+					w * 29, h * 25, '', 'Backup Repository', null, null, this.getTagsForStencil(gn, 'backup repository', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'built_in_wan_acceleration;',
+					w * 23, h * 23, '', 'Built-in WAN Acceleration', null, null, this.getTagsForStencil(gn, 'built in wan acceleration wireless area network', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'cd;',
+					w * 23, h * 23, '', 'CD', null, null, this.getTagsForStencil(gn, 'cd compact disc', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'cloud;',
+					w * 33, h * 23, '', 'Cloud', null, null, this.getTagsForStencil(gn, 'cloud', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'cloud_gateway;',
+					w * 23, h * 23, '', 'Cloud Gateway', null, null, this.getTagsForStencil(gn, 'cloud gateway', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'database;',
+					w * 29, h * 25, '', 'Database', null, null, this.getTagsForStencil(gn, 'database db', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'datastore;',
+					w * 22, h * 22, '', 'Datastore', null, null, this.getTagsForStencil(gn, 'datastore', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'datastore_snapshot;',
+					w * 23, h * 6, '', 'Datastore Snapshot', null, null, this.getTagsForStencil(gn, 'datastore snapshot', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'datastore_volume;',
+					w * 23, h * 6, '', 'Datastore Volume', null, null, this.getTagsForStencil(gn, 'datastore volume', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'data_mover;',
+					w * 19, h * 19, '', 'Data Mover', null, null, this.getTagsForStencil(gn, 'data mover', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'disaster_recovery;',
+					w * 23, h * 23, '', 'Disaster Recovery', null, null, this.getTagsForStencil(gn, 'disaster recovery', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'download;',
+					w * 23, h * 23, '', 'Download', null, null, this.getTagsForStencil(gn, 'download', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'emc_data_domain_boost;',
+					w * 23, h * 23, '', 'EMC Data Domain Boost', null, null, this.getTagsForStencil(gn, 'emc data domain boost', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'encryption_object;',
+					w * 23, h * 23, '', 'Encryption Object', null, null, this.getTagsForStencil(gn, 'encryption object', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'end_to_end_encryption;',
+					w * 23, h * 23, '', 'End to end Encryption', null, null, this.getTagsForStencil(gn, 'end to end encryption', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'esx_esxi;',
+					w * 15, h * 23, '', 'ESX/ESXi', null, null, this.getTagsForStencil(gn, 'esx esxi', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'exagrid;',
+					w * 23, h * 23, '', 'ExaGrid', null, null, this.getTagsForStencil(gn, 'exagrid', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'failover_protective_snapshot;',
+					w * 23, h * 23, '', 'Failover Protective Snapshot', null, null, this.getTagsForStencil(gn, 'failover protective snapshot', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'failover_protective_snapshot_locked;',
+					w * 27, h * 25, '', 'Failover Protective Snapshot Locked', null, null, this.getTagsForStencil(gn, 'failover protective snapshot locked', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'failover_protective_snapshot_running;',
+					w * 28, h * 25, '', 'Failover Protective Snapshot Running', null, null, this.getTagsForStencil(gn, 'failover protective snapshot running', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'file;',
+					w * 17, h * 23, '', 'File', null, null, this.getTagsForStencil(gn, 'file', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'file_system_browser;',
+					w * 23, h * 23, '', 'File System Browser', null, null, this.getTagsForStencil(gn, 'file system browser', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'folder;',
+					w * 24, h * 23, '', 'Folder', null, null, this.getTagsForStencil(gn, 'folder', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'forward_incremental_backup_increment;fillColor=#999A98;',
+					w * 17, h * 12, '', 'Forward Incremental Backup - Increment (grey)', null, null, this.getTagsForStencil(gn, 'forward incremental backup increment', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'forward_incremental_backup_increment;',
+					w * 17, h * 12, '', 'Forward Incremental Backup - Increment (blue)', null, null, this.getTagsForStencil(gn, 'forward incremental backup increment', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'free_datastore;',
+					w * 23, h * 23, '', 'Free Datastore', null, null, this.getTagsForStencil(gn, 'free datastore', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'full_datastore;',
+					w * 23, h * 23, '', 'Full Datastore', null, null, this.getTagsForStencil(gn, 'full datastore', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'full_veeam_backup;fillColor=#999A98;',
+					w * 13, h * 21, '', 'Full Veeam Backup (grey)', null, null, this.getTagsForStencil(gn, 'full veeam backup', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'full_veeam_backup;fillColor=#24B14B;',
+					w * 13, h * 21, '', 'Full Veeam Backup (green)', null, null, this.getTagsForStencil(gn, 'full veeam backup', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'full_veeam_backup;fillColor=#EF8F21;',
+					w * 13, h * 21, '', 'Full Veeam Backup (orange)', null, null, this.getTagsForStencil(gn, 'full veeam backup', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'full_veeam_backup;fillColor=#FBB715;',
+					w * 13, h * 21, '', 'Full Veeam Backup (yellow)', null, null, this.getTagsForStencil(gn, 'full veeam backup', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'group;',
+					w * 20, h * 23, '', 'Group', null, null, this.getTagsForStencil(gn, 'group', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'hard_drive;',
+					w * 19, h * 23, '', 'Hard Drive', null, null, this.getTagsForStencil(gn, 'hard drive', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'hp_storeonce;',
+					w * 23, h * 23, '', 'HP StoreOnce', null, null, this.getTagsForStencil(gn, 'hp storeonce', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'hyper_v_host;',
+					w * 62, h * 60, '', 'Hyper-V Host', null, null, this.getTagsForStencil(gn, 'hyper host', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'hyper_v_vmware_host;',
+					w * 62, h * 60, '', 'VMware/Hyper-V Host', null, null, this.getTagsForStencil(gn, 'hyper vmware host', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'letter;',
+					w * 23, h * 18, '', 'Letter', null, null, this.getTagsForStencil(gn, 'letter', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'license;',
+					w * 23, h * 23, '', 'License', null, null, this.getTagsForStencil(gn, 'license', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'lost_space;',
+					w * 23, h * 23, '', 'Lost Space', null, null, this.getTagsForStencil(gn, 'lost space', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'lun;',
+					w * 29, h * 11, '', 'LUN', null, null, this.getTagsForStencil(gn, 'lun', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'medium_datastore;',
+					w * 23, h * 23, '', 'Medium Datastore', null, null, this.getTagsForStencil(gn, 'medium datastore', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'monitoring_console;',
+					w * 23, h * 23, '', 'Monitoring Console', null, null, this.getTagsForStencil(gn, 'monitoring console', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'native_tape_support;',
+					w * 23, h * 23, '', 'Native Tape Support', null, null, this.getTagsForStencil(gn, 'native tape support', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'network_card;',
+					w * 23, h * 16, '', 'Network Card', null, null, this.getTagsForStencil(gn, 'network card', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'on_demand_sandbox;',
+					w * 23, h * 23, '', 'On Demand Sandbox', null, null, this.getTagsForStencil(gn, 'on demand sandbox', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'physical_storage;',
+					w * 38, h * 13, '', 'Physical Storage', null, null, this.getTagsForStencil(gn, 'physical storage', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'powershell_extension;',
+					w * 23, h * 23, '', 'PowerShell Extension', null, null, this.getTagsForStencil(gn, 'powershell extension', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'private_key;',
+					w * 28, h * 26, '', 'Private Key', null, null, this.getTagsForStencil(gn, 'private key', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'privilege;',
+					w * 25, h * 24, '', 'Privilege', null, null, this.getTagsForStencil(gn, 'privilege', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'proxy;',
+					w * 23, h * 23, '', 'Proxy', null, null, this.getTagsForStencil(gn, 'proxy', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'proxy_appliance;',
+					w * 23, h * 23, '', 'Proxy Appliance', null, null, this.getTagsForStencil(gn, 'proxy appliance', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'quick_migration;',
+					w * 23, h * 23, '', 'Quick Migration', null, null, this.getTagsForStencil(gn, 'quick migration', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'remote_site;',
+					w * 21, h * 22, '', 'Remote Site', null, null, this.getTagsForStencil(gn, 'remote site', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'remote_storage;',
+					w * 23, h * 23, '', 'Remote Storage', null, null, this.getTagsForStencil(gn, 'remote storage', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'replication_from_a_backup;',
+					w * 23, h * 23, '', 'Replication from a Backup', null, null, this.getTagsForStencil(gn, 'replication from a backup', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'report;',
+					w * 17, h * 23, '', 'Report', null, null, this.getTagsForStencil(gn, 'report', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'resource_pool;',
+					w * 23, h * 23, '', 'Resource Pool', null, null, this.getTagsForStencil(gn, 'resource pool', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'restful_apis;',
+					w * 23, h * 23, '', 'RESTful APIs', null, null, this.getTagsForStencil(gn, 'restful apis api', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'restore_data_from_the_vm_backup;',
+					w * 23, h * 23, '', 'Restore Data from the VM Backup', null, null, this.getTagsForStencil(gn, 'restore data from the vm backup', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'reversed_incremental_backup_increment;fillColor=#999A98;',
+					w * 17, h * 12, '', 'Reversed Incremental Backup - Increment (grey)', null, null, this.getTagsForStencil(gn, 'reversed incremental backup increment', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'reversed_incremental_backup_increment;fillColor=#6E5CA7;',
+					w * 17, h * 12, '', 'Reversed Incremental Backup - Increment (purple)', null, null, this.getTagsForStencil(gn, 'reversed incremental backup increment', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'role;',
+					w * 17, h * 23, '', 'Role', null, null, this.getTagsForStencil(gn, 'role', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'scheduled_backups;',
+					w * 23, h * 23, '', 'Scheduled Backups', null, null, this.getTagsForStencil(gn, 'Scheduled Backups', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'search;',
+					w * 23, h * 23, '', 'Search', null, null, this.getTagsForStencil(gn, 'search', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'self_service_recovery;',
+					w * 23, h * 23, '', 'Self-Service Recovery', null, null, this.getTagsForStencil(gn, 'self service recovery', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'service;',
+					w * 23, h * 23, '', 'Service', null, null, this.getTagsForStencil(gn, 'service', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'service_console;',
+					w * 23, h * 23, '', 'Service Console', null, null, this.getTagsForStencil(gn, 'service console', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'service_vnic;',
+					w * 30, h * 27, '', 'Service vNIC', null, null, this.getTagsForStencil(gn, 'service vnic', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'sure_backup;',
+					w * 23, h * 23, '', 'SureBackup', null, null, this.getTagsForStencil(gn, 'sure backup', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'sure_replica;',
+					w * 23, h * 23, '', 'SureReplica', null, null, this.getTagsForStencil(gn, 'sure replica', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'switch;',
+					w * 37, h * 6, '', 'Switch', null, null, this.getTagsForStencil(gn, 'switch', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'tape;',
+					w * 30, h * 16, '', 'Tape', null, null, this.getTagsForStencil(gn, 'tape', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'tape_checked;',
+					w * 35, h * 21, '', 'Tape Checked', null, null, this.getTagsForStencil(gn, 'tape checked', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'tape_device;',
+					w * 26, h * 26, '', 'Tape Device', null, null, this.getTagsForStencil(gn, 'tape device', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'tape_ejecting;',
+					w * 35, h * 21, '', 'Tape Ejecting', null, null, this.getTagsForStencil(gn, 'tape ejecting', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'tape_library;',
+					w * 20, h * 23, '', 'Tape Library', null, null, this.getTagsForStencil(gn, 'tape library', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'tape_licensed;',
+					w * 34, h * 19, '', 'Tape Licensed', null, null, this.getTagsForStencil(gn, 'tape licensed', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'tape_recording;',
+					w * 35, h * 21, '', 'Tape Recording', null, null, this.getTagsForStencil(gn, 'tape recording', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'tape_server;',
+					w * 37, h * 36, '', 'Tape Server', null, null, this.getTagsForStencil(gn, 'tape server', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'transport_service;',
+					w * 19, h * 19, '', 'Transport Service', null, null, this.getTagsForStencil(gn, 'transport service', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'user;',
+					w * 13, h * 23, '', 'User', null, null, this.getTagsForStencil(gn, 'user', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'u_air;',
+					w * 23, h * 23, '', 'U-AIR', null, null, this.getTagsForStencil(gn, 'air', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'vapp;',
+					w * 24, h * 24, '', 'vApp', null, null, this.getTagsForStencil(gn, 'vapp', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'vapp_started;',
+					w * 30, h * 27, '', 'vApp Started', null, null, this.getTagsForStencil(gn, 'vapp started', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'veeamzip;',
+					w * 23, h * 23, '', 'VeeamZIP', null, null, this.getTagsForStencil(gn, 'veeamzip zip', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'veeam_availability_suite;',
+					w * 23, h * 23, '', 'Veeam Availability Suite', null, null, this.getTagsForStencil(gn, 'availability suite', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'veeam_backup_and_replication_server;',
+					w * 37, h * 36, '', 'Veeam Backup and Replication Server', null, null, this.getTagsForStencil(gn, 'backup and replication server', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'veeam_backup_enterprise_manager_server;',
+					w * 37, h * 36, '', 'Veeam Backup Enterprise Manager Server', null, null, this.getTagsForStencil(gn, 'backup enterprise manager server', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'veeam_backup_search_server;',
+					w * 37, h * 36, '', 'Veeam Backup Search Server', null, null, this.getTagsForStencil(gn, 'backup search server', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'veeam_backup_shell;',
+					w * 23, h * 23, '', 'Veeam Backup Shell', null, null, this.getTagsForStencil(gn, 'backup shell', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'veeam_cloud_connect;',
+					w * 23, h * 23, '', 'Veeam Cloud Connect', null, null, this.getTagsForStencil(gn, 'cloud connect', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'veeam_explorer;',
+					w * 23, h * 23, '', 'Veeam Explorer', null, null, this.getTagsForStencil(gn, 'explorer', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'veeam_explorer_for_active_directory;',
+					w * 23, h * 23, '', 'Veeam Explorer for Active Directory', null, null, this.getTagsForStencil(gn, 'explorer for active directory', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'veeam_explorer_for_exchange;',
+					w * 23, h * 23, '', 'Veeam Explorer for Exchange', null, null, this.getTagsForStencil(gn, 'explorer for exchange', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'veeam_explorer_for_sharepoint;',
+					w * 23, h * 23, '', 'Veeam Explorer for SharePoint', null, null, this.getTagsForStencil(gn, 'explorer for sharepoint', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'veeam_explorer_for_sql;',
+					w * 23, h * 23, '', 'Veeam Explorer for SQL', null, null, this.getTagsForStencil(gn, 'explorer for sql', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'veeam_logo;fillColor=#232020;',
+					w * 72, h * 19, '', 'Veeam Logo', null, null, this.getTagsForStencil(gn, 'logo', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'veeam_one_business_view;',
+					w * 23, h * 23, '', 'Veeam ONE Business View', null, null, this.getTagsForStencil(gn, 'one business view', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'veeam_one_monitor;',
+					w * 23, h * 23, '', 'Veeam ONE Monitor', null, null, this.getTagsForStencil(gn, 'one monitor', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'veeam_one_reporter;',
+					w * 23, h * 23, '', 'Veeam ONE Reporter', null, null, this.getTagsForStencil(gn, 'one reporter', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'veeam_one_server;',
+					w * 23, h * 23, '', 'Veeam ONE Server', null, null, this.getTagsForStencil(gn, 'one server', dt).join(' ')),
+			this.createVertexTemplateEntry(s2 + 'virtual_lab;',
+					w * 23, h * 23, '', 'Virtual Lab', null, null, this.getTagsForStencil(gn, 'virtual lab', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'virtual_machine;',
+					w * 23, h * 23, '', 'Virtual Machine', null, null, this.getTagsForStencil(gn, '', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'virtual_switch;',
+					w * 23, h * 23, '', 'Virtual Switch', null, null, this.getTagsForStencil(gn, 'switch', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'vmware_host;',
+					w * 62, h * 60, '', 'VMware Host', null, null, this.getTagsForStencil(gn, 'vmware host', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'vm_backup;',
+					w * 25, h * 23, '', 'VM Backup', null, null, this.getTagsForStencil(gn, 'vm backup', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'vm_failed;',
+					w * 28, h * 27, '', 'VM Failed', null, null, this.getTagsForStencil(gn, 'vm failed', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'vm_image_full_backup;',
+					w * 19, h * 26, '', 'VM Image Full Backup', null, null, this.getTagsForStencil(gn, 'vm image full backup', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'vm_image_incremental_backup;',
+					w * 19, h * 26, '', 'VM Image Incremental Backup', null, null, this.getTagsForStencil(gn, 'vm image incremental backup', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'vm_linux;',
+					w * 23, h * 42, '', 'VM Linux', null, null, this.getTagsForStencil(gn, 'vm linux', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'vm_locked;',
+					w * 28, h * 26, '', 'VM Locked', null, null, this.getTagsForStencil(gn, 'vm locked', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'vm_no_network;',
+					w * 27, h * 26, '', 'VM No Network', null, null, this.getTagsForStencil(gn, 'vm no network', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'vm_problem;',
+					w * 28, h * 26, '', 'VM Problem', null, null, this.getTagsForStencil(gn, 'vm problem', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'vm_running;',
+					w * 28, h * 27, '', 'VM Running', null, null, this.getTagsForStencil(gn, 'vm running', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'vm_saved_state;',
+					w * 28, h * 27, '', 'VM Saved State', null, null, this.getTagsForStencil(gn, 'vm saved state', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'vm_windows;',
+					w * 23, h * 42, '', 'VM Windows', null, null, this.getTagsForStencil(gn, 'vm windows', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'vnic;',
+					w * 23, h * 23, '', 'vNIC', null, null, this.getTagsForStencil(gn, 'vnic', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'vsb_file;',
+					w * 17, h * 23, '', '.vsb File', null, null, this.getTagsForStencil(gn, 'vsb file', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'wan_accelerator;',
+					w * 23, h * 23, '', 'WAN Accelerator', null, null, this.getTagsForStencil(gn, 'wan accelerator wireless area network', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'web_console;',
+					w * 23, h * 23, '', 'Web Console', null, null, this.getTagsForStencil(gn, 'web console', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'web_ui;',
+					w * 23, h * 23, '', 'Web UI', null, null, this.getTagsForStencil(gn, 'web ui user interface', dt).join(' ')),
+			this.createVertexTemplateEntry(s + 'workstation;',
+					w * 34, h * 23, '', 'Workstation', null, null, this.getTagsForStencil(gn, 'workstation', dt).join(' '))
+		];
+			
+		this.addPalette('veeam2D', 'Veeam / 2D', false, mxUtils.bind(this, function(content)
+				{
+					for (var i = 0; i < fns.length; i++)
+					{
+						content.appendChild(fns[i](content));
+					}
+		}));
+	};
+
+	Sidebar.prototype.addVeeam3DPalette = function()
+	{
+		var sn = 'shadow=0;dashed=0;html=1;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;shape=mxgraph.veeam.3d.';
+		var s = 'shadow=0;dashed=0;html=1;strokeColor=none;fillColor=#4495D1;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;shape=mxgraph.veeam.3d.';
+		var s2 = 'shadow=0;dashed=0;html=1;strokeColor=none;fillColor=#EF8F21;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;shape=mxgraph.veeam.3d.';
+
+		// Space savers
+		var sb = this;
+		var gn = 'mxgraph.veeam.3d';
+		var dt = 'veeam 3d three dimension vmware virtual machine ';
+		
+		var w = 2.0;
+		var h = 2.0;
+		
+		var fns =
+		[
+			this.createVertexTemplateEntry(sn + '1ftvm;',
+					w * 34, h * 31, '', '1FTVM', null, null, this.getTagsForStencil(gn, '1ftvm', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + '1ftvm_error;',
+					w * 34, h * 31, '', '1FTVM Error', null, null, this.getTagsForStencil(gn, '1ftvm error', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + '1ftvm_running;',
+					w * 34, h * 31, '', '1FTVM Running', null, null, this.getTagsForStencil(gn, '1ftvm running', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + '1ftvm_unavailable;',
+					w * 34, h * 31, '', '1FTVM Unavailable', null, null, this.getTagsForStencil(gn, '1ftvm unavailable', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + '1ftvm_warning;',
+					w * 34, h * 31, '', '1FTVM Warning', null, null, this.getTagsForStencil(gn, '1ftvm warning', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + '2ftvm;',
+					w * 34, h * 31, '', '2FTVM', null, null, this.getTagsForStencil(gn, '2ftvm', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + '2ftvm_error;',
+					w * 34, h * 31, '', '2FTVM Error', null, null, this.getTagsForStencil(gn, '2ftvm error', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + '2ftvm_running;',
+					w * 34, h * 31, '', '2FTVM Running', null, null, this.getTagsForStencil(gn, '2ftvm running', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + '2ftvm_unavailable;',
+					w * 34, h * 31, '', '2FTVM Unavailable', null, null, this.getTagsForStencil(gn, '2ftvm unavailable', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + '2ftvm_warning;',
+					w * 34, h * 31, '', '2FTVM Warning', null, null, this.getTagsForStencil(gn, '2ftvm warning', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'backup_repository;',
+					w * 31, h * 31, '', 'Backup Repository', null, null, this.getTagsForStencil(gn, 'backup repository', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'backup_repository_2;',
+					w * 31, h * 31, '', 'Backup Repository', null, null, this.getTagsForStencil(gn, 'backup repository', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'cd;',
+					w * 34, h * 13, '', 'CD', null, null, this.getTagsForStencil(gn, 'cd', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'database;',
+					w * 29, h * 31, '', 'Database', null, null, this.getTagsForStencil(gn, 'database', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'datastore;',
+					w * 22, h * 30, '', 'Datastore', null, null, this.getTagsForStencil(gn, 'datastore', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'datastore_snapshot;',
+					w * 27, h * 17, '', 'Datastore Snapshot', null, null, this.getTagsForStencil(gn, 'datastore snapshot', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'datastore_volume;',
+					w * 27, h * 17, '', 'Datastore Volume', null, null, this.getTagsForStencil(gn, 'datastore volume', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'esx_esxi;',
+					w * 19, h * 26, '', 'ESX ESXi', null, null, this.getTagsForStencil(gn, 'esx esxi', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'failover_protective_snapshot;',
+					w * 23, h * 23, '', 'Failover Protective Snapshot', null, null, this.getTagsForStencil(gn, 'failover protective snapshot', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'failover_protective_snapshot_locked;',
+					w * 28, h * 23, '', 'Failover Protective Snapshot Locked', null, null, this.getTagsForStencil(gn, 'failover protective snapshot locked', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'failover_protective_snapshot_running;',
+					w * 29, h * 23, '', 'Failover Protective Snapshot Running', null, null, this.getTagsForStencil(gn, 'failover protective snapshot running', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'free_datastore;',
+					w * 22, h * 30, '', 'Free Datastore', null, null, this.getTagsForStencil(gn, 'free datastore', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'full_datastore;',
+					w * 22, h * 30, '', 'Full Datastore', null, null, this.getTagsForStencil(gn, 'full datastore', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'hard_drive;fillColor=#637D8A;gradientColor=#324752;strokeColor=none;',
+					w * 31, h * 14, '', 'Hard Drive', null, null, this.getTagsForStencil(gn, 'hard drive', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'hyper_v_host;',
+					w * 55, h * 49, '', 'Hyper-V Host', null, null, this.getTagsForStencil(gn, 'hyper-v host', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'lost_space;',
+					w * 22, h * 30, '', 'Lost Space', null, null, this.getTagsForStencil(gn, 'lost space', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'lun;',
+					w * 36, h * 20, '', 'LUN', null, null, this.getTagsForStencil(gn, 'lun', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'medium_datastore;',
+					w * 22, h * 30, '', 'Medium Datastore', null, null, this.getTagsForStencil(gn, 'medium datastore', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'network_card;',
+					w * 19, h * 20, '', 'Network Card', null, null, this.getTagsForStencil(gn, 'network card', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'physical_storage;',
+					w * 54, h * 30, '', 'Physical Storage', null, null, this.getTagsForStencil(gn, 'physical_storage', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'proxy;',
+					w * 23, h * 23, '', 'Proxy', null, null, this.getTagsForStencil(gn, 'proxy', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'proxy_appliance;',
+					w * 23, h * 23, '', 'Proxy Appliance', null, null, this.getTagsForStencil(gn, 'proxy appliance', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'remote_site;',
+					w * 23, h * 30, '', 'Remote Site', null, null, this.getTagsForStencil(gn, 'remote site', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'remote_storage;',
+					w * 26, h * 31, '', 'Remote Storage', null, null, this.getTagsForStencil(gn, 'remote storage', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'resource_pool;',
+					w * 28, h * 16, '', 'Resource Pool', null, null, this.getTagsForStencil(gn, 'resource pool', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'service_vnic;',
+					w * 36, h * 32, '', 'Service vNIC', null, null, this.getTagsForStencil(gn, 'service vnic', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'switch;',
+					w * 55, h * 29, '', 'Switch', null, null, this.getTagsForStencil(gn, 'switch', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'tape;',
+					w * 29, h * 29, '', 'Tape', null, null, this.getTagsForStencil(gn, 'tape', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'tape_checked;',
+					w * 35, h * 29, '', 'Tape Checked', null, null, this.getTagsForStencil(gn, 'tape checked', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'tape_ejecting;',
+					w * 35, h * 29, '', 'Tape Ejecting', null, null, this.getTagsForStencil(gn, 'tape ejecting', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'tape_library;',
+					w * 31, h * 37, '', 'Tape Library', null, null, this.getTagsForStencil(gn, 'tape library', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'tape_licensed;',
+					w * 35, h * 29, '', 'Tape Licensed', null, null, this.getTagsForStencil(gn, 'tape licensed', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'tape_recording;',
+					w * 35, h * 29, '', 'Tape Recording', null, null, this.getTagsForStencil(gn, 'tape recording', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'tape_server;',
+					w * 23, h * 23, '', 'Tape Server', null, null, this.getTagsForStencil(gn, 'tape server', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'vapp;',
+					w * 46, h * 31, '', 'vApp', null, null, this.getTagsForStencil(gn, 'vapp', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'vapp_started;',
+					w * 46, h * 31, '', 'vApp Started', null, null, this.getTagsForStencil(gn, 'vapp started', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'veeam_availability_suite;',
+					w * 23, h * 23, '', 'Veeam Availability Suite', null, null, this.getTagsForStencil(gn, 'veeam availability suite', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'veeam_backup_and_replication_server;',
+					w * 23, h * 23, '', 'Veeam Backup and Replication Server', null, null, this.getTagsForStencil(gn, 'veeam backup and replication server', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'veeam_backup_enterprise_manager_server;',
+					w * 23, h * 23, '', 'Veeam Backup Enterprise Manager Server', null, null, this.getTagsForStencil(gn, 'veeam backup enterprise manager server', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'veeam_backup_enterprise_manager_server_2d;',
+					w * 20, h * 20, '', 'Veeam Backup Enterprise Manager Server 2D', null, null, this.getTagsForStencil(gn, 'veeam backup enterprise manager derver 2d two dimensional', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'veeam_backup_search_server;',
+					w * 23, h * 23, '', 'Veeam Backup Search Server', null, null, this.getTagsForStencil(gn, 'veeam backup search server', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'veeam_one_business_view;',
+					w * 23, h * 23, '', 'Veeam ONE Business View', null, null, this.getTagsForStencil(gn, 'veeam one business view', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'veeam_one_monitor;',
+					w * 23, h * 23, '', 'Veeam ONE Monitor', null, null, this.getTagsForStencil(gn, 'veeam one monitor', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'veeam_one_reporter;',
+					w * 23, h * 23, '', 'Veeam ONE Reporter', null, null, this.getTagsForStencil(gn, 'veeam one reporter', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'veeam_one_server;',
+					w * 23, h * 23, '', 'Veeam ONE Server', null, null, this.getTagsForStencil(gn, 'veeam one server', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'virtual_machine;',
+					w * 23, h * 23, '', 'Virtual Machine', null, null, this.getTagsForStencil(gn, 'virtual machine', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'vmware_host;',
+					w * 55, h * 49, '', 'VMware Host', null, null, this.getTagsForStencil(gn, 'vmware host', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'vm_failed;',
+					w * 28, h * 23, '', 'VM Failed', null, null, this.getTagsForStencil(gn, 'vm failed', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'vm_linux;',
+					w * 23, h * 30, '', 'VM Linux', null, null, this.getTagsForStencil(gn, 'vm linux', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'vm_no_network;',
+					w * 29, h * 23, '', 'VM No Network', null, null, this.getTagsForStencil(gn, 'vm no network', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'vm_problem;',
+					w * 28, h * 23, '', 'VM Problem', null, null, this.getTagsForStencil(gn, 'vm problem', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'vm_running;',
+					w * 28, h * 23, '', 'VM Running', null, null, this.getTagsForStencil(gn, 'vm running', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'vm_saved_state;',
+					w * 29, h * 24, '', 'VM Saved State', null, null, this.getTagsForStencil(gn, 'vm saved state', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'vm_windows;',
+					w * 23, h * 30, '', 'VM Windows', null, null, this.getTagsForStencil(gn, 'vm windows', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'vnic;',
+					w * 31, h * 31, '', 'vNIC', null, null, this.getTagsForStencil(gn, 'vnic', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'wan_accelerator;',
+					w * 23, h * 23, '', 'WAN Accelerator', null, null, this.getTagsForStencil(gn, 'wan accelerator', dt).join(' ')),
+			this.createVertexTemplateEntry(sn + 'workstation;',
+					w * 38, h * 31, '', 'Workstation', null, null, this.getTagsForStencil(gn, 'workstation', dt).join(' '))
+		];
+			
+		this.addPalette('veeam3D', 'Veeam / 3D', false, mxUtils.bind(this, function(content)
+				{
+					for (var i = 0; i < fns.length; i++)
+					{
+						content.appendChild(fns[i](content));
+					}
+		}));
+	};
+
+	
+	
+	
+	
+	
+	
+	
+	
+})();

+ 12 - 3
war/js/diagramly/sidebar/Sidebar.js

@@ -74,6 +74,11 @@
 	 */
 	Sidebar.prototype.office = ['Clouds', 'Communications', 'Concepts', 'Databases', 'Devices', 'Security', 'Servers', 'Services', 'Sites', 'Users'];
 
+	/**
+	 * 
+	 */
+	Sidebar.prototype.veeam = ['2D', '3D'];
+
 	/**
 	 * 
 	 */
@@ -105,6 +110,7 @@
            	                           {id: 'pid', prefix: 'pid', libs: Sidebar.prototype.pids},
            	                           {id: 'cisco', prefix: 'cisco', libs: Sidebar.prototype.cisco},
            	                           {id: 'office', prefix: 'office', libs: Sidebar.prototype.office},
+           	                           {id: 'veeam', prefix: 'veeam', libs: Sidebar.prototype.veeam},
            	                           {id: 'cabinets', libs: ['cabinets']},
            	                           {id: 'floorplan', libs: ['floorplan']},
            	                           {id: 'bootstrap', libs: ['bootstrap']},
@@ -286,7 +292,8 @@
             			          {title: 'Citrix', id: 'citrix', image: IMAGE_PATH + '/sidebar-citrix.png'},
             			          {title: 'Network', id: 'network', image: IMAGE_PATH + '/sidebar-network.png'},
             			          {title: 'Office', id: 'office', image: IMAGE_PATH + '/sidebar-office.png'},
-            			          {title: mxResources.get('rack'), id: 'rack', image: IMAGE_PATH + '/sidebar-rack.png'}]},
+            			          {title: mxResources.get('rack'), id: 'rack', image: IMAGE_PATH + '/sidebar-rack.png'},
+            			          {title: 'Veeam', id: 'veeam', image: IMAGE_PATH + '/sidebar-veeam.png'}]},
             			{title: mxResources.get('business'),
             			entries: [{title: 'ArchiMate 3.0', id: 'archimate3', image: IMAGE_PATH + '/sidebar-archimate3.png'},
             			          {title: mxResources.get('archiMate21'), id: 'archimate', image: IMAGE_PATH + '/sidebar-archimate.png'},
@@ -437,8 +444,8 @@
 					var h = clone.clientHeight + 18;
 					clone.parentNode.removeChild(clone);
 					
-		    		new mxXmlRequest(EXPORT_URL, 'w=456&h=' + h + '&html=' +
-		    				encodeURIComponent(this.editorUi.editor.compress(html))).simulate(document, '_blank');
+		    		new mxXmlRequest(EXPORT_URL, 'w=456&h=' + h + '&html=' + encodeURIComponent(
+		    			this.editorUi.editor.graph.compress(html))).simulate(document, '_blank');
 	
 					return;
 				}
@@ -564,6 +571,7 @@
 		var eip = this.eip;
 		var gmdl = this.gmdl;
 		var office = this.office;
+		var veeam = this.veeam;
 		var archimate3 = this.archimate3;
 		
 		if (urlParams['createindex'] == '1')
@@ -593,6 +601,7 @@
 		this.addMockupPalette();
 		this.addElectricalPalette();
 		this.addOfficePalette();
+		this.addVeeamPalette();
 
 		this.addStencilPalette('arrows', mxResources.get('arrows'), dir + '/arrows.xml',
 				';html=1;' + mxConstants.STYLE_VERTICAL_LABEL_POSITION + '=bottom;' + mxConstants.STYLE_VERTICAL_ALIGN + '=top;' + mxConstants.STYLE_STROKEWIDTH + '=2;strokeColor=#000000;');

File diff suppressed because it is too large
+ 376 - 376
war/js/embed-static.min.js


+ 52 - 61
war/js/mxgraph/Graph.js

@@ -5371,30 +5371,33 @@ if (typeof mxVertexHandler != 'undefined')
 			// Checks the given node for new nodes, recursively
 			function checkNode(node, clone)
 			{
-				if (clone.originalNode != node)
+				if (node != null)
 				{
-					cleanNode(node);
-				}
-				else if (node != null)
-				{
-					node = node.firstChild;
-					clone = clone.firstChild;
-					
-					while (node != null)
+					if (clone.originalNode != node)
+					{
+						cleanNode(node);
+					}
+					else
 					{
-						var nextNode = node.nextSibling;
+						node = node.firstChild;
+						clone = clone.firstChild;
 						
-						if (clone == null)
+						while (node != null)
 						{
-							cleanNode(node);
-						}
-						else
-						{
-							checkNode(node, clone);
-							clone = clone.nextSibling;
+							var nextNode = node.nextSibling;
+							
+							if (clone == null)
+							{
+								cleanNode(node);
+							}
+							else
+							{
+								checkNode(node, clone);
+								clone = clone.nextSibling;
+							}
+	
+							node = nextNode;
 						}
-
-						node = nextNode;
 					}
 				}
 			};
@@ -6085,7 +6088,7 @@ if (typeof mxVertexHandler != 'undefined')
 			};
 		}
 
-		// Overrides/extends rubberband for space handling with Ctrl+Shift(+Alt) drag
+		// Overrides/extends rubberband for space handling with Ctrl+Shift(+Alt) drag ("scissors tool")
 		mxRubberband.prototype.isSpaceEvent = function(me)
 		{
 			return this.graph.isEnabled() && !this.graph.isCellLocked(this.graph.getDefaultParent()) &&
@@ -6125,48 +6128,24 @@ if (typeof mxVertexHandler != 'undefined')
 					this.graph.model.beginUpdate();
 					try
 					{
-						var right = this.graph.getCellsBeyond(x0, y0, this.graph.getDefaultParent(), true, false);
-						var bottom = this.graph.getCellsBeyond(x0, y0, this.graph.getDefaultParent(), false, true);
-						
-						for (var i = 0; i < right.length; i++)
-						{
-							if (this.graph.isCellMovable(right[i]))
-							{
-								var tmp = this.graph.view.getState(right[i]);
-								var geo = this.graph.getCellGeometry(right[i]);
-								
-								if (tmp != null && geo != null)
-								{
-									geo = geo.clone();
-									geo.translate(dx, 0);
-									this.graph.model.setGeometry(right[i], geo);
-								}
-							}
-						}
-						
-						for (var i = 0; i < bottom.length; i++)
+						var cells = this.graph.getCellsBeyond(x0, y0, this.graph.getDefaultParent(), true, true);
+
+						for (var i = 0; i < cells.length; i++)
 						{
-							if (this.graph.isCellMovable(bottom[i]))
+							if (this.graph.isCellMovable(cells[i]))
 							{
-								var tmp = this.graph.view.getState(bottom[i]);
-								var geo = this.graph.getCellGeometry(bottom[i]);
+								var tmp = this.graph.view.getState(cells[i]);
+								var geo = this.graph.getCellGeometry(cells[i]);
 								
 								if (tmp != null && geo != null)
 								{
 									geo = geo.clone();
-									geo.translate(0, dy);
-									this.graph.model.setGeometry(bottom[i], geo);
+									geo.translate(dx, dy);
+									this.graph.model.setGeometry(cells[i], geo);
 								}
 							}
 						}
 					}
-					catch (e)
-					{
-		    			if (window.console != null)
-		    			{
-		    				console.log('Error in rubberband: ' + e);
-		    			}
-					}
 					finally
 					{
 						this.graph.model.endUpdate();
@@ -6211,7 +6190,6 @@ if (typeof mxVertexHandler != 'undefined')
 					
 					if (this.isSpaceEvent(me))
 					{
-						// TODO: Check locked state
 						var right = this.x + this.width;
 						var bottom = this.y + this.height;
 						var scale = this.graph.view.scale;
@@ -6221,6 +6199,19 @@ if (typeof mxVertexHandler != 'undefined')
 							this.width = this.graph.snap(this.width / scale) * scale;
 							this.height = this.graph.snap(this.height / scale) * scale;
 							
+							if (!this.graph.isGridEnabled())
+							{
+								if (this.width < this.graph.tolerance)
+								{
+									this.width = 0;
+								}
+								
+								if (this.height < this.graph.tolerance)
+								{
+									this.height = 0;
+								}
+							}
+							
 							if (this.x < this.first.x)
 							{
 								this.x = right - this.width;
@@ -6232,13 +6223,13 @@ if (typeof mxVertexHandler != 'undefined')
 							}
 						}
 						
+						this.div.style.borderStyle = 'dashed';
+						this.div.style.backgroundColor = 'white';
 						this.div.style.left = this.x + 'px';
-						this.div.style.top = (origin.y + offset.y) + 'px';
+						this.div.style.top = this.y + 'px';
 						this.div.style.width = Math.max(0, this.width) + 'px';
-						this.div.style.height = Math.max(0, this.graph.container.clientHeight) + 'px';
-						this.div.style.backgroundColor = 'white';
-						this.div.style.borderWidth = '0px 1px 0px 1px';
-						this.div.style.borderStyle = 'dashed';
+						this.div.style.height = this.graph.container.clientHeight + 'px';
+						this.div.style.borderWidth = (this.width <= 0) ? '0px 1px 0px 0px' : '0px 1px 0px 1px';
 						
 						if (this.secondDiv == null)
 						{
@@ -6246,11 +6237,11 @@ if (typeof mxVertexHandler != 'undefined')
 							this.div.parentNode.appendChild(this.secondDiv);
 						}
 						
-						this.secondDiv.style.left = (origin.x + offset.x) + 'px';
+						this.secondDiv.style.left = this.x + 'px';
 						this.secondDiv.style.top = this.y + 'px';
-						this.secondDiv.style.width = Math.max(0, this.graph.container.clientWidth) + 'px';
+						this.secondDiv.style.width = this.graph.container.clientWidth + 'px';
 						this.secondDiv.style.height = Math.max(0, this.height) + 'px';
-						this.secondDiv.style.borderWidth = '1px 0px 1px 0px';
+						this.secondDiv.style.borderWidth = (this.height <= 0) ? '1px 0px 0px 0px' : '1px 0px 1px 0px';
 					}
 					else
 					{

File diff suppressed because it is too large
+ 376 - 376
war/js/reader.min.js


File diff suppressed because it is too large
+ 38 - 38
war/js/viewer.min.js


File diff suppressed because it is too large
+ 23016 - 0
war/stencils.xml


File diff suppressed because it is too large
+ 11861 - 0
war/stencils/veeam/2d.xml


File diff suppressed because it is too large
+ 11155 - 0
war/stencils/veeam/3d.xml