瀏覽代碼

6.5.8 release

Former-commit-id: 09075f8fee58d33e7bc209000e56547b52cc1b44
David Benson 8 年之前
父節點
當前提交
e2d4b77308

+ 7 - 0
ChangeLog

@@ -1,3 +1,10 @@
+08-MAY-2017: 6.5.8
+
+- Adds .vssx stencil import
+- Adds square, circle, partial rectangles in sidebar
+- Adds identity configuration for CSV import
+- Uses mxGraph 3.7.3 beta 4
+
 29-APR-2017: 6.5.7
 
 - Fixes default and button labels for lost changes dialog

+ 1 - 1
VERSION

@@ -1 +1 @@
-6.5.7
+6.5.8

File diff suppressed because it is too large
+ 1 - 1
etc/mxgraph/mxClient.js


+ 57 - 29
src/com/mxgraph/io/mxVsdxCodec.java

@@ -42,7 +42,6 @@ import com.mxgraph.io.vsdx.VsdxShape;
 import com.mxgraph.io.vsdx.mxPathDebug;
 import com.mxgraph.io.vsdx.mxVsdxConnect;
 import com.mxgraph.io.vsdx.mxVsdxConstants;
-import com.mxgraph.io.vsdx.mxVsdxGeometry;
 import com.mxgraph.io.vsdx.mxVsdxGeometryList;
 import com.mxgraph.io.vsdx.mxVsdxMaster;
 import com.mxgraph.io.vsdx.mxVsdxModel;
@@ -68,6 +67,13 @@ import com.mxgraph.view.mxGraphHeadless;
  */
 public class mxVsdxCodec
 {
+	protected String RESPONSE_END = "</mxfile>";
+
+	protected String RESPONSE_DIAGRAM_START = "";
+	protected String RESPONSE_DIAGRAM_END = "</diagram>";
+
+	protected String RESPONSE_HEADER = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><mxfile>";
+
 	/**
 	 * Stores the vertexes imported.
 	 */
@@ -97,6 +103,8 @@ public class mxVsdxCodec
 	 * Do not remove, ask David
 	 */
 	public static String vsdxPlaceholder = new String(Base64.decodeBase64("dmlzaW8="));
+
+	protected mxVsdxModel vsdxModel;
 	
 	public mxVsdxCodec()
 	{
@@ -260,12 +268,12 @@ public class mxVsdxCodec
 			return null;
 		}
 
-		mxVsdxModel vsdxModel = new mxVsdxModel(rootDoc, docData, mediaData);
+		vsdxModel = new mxVsdxModel(rootDoc, docData, mediaData);
 
 		//Imports each page of the document.
 		Map<Integer, mxVsdxPage> pages = vsdxModel.getPages();
 
-		StringBuilder xmlBuilder = new StringBuilder("<?xml version=\"1.0\" encoding=\"UTF-8\"?><mxfile>");
+		StringBuilder xmlBuilder = new StringBuilder(RESPONSE_HEADER);
 
 		for (Map.Entry<Integer, mxVsdxPage> entry : pages.entrySet())
 		{
@@ -273,15 +281,7 @@ public class mxVsdxCodec
 			
 			if (!page.isBackground())
 			{
-				mxGraph graph = new mxGraphHeadless();
-				//Disable parent (groups) auto extend feature as it miss with the coordinates of vsdx format
-				graph.setExtendParents(false);
-				graph.setExtendParentsOnAdd(false);
-				
-				graph.setConstrainChildren(false);
-				graph.setHtmlLabels(true);
-				//Prevent change of edge parent as it misses with the routing points
-				((mxGraphModel)graph.getModel()).setMaintainEdgeParent(false);
+				mxGraph graph = createMxGraph();
 				
 				graph.getModel().beginUpdate();
 				importPage(page, graph, graph.getDefaultParent());
@@ -333,27 +333,54 @@ public class mxVsdxCodec
 				}
 				
 				graph.getModel().endUpdate();
-
-				mxCodec codec = new mxCodec();
-				Node node = codec.encode(graph.getModel());
-				((Element) node).setAttribute("style", "default-style2");
-				String pageName = StringEscapeUtils.escapeXml11(page.getPageName());
-				xmlBuilder.append("<diagram name=\"" + pageName + "\">");
-				String modelString = mxXmlUtils.getXml(node);
-				String modelAscii = Utils.encodeURIComponent(modelString, Utils.CHARSET_FOR_URL_ENCODING);
-				byte[] modelBytes= Utils.deflate(modelAscii);
-				String output = mxBase64.encodeToString(modelBytes, false);
 				
-				xmlBuilder.append(output);
-				xmlBuilder.append("</diagram>");
+				xmlBuilder.append(RESPONSE_DIAGRAM_START);
+				xmlBuilder.append(processPage(graph, page));
+				xmlBuilder.append(RESPONSE_DIAGRAM_END);
 			}
 		}
 
-		xmlBuilder.append("</mxfile>");
+		xmlBuilder.append(RESPONSE_END);
 		
 		return xmlBuilder.toString();
 	}
 
+	protected mxGraph createMxGraph() {
+		mxGraph graph = new mxGraphHeadless();
+		//Disable parent (groups) auto extend feature as it miss with the coordinates of vsdx format
+		graph.setExtendParents(false);
+		graph.setExtendParentsOnAdd(false);
+		
+		graph.setConstrainChildren(false);
+		graph.setHtmlLabels(true);
+		//Prevent change of edge parent as it misses with the routing points
+		((mxGraphModel)graph.getModel()).setMaintainEdgeParent(false);
+		return graph;
+	}
+
+	protected String processPage(mxGraph graph, mxVsdxPage page) throws IOException
+	{
+		mxCodec codec = new mxCodec();
+		Node node = codec.encode(graph.getModel());
+		((Element) node).setAttribute("style", "default-style2");
+		String modelString = mxXmlUtils.getXml(node);
+		String modelAscii = Utils.encodeURIComponent(modelString, Utils.CHARSET_FOR_URL_ENCODING);
+		byte[] modelBytes= Utils.deflate(modelAscii);
+		
+		StringBuilder output = new StringBuilder();
+		
+		if (page != null)
+		{
+			String pageName = StringEscapeUtils.escapeXml11(page.getPageName());
+			output.append("<diagram name=\""); 
+			output.append(pageName);
+			output.append("\">");
+		}
+		output.append(mxBase64.encodeToString(modelBytes, false));
+		
+		return  output.toString();
+	}
+	
 	private boolean isJpg(byte[] emfData, int i) 
 	{
 		//the loop calling this function make sure that we still have 3 bytes in the buffer
@@ -574,7 +601,7 @@ public class mxVsdxCodec
 	 * @param parentHeight Height of the parent cell.
 	 * @return the new vertex added. null if 'shape' is not a vertex.
 	 */
-	private mxCell addShape(mxGraph graph, VsdxShape shape, Object parent, Integer pageId, double parentHeight)
+	protected mxCell addShape(mxGraph graph, VsdxShape shape, Object parent, Integer pageId, double parentHeight)
 	{
 		shape.parentHeight = parentHeight;
 
@@ -911,12 +938,13 @@ public class mxVsdxCodec
 		{
 			// Source is dangling
 			source = (mxCell) graph.insertVertex(parent, null, null,
-					beginXY.getX(), beginXY.getY(), 0, 0);
+					origBeginXY.getX(), origBeginXY.getY(), 0, 0);
 			fromConstraint = new mxPoint(0, 0);
 		}
 		//Else: Routing points will contain the exit/entry points, so no need to set the to/from constraint 
 
 		mxPoint endXY = edgeShape.getEndXY(parentHeight);
+		mxPoint originEndXY = new mxPoint(endXY);
 		endXY = calculateAbsolutePoint(parent, graph, endXY);
 		
 		mxPoint toConstraint = null;
@@ -929,7 +957,7 @@ public class mxVsdxCodec
 		{
 			// Target is dangling
 			target = (mxCell) graph.insertVertex(parent, null, null,
-					endXY.getX(), endXY.getY(), 0, 0);
+					originEndXY.getX(), originEndXY.getY(), 0, 0);
 			toConstraint = new mxPoint(0, 0);
 		}
 		//Else: Routing points will contain the exit/entry points, so no need to set the to/from constraint 
@@ -1106,7 +1134,7 @@ public class mxVsdxCodec
 	 * Post processes groups to remove leaf vertices that render nothing
 	 * @param group
 	 */
-	private void sanitiseGraph(mxGraph graph)
+	protected void sanitiseGraph(mxGraph graph)
 	{
 		Object root = graph.getModel().getRoot();
 		sanitiseCell(graph, root);

+ 210 - 0
src/com/mxgraph/io/mxVssxCodec.java

@@ -0,0 +1,210 @@
+package com.mxgraph.io;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.TransformerException;
+
+import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+
+import com.mxgraph.io.vsdx.ShapePageId;
+import com.mxgraph.io.vsdx.VsdxShape;
+import com.mxgraph.io.vsdx.mxVsdxConstants;
+import com.mxgraph.io.vsdx.mxVsdxMaster;
+import com.mxgraph.io.vsdx.mxVsdxPage;
+import com.mxgraph.io.vsdx.mxVsdxUtils;
+import com.mxgraph.model.mxCell;
+import com.mxgraph.model.mxGeometry;
+import com.mxgraph.model.mxGraphModel;
+import com.mxgraph.util.mxPoint;
+import com.mxgraph.view.mxGraph;
+
+public class mxVssxCodec extends mxVsdxCodec 
+{
+	public mxVssxCodec()
+	{
+		RESPONSE_END = "";
+		RESPONSE_DIAGRAM_START = "";
+		RESPONSE_DIAGRAM_END = "";
+		RESPONSE_HEADER = "";
+	}
+	
+	public String decodeVssx(byte[] data, String charset)
+			throws IOException, ParserConfigurationException, SAXException,
+			TransformerException
+	{
+		StringBuilder library = new StringBuilder("<mxlibrary>[");
+		
+		//process shapes in pages
+		String shapesInPages = decodeVsdx(data, charset);
+		
+		library.append(shapesInPages);
+		
+		//process shapes in master
+		Map<String, mxVsdxMaster> masterShapes = vsdxModel.getMasterShapes();
+		
+		//using the first page as a dummy one
+		mxVsdxPage page = vsdxModel.getPages().values().iterator().next();
+		
+		if (masterShapes != null)
+		{
+			StringBuilder shapes = new StringBuilder();
+			String comma = shapesInPages.isEmpty()? "" : ",";
+			for (mxVsdxMaster master : masterShapes.values())
+			{
+				mxGraph shapeGraph = createMxGraph();
+				
+				Element shapeElem = master.getMasterShape().getShape();
+				VsdxShape shape = new VsdxShape(page, shapeElem, !page.isEdge(shapeElem), masterShapes, null, vsdxModel);
+				mxCell cell = null;
+				
+				if (shape.isVertex())
+				{
+					edgeShapeMap.clear();
+					parentsMap.clear();
+					cell = addShape(shapeGraph, shape, shapeGraph.getDefaultParent(),
+							0, 1169); //1169 is A4 page height
+					
+					for ( Entry<ShapePageId, VsdxShape> edgeEntry : edgeShapeMap.entrySet()) 
+					{
+						Object parent = parentsMap.get(edgeEntry.getKey());
+						addUnconnectedEdge(shapeGraph, parent, edgeEntry.getValue(), 1169); //1169 is A4 page height
+					}
+				}
+				else
+				{
+					cell = (mxCell) addUnconnectedEdge(shapeGraph, null, shape, 1169); //1169 is A4 page height
+				}
+				
+				if (cell != null)
+				{
+					shapes.append(comma);
+					shapes.append("{\"xml\":\"");
+					mxGeometry geo = normalizeGeo(cell);
+					
+					sanitiseGraph(shapeGraph);
+					
+					if (shapeGraph.getModel().getChildCount(shapeGraph.getDefaultParent()) == 0) continue;
+				
+					String shapeXML = super.processPage(shapeGraph, null);
+					shapes.append(shapeXML);
+					shapes.append("\",\"w\":");
+					shapes.append(geo.getWidth());
+					shapes.append(",\"h\":");
+					shapes.append(geo.getHeight());
+					shapes.append(",\"title\":\"");
+					
+					String shapeName = master.getName();
+					if (shapeName != null) shapeName = mxVsdxUtils.htmlEntities(shapeName);
+					
+					shapes.append(shapeName);
+					
+					shapes.append("\"}");
+					comma = ",";
+				}
+			}
+			library.append(shapes);
+		}
+		library.append("]</mxlibrary>");
+		
+		//TODO UTF-8 support is missing
+		//
+//		System.out.println(library);
+		
+		return library.toString();
+	}
+
+	protected mxGeometry normalizeGeo(mxCell cell) {
+		mxGeometry geo = cell.getGeometry();
+		geo.setX(0);
+		geo.setY(0);
+		
+		mxPoint srcP = geo.getSourcePoint();
+
+		if (cell.isEdge() && srcP != null)
+		{
+			transPoint(geo.getTargetPoint(), srcP);
+			transPoint(geo.getOffset(), srcP);
+			List<mxPoint> points = geo.getPoints();
+			
+			if (points != null) 
+			{
+				for (mxPoint p : points) 
+				{
+					transPoint(p, srcP);
+				}
+			}
+			transPoint(srcP, srcP);
+		}
+		return geo;
+	}
+
+	protected void transPoint(mxPoint p, mxPoint srcP) 
+	{
+		if (p != null)
+		{
+			p.setX(p.getX() - srcP.getX());
+			p.setY(p.getY() - srcP.getY());
+		}
+	}
+
+	@Override
+	protected String processPage(mxGraph graph, mxVsdxPage page) throws IOException {
+		mxGraphModel model = (mxGraphModel)graph.getModel();
+		
+		StringBuilder shapes = new StringBuilder(); 
+		String comma = "";
+		for (Object c : model.getCells().values()) 
+		{
+			//add top level shapes only to the library
+			if (graph.getDefaultParent() == model.getParent(c))
+			{
+				shapes.append(comma);
+				shapes.append("{\"xml\":\"");
+				mxGraph shapeGraph = createMxGraph();
+				shapeGraph.addCell(c);
+				sanitiseGraph(shapeGraph);
+				
+				if (shapeGraph.getModel().getChildCount(shapeGraph.getDefaultParent()) == 0) continue;
+				
+				mxGeometry geo = normalizeGeo((mxCell) c);
+				String shapeXML = super.processPage(shapeGraph, null);
+				shapes.append(shapeXML);
+				shapes.append("\",\"w\":");
+				shapes.append(geo.getWidth());
+				shapes.append(",\"h\":");
+				shapes.append(geo.getHeight());
+				shapes.append(",\"title\":\"");
+				String style = model.getStyle(c);
+				
+				String name = "";
+				if (style != null)
+				{
+					int p = style.indexOf(mxVsdxConstants.VSDX_ID);
+					if (p >= 0)
+					{
+						p += mxVsdxConstants.VSDX_ID.length() + 1;
+						int id = Integer.parseInt(style.substring(p, style.indexOf(";", p)));
+						VsdxShape vsdxShape = vertexShapeMap.get(new ShapePageId(page.getId(), id));
+						if (vsdxShape != null)
+							name = vsdxShape.getName();
+					}
+				}
+				shapes.append(name);
+				shapes.append("\"}");
+				comma = ",";
+			}
+		}
+		
+		if (shapes.length() > 0) 
+			RESPONSE_DIAGRAM_START = ",";
+		else
+			RESPONSE_DIAGRAM_START = "";
+		
+		return shapes.toString();
+	}
+}

+ 55 - 10
src/com/mxgraph/io/vsdx/Shape.java

@@ -4,7 +4,9 @@
  */
 package com.mxgraph.io.vsdx;
 
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -20,6 +22,7 @@ import com.mxgraph.util.mxConstants;
 
 public class Shape extends Style
 {
+	public final static long VSDX_START_TIME = -2209168800000L;//new Date("12/30/1899").getTime(); 
 	/**
 	 * The text element of the shape, if any
 	 */
@@ -275,25 +278,67 @@ public class Shape extends Style
 			for (Element row : rows)
 			{
 				String ix = row.getAttribute("IX");
-				ArrayList<Element> cells = mxVsdxUtils.getDirectChildNamedElements(row, "Cell");
-				
-				for (Element cell : cells)
+
+				if (!ix.isEmpty())
 				{
-					n = cell.getAttribute("N");
+					if (this.fields == null)
+					{
+						fields = new LinkedHashMap<String, String>();
+					}
+	
+					String del = row.getAttribute("Del");
 					
-					if (n.equals("Value"))
+					//supporting deletion of a field by adding an empty string such that master field is not used
+					if ("1".equals(del))
+					{
+						this.fields.put(ix, "");
+						continue;
+					}
+					
+					ArrayList<Element> cells = mxVsdxUtils.getDirectChildNamedElements(row, "Cell");
+	
+					String value = "", format = "", calendar = "", type = "";
+					for (Element cell : cells)
 					{
+						n = cell.getAttribute("N");
 						String v = cell.getAttribute("V");
 						
-						if (!ix.isEmpty() && !v.isEmpty())
+						switch (n)
 						{
-							if (this.fields == null)
+							case "Value":
+								value = v;
+							break;
+							case "Format":
+								format = v;
+							break;
+							case "Calendar":
+								calendar = v;
+							break;
+							case "Type":
+								type = v;
+							break;
+						}
+					}
+					
+					if (!value.isEmpty())
+					{
+						
+						try
+						{
+							//TODO support other formats and calendars
+							if (format.startsWith("{{"))
 							{
-								fields = new LinkedHashMap<String, String>();
+								value = new SimpleDateFormat(
+										format.replaceAll("\\{|\\}", ""))
+										//the value is the number of days after 30/12/1899 (VSDX_START_TIME)
+										.format(new Date(VSDX_START_TIME + (long)(Double.parseDouble(value) * 24 * 60 * 60 * 1000)));
 							}
-
-							this.fields.put(ix, v);
 						}
+						catch (Exception e)
+						{
+	//						System.out.println("Vsdx import: Unkown text format " + format + ". Error: " + e.getMessage());
+						}
+						this.fields.put(ix, value);
 					}
 				}
 			}

+ 9 - 0
src/com/mxgraph/io/vsdx/Style.java

@@ -630,6 +630,15 @@ public class Style
 		return shape.getAttribute(mxVsdxConstants.NAME_U);
 	}
 
+	/**
+	 * Returns the Name attribute.
+	 * @return Value of the Name attribute (Human readable name).
+	 */
+	public String getName()
+	{
+		return shape.getAttribute(mxVsdxConstants.NAME);
+	}
+
 	/**
 	 * Returns the UniqueID attribute.
 	 * @return Value of the UniqueID attribute.

+ 55 - 15
src/com/mxgraph/io/vsdx/VsdxShape.java

@@ -251,20 +251,35 @@ public class VsdxShape extends Shape
 			this.childShapes = page.parseShapes(shapesElement, this.master, false);
 		}
 		
-		for (Map.Entry<Integer, VsdxShape> entry : childShapes.entrySet())
-		{
-		    entry.getValue().setRootShape(this);
-		}
 		
 		double rotation = this.calcRotation();
 		this.rotation = rotation * 100/100;
 		this.rotation = this.rotation % 360.0;
 		
-		mxVsdxTheme	theme = model.getThemes().get(page.getCellIntValue("ThemeIndex", 33));
+		int themeIndex = page.getCellIntValue("ThemeIndex", -100);
+		
+		//sometimes theme information are at the shape level!
+		if (themeIndex == -100)
+		{
+			themeIndex = Integer.parseInt(this.getValue(this.getCellElement("ThemeIndex"), "0"));
+		}
+		
+		mxVsdxTheme	theme = model.getThemes().get(themeIndex);
 		int variant = page.getCellIntValue("VariationColorIndex", 0);
 		
 		setThemeAndVariant(theme, variant);
 		
+		for (Map.Entry<Integer, VsdxShape> entry : childShapes.entrySet())
+		{
+			VsdxShape childShape = entry.getValue();
+		    childShape.setRootShape(this);
+		    
+		    if (childShape.theme == null)
+		    {
+		    	childShape.setThemeAndVariant(theme, variant);
+		    }
+		}
+
 		quickStyleVals = new QuickStyleVals(
 				Integer.parseInt(this.getValue(this.getCellElement("QuickStyleEffectsMatrix"), "0")), 
 				Integer.parseInt(this.getValue(this.getCellElement("QuickStyleFillColor"), "1")), 
@@ -294,7 +309,7 @@ public class VsdxShape extends Shape
 		}
 		//several shapes have beginX/Y and also has a fill color, thus it is better to render it as a vertex
 		//vsdx can have an edge as a group!
-		this.vertex = vertex || isGroup() || (geomList != null && !geomList.isNoFill());
+		this.vertex = vertex || (childShapes != null && !childShapes.isEmpty()) || (geomList != null && !geomList.isNoFill());
 	}
 	
 	/**
@@ -323,6 +338,13 @@ public class VsdxShape extends Shape
 	 */
 	public String getTextLabel()
 	{
+		String hideText = this.getValue(this.getCellElement(mxVsdxConstants.HIDE_TEXT), "0");
+		
+		if ("1".equals(hideText))
+		{
+			return null;
+		}
+		
 		NodeList txtChildren = getTextChildren();
 
 		if (txtChildren == null && masterShape != null)
@@ -593,11 +615,7 @@ public class VsdxShape extends Shape
 
 	private String processLblTxt(String text) {
 		// It's HTML text, so escape it.
-		text = text.replaceAll("&", "&amp;")
-				.replaceAll("\"", "&quot;")
-				.replaceAll("'", "&prime;")
-				.replaceAll("<", "&lt;")
-				.replaceAll(">", "&gt;");
+		text = mxVsdxUtils.htmlEntities(text);
 
 		text = textToList(text, pp);
 
@@ -914,7 +932,7 @@ public class VsdxShape extends Shape
 	 */
 	public Map<String, String> getStyleFromShape()
 	{
-		styleMap.put("vsdxID", this.getId().toString());
+		styleMap.put(mxVsdxConstants.VSDX_ID, this.getId().toString());
 		
 		// Rotation.		
 //		String labelRotation = getLabelRotation() ? "1" : "0";
@@ -1070,7 +1088,7 @@ public class VsdxShape extends Shape
 		}
 		
 		String flibX = getValue(this.getCellElement(mxVsdxConstants.FLIP_X), "0");
-		String flibY = getValue(this.getCellElement(mxVsdxConstants.FLIP_X), "0");
+		String flibY = getValue(this.getCellElement(mxVsdxConstants.FLIP_Y), "0");
 		
 		if ("1".equals(flibX))
 		{
@@ -1270,6 +1288,12 @@ public class VsdxShape extends Shape
 	public boolean isRounded()
 	{
 		String val = getValue(this.getCellElement(mxVsdxConstants.ROUNDING), "0");
+		
+		if ("Themed".equals(val))
+		{
+			//TODO add theme support 
+			val = "0";
+		}
 		return Double.valueOf(val) > 0;
 	}
 
@@ -1393,7 +1417,23 @@ public class VsdxShape extends Shape
 		
 		return result;
 	}
-	
+
+	/**
+	 * Returns the Name attribute.
+	 * @return Value of the Name attribute (Human readable name).
+	 */
+	public String getName()
+	{
+		String result = shape.getAttribute(mxVsdxConstants.NAME);
+		
+		if ((result == null || result.equals("")) && masterShape != null)
+		{
+			result = masterShape.getName();
+		}
+		
+		return result;
+	}
+
 	/**
 	 * Returns the master name of the shape
 	 * @return Master name of the shape
@@ -1954,7 +1994,7 @@ public class VsdxShape extends Shape
 	 */
 	public Map<String, String> getStyleFromEdgeShape(double parentHeight)
 	{
-		styleMap.put("vsdxID", this.getId().toString());
+		styleMap.put(mxVsdxConstants.VSDX_ID, this.getId().toString());
 
 		// Rotation.
 //		double rotation = this.getRotation();

+ 3 - 0
src/com/mxgraph/io/vsdx/mxVsdxConstants.java

@@ -140,6 +140,9 @@ public class mxVsdxConstants
 	public static String Y_CON = "YCon";
 	public static String Y_DYN = "YDyn";
 	public static String Y = "Y";
+	public static String HIDE_TEXT = "HideText";
+	
+	public static String VSDX_ID = "vsdxID";
 	
 	public static int CONNECT_TO_PART_WHOLE_SHAPE = 3;
 	

+ 1 - 1
src/com/mxgraph/io/vsdx/mxVsdxGeometryList.java

@@ -110,7 +110,7 @@ public class mxVsdxGeometryList
 		List<mxPoint> points = new ArrayList<mxPoint>();
 		
 		//Adding the starting point as a routing point instead of setting the entryX/Y
-		points.add(startPoint);
+		points.add(new mxPoint(startPoint));
 		
 		double offsetX = 0;
 		double offsetY = 0;

+ 10 - 9
src/com/mxgraph/io/vsdx/mxVsdxModel.java

@@ -115,20 +115,21 @@ public class mxVsdxModel {
 						{
 							mxVsdxTheme theme = new mxVsdxTheme((Element) child);
 							
-							if (theme.getThemeIndex() > -1)
-							{
-								mxVsdxTheme existingTheme = themes.get(theme.getThemeIndex());
-								if (existingTheme == null || !existingTheme.isPure())
-								{
-									themes.put(theme.getThemeIndex(), theme);
-								}
-							}
-							else
+							
+							if (theme.getThemeIndex() < 0)
 							{
 								//theme index cannot be determined unless the theme is parsed
 								theme.processTheme();
+							}
+							
+							//TODO having two theme files with the same id still requires more handling
+							//		probably we need to merge the similar parts (has same theme name)
+							mxVsdxTheme existingTheme = themes.get(theme.getThemeIndex());
+							if (existingTheme == null || !existingTheme.isPure())
+							{
 								themes.put(theme.getThemeIndex(), theme);
 							}
+							
 							break;
 						}
 						

+ 1 - 1
src/com/mxgraph/io/vsdx/mxVsdxPage.java

@@ -246,7 +246,7 @@ public class mxVsdxPage {
 		return new VsdxShape(this, shapeElem, vertex, this.model.getMasterShapes(), masterTmp, this.model);
 	}
 
-	protected boolean isEdge(Element shape)
+	public boolean isEdge(Element shape)
 	{
 		if (shape != null)
 		{

+ 50 - 25
src/com/mxgraph/io/vsdx/mxVsdxTheme.java

@@ -11,6 +11,7 @@ import com.mxgraph.io.vsdx.theme.Color;
 import com.mxgraph.io.vsdx.theme.FillStyle;
 import com.mxgraph.io.vsdx.theme.FillStyleFactory;
 import com.mxgraph.io.vsdx.theme.GradFill;
+import com.mxgraph.io.vsdx.theme.HSLColor;
 import com.mxgraph.io.vsdx.theme.LineStyle;
 import com.mxgraph.io.vsdx.theme.LineStyleExt;
 import com.mxgraph.io.vsdx.theme.OoxmlColor;
@@ -600,28 +601,39 @@ public class mxVsdxTheme
 			break;
 		}
 		
+		Color retColor;
 		if (fillStyle != null)
 		{
 			if (getGradient)
 			{
-				return (fillStyle instanceof GradFill)? fillStyle.applyStyle(fillColorStyle, this).getGradientClr() : null;
+				retColor = (fillStyle instanceof GradFill)? fillStyle.applyStyle(fillColorStyle, this).getGradientClr() : null;
 			}
 			else
 			{
-				return fillStyle.applyStyle(fillColorStyle, this);
+				retColor = fillStyle.applyStyle(fillColorStyle, this);
 			}
 		}
 		else
 		{
 			if (getGradient)
 			{
-				return null;
+				retColor = null;
 			}
 			else
 			{
-				return getStyleColor(fillColorStyle);
+				retColor = getStyleColor(fillColorStyle);
 			}
 		}
+		
+		int styleVariation = quickStyleVals.getQuickStyleVariation();
+		
+		//TODO using the line color does not cover all the cases but works with most of the sample files
+		if (retColor != null && (styleVariation & 8) > 0)
+		{
+			retColor = getLineColor(quickStyleVals);
+		}
+		
+		return retColor;
 	}
 	
 	//Get line style based on QuickStyleLineMatrix
@@ -699,14 +711,25 @@ public class mxVsdxTheme
 			break;
 		}
 		
+		Color lineClr;
+		
 		if (lineStyle != null)
 		{
-			return lineStyle.getLineColor(lineColorStyle, this);
+			lineClr = lineStyle.getLineColor(lineColorStyle, this);
 		}
 		else
 		{
-			return getStyleColor(lineColorStyle);
-		}		
+			lineClr = getStyleColor(lineColorStyle);
+		}
+		
+		int styleVariation = quickStyleVals.getQuickStyleVariation();
+		
+		//TODO using the fill color does not cover all the cases but works with most of the sample files
+		if ((styleVariation & 4) > 0)
+		{
+			lineClr = getFillColor(quickStyleVals);
+		}
+		return lineClr;
 	}
 
 	//Get line color based on QuickStyleLineColor & QuickStyleLineMatrix
@@ -860,12 +883,14 @@ public class mxVsdxTheme
 			txtColor = getStyleColor(fontColorStyle);
 		}
 		
-//		int styleVariation = quickStyleVals.getQuickStyleVariation();
-//		
-//		if ((styleVariation & 2) > 0)
-//		{
-//			Color fillColor = getFillColor(quickStyleVals);
-//			HSLColor fillHSLClr = fillColor.toHsl();
+		int styleVariation = quickStyleVals.getQuickStyleVariation();
+		
+		//TODO The formula in the documentation doesn't match how vsdx viewer works. Simply using the fill/line color works!
+		//		Note: Using the fill/line color does not cover all the cases but works with most of the sample files
+		if ((styleVariation & 2) > 0)
+		{
+			Color fillColor = getFillColor(quickStyleVals);
+			HSLColor fillHSLClr = fillColor.toHsl();
 //			HSLColor txtColorHSL = txtColor.toHsl();
 //			if (Math.abs(fillHSLClr.getLum() - txtColorHSL.getLum()) < 0.1616)
 //			{
@@ -875,20 +900,20 @@ public class mxVsdxTheme
 //				}
 //				else
 //				{
-//					Color lineClr = getLineColor(quickStyleVals);
-//					HSLColor lineHSLClr = lineClr.toHsl();
-//					if (fillHSLClr.getLum() < lineHSLClr.getLum())
-//					{
-//						txtColor = fillColor;
-//					}
-//					else
-//					{
-//						txtColor = lineClr;
-//					}
+					Color lineClr = getLineColor(quickStyleVals);
+					HSLColor lineHSLClr = lineClr.toHsl();
+					if (fillHSLClr.getLum() < lineHSLClr.getLum())
+					{
+						txtColor = fillColor;
+					}
+					else
+					{
+						txtColor = lineClr;
+					}
 //				}
 //			}
-//		}
-//		
+		}
+		
 		return txtColor;
 	}
 

+ 14 - 0
src/com/mxgraph/io/vsdx/mxVsdxUtils.java

@@ -169,6 +169,20 @@ public class mxVsdxUtils
 		return "<" + tag + ">" + text + "</" + tag + ">";
 	}
 
+	
+	/**
+	 * Converts the ampersand, quote, prime, less-than and greater-than
+	 * characters to their corresponding HTML entities in the given string.
+	 * 
+	 * Note: this is the same method of mxUtils but we cannot use it as it is not compatible with google app engine
+	 */
+	public static String htmlEntities(String text)
+	{
+		return text.replaceAll("&", "&amp;").replaceAll("\"", "&quot;")
+				.replaceAll("'", "&prime;").replaceAll("<", "&lt;")
+				.replaceAll(">", "&gt;");
+	}
+	
 	/**
 	 * Converts the initial letter  of each word in text to uppercase
 	 * @param text Text to be transformed.

+ 18 - 1
src/com/mxgraph/online/OpenServlet.java

@@ -28,6 +28,7 @@ import org.apache.commons.fileupload.util.Streams;
 import com.mxgraph.io.mxCodec;
 import com.mxgraph.io.mxGraphMlCodec;
 import com.mxgraph.io.mxVsdxCodec;
+import com.mxgraph.io.mxVssxCodec;
 import com.mxgraph.io.gliffy.importer.GliffyDiagramConverter;
 import com.mxgraph.util.mxXmlUtils;
 import com.mxgraph.view.mxGraph;
@@ -48,6 +49,11 @@ public class OpenServlet extends HttpServlet
 	 */
 	public static boolean ENABLE_VSDX_SUPPORT = true;
 
+	/**
+	 * Global switch to enabled VSSX support.
+	 */
+	public static boolean ENABLE_VSSX_SUPPORT = true;
+
 	/**
 	 * Global switch to enabled Gliffy support.
 	 */
@@ -102,6 +108,7 @@ public class OpenServlet extends HttpServlet
 				String format = null;
 				String upfile = null;
 				boolean vsdx = false;
+				boolean vssx = false;
 
 				ServletFileUpload upload = new ServletFileUpload();
 				FileItemIterator iterator = upload.getItemIterator(request);
@@ -120,8 +127,9 @@ public class OpenServlet extends HttpServlet
 					{
 						filename = item.getName();
 						vsdx = filename.toLowerCase().endsWith(".vsdx");
+						vssx = filename.toLowerCase().endsWith(".vssx");
 						
-						if (vsdx)
+						if (vsdx || vssx)
 						{
 							upfile = Streams.asString(stream, "ISO-8859-1");  
 						}
@@ -168,6 +176,15 @@ public class OpenServlet extends HttpServlet
 					int dot = filename.lastIndexOf('.');
 					filename = filename.substring(0, dot + 1) + "xml";
 				}
+				else if (ENABLE_VSSX_SUPPORT && vssx)
+				{
+					mxVssxCodec vssxCodec = new mxVssxCodec();
+					xml = vssxCodec.decodeVssx(upfile.getBytes("ISO-8859-1"), Utils.CHARSET_FOR_URL_ENCODING);
+
+					// Replaces VSDX extension
+					int dot = filename.lastIndexOf('.');
+					filename = filename.substring(0, dot + 1) + "xml";
+				}
 				else if (ENABLE_GLIFFY_SUPPORT && upfile.matches(gliffyRegex))
 				{
 					GliffyDiagramConverter converter = new GliffyDiagramConverter(

+ 1 - 1
war/cache.manifest

@@ -1,7 +1,7 @@
 CACHE MANIFEST
 
 # THIS FILE WAS GENERATED. DO NOT MODIFY!
-# 04/29/2017 11:28 AM
+# 05/08/2017 10:18 PM
 
 app.html
 index.html?offline=1

+ 0 - 1
war/index.html

@@ -15,7 +15,6 @@
     <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
     <meta name="mobile-web-app-capable" content="yes">
     <meta name="apple-mobile-web-app-capable" content="yes">
-    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
     <link rel="canonical" href="https://www.draw.io">
     <script type="text/javascript">
 		/**

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


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


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


+ 16 - 10
war/js/diagramly/App.js

@@ -50,6 +50,7 @@ App = function(editor, container, lightbox)
 	});
 	
 	// Initial state for toolbar items is disabled
+	this.updateDocumentTitle();
 	this.updateUi();
 	
 	// Checks if canvas is supported
@@ -839,8 +840,10 @@ App.prototype.init = function()
 											'<img border="0" align="absmiddle" style="margin-top:-4px;" src="images/glyphicons_star.png"/>&nbsp;&nbsp;Google Docs Add-on</a>',
 											'<a title="Google Chrome App" href="https://chrome.google.com/webstore/detail/drawio-desktop/pebppomjfocnoigkeepgbmcifnnlndla" target="_blank">' +
 											'<img border="0" align="absmiddle" style="margin-top:-4px;" src="images/glyphicons_star.png"/>&nbsp;&nbsp;Google Chrome App</a>',
-											'<a title="Please help us to 5 stars" href="https://chrome.google.com/webstore/detail/drawio-pro/onlkggianjhjenigcpigpjehhpplldkc/reviews" target="_blank">' +
-											'<img border="0" align="absmiddle" style="margin-top:-4px;" src="images/glyphicons_star.png"/>&nbsp;&nbsp;Please help us to 5 stars</a>'];
+//											'<a title="Please help us to 5 stars" href="https://chrome.google.com/webstore/detail/drawio-pro/onlkggianjhjenigcpigpjehhpplldkc/reviews" target="_blank">' +
+//											'<img border="0" align="absmiddle" style="margin-top:-4px;" src="images/glyphicons_star.png"/>&nbsp;&nbsp;Please help us to 5 stars</a>'];
+											'<a title="Please star this issue" href="https://issuetracker.google.com/issues/36761624" target="_blank">' +
+											'<img border="0" align="absmiddle" style="margin-top:-4px;" src="images/glyphicons_star.png"/>&nbsp;&nbsp;Please star this issue</a>'];
 							this.updateAd(this.adsHtml.length - 1);
 						}
 						
@@ -1470,17 +1473,20 @@ App.prototype.updateDocumentTitle = function()
 	if (!this.editor.graph.lightbox)
 	{
 		var title = this.editor.appName;
-		var file = this.getCurrentFile();
-		
-		if (file != null)
+
+		if (this.isOfflineApp())
 		{
-			var filename = (file.getTitle() != null) ? file.getTitle() : this.defaultFilename;
-			title = filename + ' - ' + title;
+			title += ' ' + mxResources.get('offline');
 		}
-		
-		if (this.isOfflineApp())
+		else
 		{
-			title += ' [' + mxResources.get('offline') + ']';
+			var file = this.getCurrentFile();
+			
+			if (file != null)
+			{
+				var filename = (file.getTitle() != null) ? file.getTitle() : this.defaultFilename;
+				title = filename + ' - ' + title;
+			}
 		}
 		
 		document.title = title;

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

@@ -58,6 +58,11 @@
 		'#\n' +
 		'# style: label;image=%image%;whiteSpace=wrap;html=1;rounded=1;fillColor=%fill%;strokeColor=%stroke%;\n' +
 		'#\n' +
+		'## Uses the given column name as the identity for cells (updates existing cells).\n' +
+		'## Default is no identity (empty value or -).\n' +
+		'#\n' +
+		'# identity: -\n' +
+		'#\n' +
 		'## Connections between rows ("from": source colum, "to": target column).\n' +
 		'## Label, style and invert are optional. Defaults are \'\', current style and false.\n' +
 		'## The target column may contain a comma-separated list of values.\n' +

+ 48 - 6
war/js/diagramly/EditorUi.js

@@ -4866,7 +4866,8 @@
 	{
 		return /(\.*<graphml xmlns=\".*)/.test(data) ||
 			/(\"contentType\":\s*\"application\/gliffy\+json\")/.test(data) ||
-			(filename != null && /(\.vsdx)($|\?)/i.test(filename));
+			(filename != null && /(\.vsdx)($|\?)/i.test(filename)) ||
+			(filename != null && /(\.vssx)($|\?)/i.test(filename));
 	};
 	
 	/**
@@ -4930,7 +4931,16 @@
 					
 					if (xhr.status >= 200 && xhr.status <= 299)
 					{
-						importedCells = this.importXml(xhr.responseText, dx, dy, crop);
+						var xml = xhr.responseText;
+						
+						if (xml != null && xml.substring(0, 10) == '<mxlibrary')
+						{
+							this.loadLibrary(new LocalLibrary(this, xml, filename));
+						}
+						else
+						{
+							importedCells = this.importXml(xml, dx, dy, crop);
+						}
 					}
 					
 					if (done != null)
@@ -5266,7 +5276,7 @@
 					});
 					
 					// Handles special case of binary file where the reader should not be used
-					if (/(\.vsdx)($|\?)/i.test(file.name))
+					if (/(\.vsdx)($|\?)/i.test(file.name) || /(\.vssx)($|\?)/i.test(file.name))
 					{
 						fn(null, file.type, x + index * gs, y + index * gs, 240, 160, file.name, function(cells)
 						{
@@ -7607,6 +7617,7 @@
         		
         		// Default values
         		var style = null;
+        		var identity = null;
         		var width = 'auto';
         		var height = 'auto';
         		var edgespacing = 40;
@@ -7681,6 +7692,10 @@
 		    				{
 		    					style = value;
 		    				}
+		    				else if (key == 'identity' && value.length > 0 && value != '-')
+		    				{
+		    					identity = value;
+		    				}
 		    				else if (key == 'width')
 		    				{
 		    					width = value;
@@ -7723,6 +7738,21 @@
         		
     			var keys = this.editor.csvToArray(lines[index]);
     			
+    			// Converts name of identity to index of column
+    			var identityIndex = null;
+    			
+    			if (identity != null)
+    			{
+    				for (var i = 0; i < keys.length; i++)
+		    		{
+    					if (identity == keys[i])
+    					{
+    						identityIndex = i;
+    						break;
+    					}
+		    		}
+    			}
+    			
     			if (label == null)
     			{
     				label = '%' + keys[0] + '%';
@@ -7748,9 +7778,21 @@
     	    			
 	    				if (values.length == keys.length)
 		    			{
-			    			var cell = new mxCell(label, new mxGeometry(x0, y,
-			    				0, 0), style || 'whiteSpace=wrap;html=1;');
-							cell.vertex = true;
+	    					var cell = null;
+	    					var id = (identityIndex != null) ? values[identityIndex] : null;
+	    					
+	    					if (id != null)
+	    					{
+	    						cell = graph.model.getCell(id);
+	    					}
+
+	    					if (cell == null)
+	    					{
+				    			var cell = new mxCell(label, new mxGeometry(x0, y,
+				    				0, 0), style || 'whiteSpace=wrap;html=1;');
+								cell.vertex = true;
+								cell.id = id;
+	    					}
 							
 							for (var j = 0; j < values.length; j++)
 				    		{

+ 56 - 10
war/js/diagramly/ElectronApp.js

@@ -366,7 +366,11 @@ FeedbackDialog.feedbackUrl = 'https://log.draw.io/email';
 	{
 		var fn = mxUtils.bind(this, function()
 		{
-			var doSave = mxUtils.bind(this, function(data)
+			// Disables temporary file state
+			this.mode = App.MODE_DEVICE;
+			this.ui.mode = this.mode;
+			
+			var doSave = mxUtils.bind(this, function(data, enc)
 			{
 				if (!this.savingFile)
 				{
@@ -378,7 +382,7 @@ FeedbackDialog.feedbackUrl = 'https://log.draw.io/email';
 					this.setModified(false);
 					var fs = require('fs');
 					
-					fs.writeFile(this.fileObject.path, data, this.fileObject.encoding, mxUtils.bind(this, function (e)
+					fs.writeFile(this.fileObject.path, data, enc || this.fileObject.encoding, mxUtils.bind(this, function (e)
 				    {
 		        		if (e)
 		        		{
@@ -418,14 +422,48 @@ FeedbackDialog.feedbackUrl = 'https://log.draw.io/email';
 			}
 			else
 			{
+				var graph = this.ui.editor.graph;
+				
+				// Exports PNG for first page while other page is visible by creating a graph
+				// LATER: Add caching for the graph or SVG while not on first page
+				if (this.ui.pages != null && this.ui.currentPage != this.ui.pages[0])
+				{
+					graph = this.ui.createTemporaryGraph(graph.getStylesheet());
+					var graphGetGlobalVariable = graph.getGlobalVariable;
+					var page = this.ui.pages[0];
+			
+					graph.getGlobalVariable = function(name)
+					{
+						if (name == 'page')
+						{
+							return page.getName();
+						}
+						else if (name == 'pagenumber')
+						{
+							return 1;
+						}
+						
+						return graphGetGlobalVariable.apply(this, arguments);
+					};
+			
+					document.body.appendChild(graph.container);
+					graph.model.setRoot(page.root);
+				}
+				
 			   	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');
+		   	   	   			atob(this.ui.editor.graph.compress(this.ui.getFileData(true))));
+		   	   	   		doSave(atob(data.substring(data.lastIndexOf(',') + 1)), 'binary');
+
+						// Removes temporary graph from DOM
+		   	   	    	if (graph != this.ui.editor.graph)
+						{
+							graph.container.parentNode.removeChild(graph.container);
+						}
 			   		}
 			   		catch (e)
 			   		{
@@ -440,7 +478,7 @@ FeedbackDialog.feedbackUrl = 'https://log.draw.io/email';
 		   			{
 		   				error(e);
 		   			}
-			   	}));
+			   	}), null, null, null, null, null, null, graph);
 			}
 		});
 		
@@ -449,8 +487,8 @@ FeedbackDialog.feedbackUrl = 'https://log.draw.io/email';
 			const electron = require('electron');
 			var remote = electron.remote;
 			var dialog = remote.dialog;
-
-	        var path = dialog.showSaveDialog();
+			
+	        var path = dialog.showSaveDialog({defaultPath: this.title});
 
 	        if (path != null)
 	        {
@@ -472,8 +510,16 @@ FeedbackDialog.feedbackUrl = 'https://log.draw.io/email';
 		const electron = require('electron');
 		var remote = electron.remote;
 		var dialog = remote.dialog;
-
-        var path = dialog.showSaveDialog();
+		var filename = this.title;
+		
+		// Adds default extension
+		if (filename.length > 0 && (!/(\.xml)$/i.test(filename) && !/(\.html)$/i.test(filename) &&
+			!/(\.svg)$/i.test(filename) && !/(\.png)$/i.test(filename)))
+		{
+			filename += '.xml';
+		}
+		
+        var path = dialog.showSaveDialog({defaultPath: filename});
         
         if (path != null)
         {
@@ -610,7 +656,7 @@ FeedbackDialog.feedbackUrl = 'https://log.draw.io/email';
 		var remote = electron.remote;
 		var dialog = remote.dialog;
 
-        var path = dialog.showSaveDialog();
+        var path = dialog.showSaveDialog({defaultPath: filename});
 
         if (path != null)
         {

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


+ 4 - 0
war/js/mxgraph/Dialogs.js

@@ -1534,6 +1534,10 @@ var EditDiagramDialog = function(editorUi)
 	div.style.textAlign = 'right';
 	var textarea = document.createElement('textarea');
 	textarea.setAttribute('wrap', 'off');
+	textarea.setAttribute('spellcheck', 'false');
+	textarea.setAttribute('autocorrect', 'off');
+	textarea.setAttribute('autocomplete', 'off');
+	textarea.setAttribute('autocapitalize', 'off');
 	textarea.style.overflow = 'auto';
 	textarea.style.resize = 'none';
 	textarea.style.width = '600px';

+ 8 - 4
war/js/mxgraph/Sidebar.js

@@ -914,6 +914,10 @@ Sidebar.prototype.addGeneralPalette = function(expand)
 	 	this.createVertexTemplateEntry('shape=ext;double=1;rounded=1;whiteSpace=wrap;html=1;', 120, 60, '', 'Double Rounded Rectangle', null, null, 'rounded rect rectangle box double'),
  		this.createVertexTemplateEntry('ellipse;whiteSpace=wrap;html=1;', 120, 80, '', 'Ellipse', null, null, 'oval ellipse state'),
 	 	this.createVertexTemplateEntry('ellipse;shape=doubleEllipse;whiteSpace=wrap;html=1;', 120, 80, '', 'Double Ellipse', null, null, 'oval ellipse start end state double'),
+		this.createVertexTemplateEntry('whiteSpace=wrap;html=1;aspect=fixed;', 80, 80, '', 'Square', null, null, 'square'),
+		this.createVertexTemplateEntry('shape=ext;double=1;whiteSpace=wrap;html=1;aspect=fixed;', 80, 80, '', 'Double Square', null, null, 'double square'),
+		this.createVertexTemplateEntry('ellipse;whiteSpace=wrap;html=1;aspect=fixed;', 80, 80, '', 'Circle', null, null, 'circle'),
+		this.createVertexTemplateEntry('ellipse;shape=doubleEllipse;whiteSpace=wrap;html=1;aspect=fixed;', 80, 80, '', 'Double Circle', null, null, 'double circle'),
 	 	this.createVertexTemplateEntry('shape=process;whiteSpace=wrap;html=1;', 120, 60, '', 'Process', null, null, 'process task'),
 	 	this.createVertexTemplateEntry('rhombus;whiteSpace=wrap;html=1;', 80, 80, '', 'Diamond', null, null, 'diamond rhombus if condition decision conditional question test'),
 	 	this.createVertexTemplateEntry('shape=parallelogram;whiteSpace=wrap;html=1;', 120, 60, '', 'Parallelogram'),
@@ -947,10 +951,10 @@ Sidebar.prototype.addBasicPalette = function(dir)
 	this.addStencilPalette('basic', mxResources.get('basic'), dir + '/basic.xml',
 		';whiteSpace=wrap;html=1;fillColor=#ffffff;strokeColor=#000000;strokeWidth=2',
 		null, null, null, null, [
-		this.createVertexTemplateEntry('whiteSpace=wrap;html=1;aspect=fixed;', 80, 80, '', 'Square', null, null, 'square'),
-		this.createVertexTemplateEntry('ellipse;whiteSpace=wrap;html=1;aspect=fixed;', 80, 80, '', 'Circle', null, null, 'circle'),
-		this.createVertexTemplateEntry('shape=ext;double=1;whiteSpace=wrap;html=1;aspect=fixed;', 80, 80, '', 'Double Square', null, null, 'double square'),
-		this.createVertexTemplateEntry('ellipse;shape=doubleEllipse;whiteSpace=wrap;html=1;aspect=fixed;', 80, 80, '', 'Double Circle', null, null, 'double circle')
+		this.createVertexTemplateEntry('shape=partialRectangle;whiteSpace=wrap;html=1;left=0;right=0;fillColor=none;', 120, 60, '', 'Partial Rectangle'),
+		this.createVertexTemplateEntry('shape=partialRectangle;whiteSpace=wrap;html=1;top=0;bottom=0;fillColor=none;', 120, 60, '', 'Partial Rectangle'),
+		this.createVertexTemplateEntry('shape=partialRectangle;whiteSpace=wrap;html=1;left=0;right=0;top=0;fillColor=none;', 120, 60, '', 'Partial Rectangle'),
+		this.createVertexTemplateEntry('shape=partialRectangle;whiteSpace=wrap;html=1;right=0;top=0;bottom=0;fillColor=none;', 120, 60, '', 'Partial Rectangle')
 	]);
 };
 

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


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


File diff suppressed because it is too large
+ 171 - 37
war/plugins/update.js