瀏覽代碼

6.0.1.8 release

Former-commit-id: 11f69642bb026340fae754bb6d9fe963ca72029d
Gaudenz Alder 8 年之前
父節點
當前提交
fd5e784e35

+ 5 - 0
ChangeLog

@@ -1,3 +1,8 @@
+01-DEC-2016: 6.0.1.8
+
+- Removes blocking DIV while inserting files
+- Adds marker styles for copy/paste style
+
 23-NOV-2016: 6.0.1.7
 
 - Fixes links to same domain for viewer in iframes

+ 2 - 2
README.md

@@ -6,7 +6,7 @@ draw.io uses the [mxGraph library](https://github.com/jgraph/mxgraph) as the bas
 
 License
 -------
-draw.io is licensed under either the GPL v3.
+draw.io is licensed under the GPL v3.
 
 Development
 -----------
@@ -21,4 +21,4 @@ The simplest way to run draw.io initially is to fork this project, [publish the
 
 Supported Browsers
 ------------------
-draw.io supports IE 9+, Chrome 30+, Firefox 31+, Safari versions actively patched by Apple (6.2.x, 7.1.x, 8.0.x and 9.x at time of writing), Opera 20+, Native Android browser 5.x+, the default browser in the current and previous major iOS versions (e.g. 9.x and 8.x) and Edge 20+.
+draw.io supports IE 9+, Chrome 30+, Firefox 31+, Safari versions actively patched by Apple (6.2.x, 7.1.x, 8.0.x and 9.x at time of writing), Opera 20+, Native Android browser 5.x+, the default browser in the current and previous major iOS versions (e.g. 9.x and 8.x) and Edge 20+.

+ 1 - 1
VERSION

@@ -1 +1 @@
-6.0.1.7
+6.0.1.8

+ 6 - 14
etc/build/build.xml

@@ -110,7 +110,7 @@
 			</sources>
 		</jscomp>
 
-		<jscomp compilationLevel="simple" forceRecompile="true" debug="false" output="${war.dir}/js/atlas-viewer.min.js">
+		<jscomp compilationLevel="simple" forceRecompile="true" debug="false" output="${basedir}/base-viewer.min.js">
 			<sources dir="${basedir}">
 				<file name=".tmp0.js"/>
 			</sources>
@@ -127,10 +127,6 @@
 				<file name="base64.js" />
 				<file name="pako.min.js" />
 			</sources>
-			
-			<sources dir="${war.dir}/js/json">
-				<file name="json2.min.js" />
-			</sources>
 
 			<sources dir="${war.dir}/js/diagramly">
 				<file name="Init.js" />
@@ -190,8 +186,8 @@
 		</echo>
 		
 		<jscomp compilationLevel="simple" forceRecompile="true" debug="false" output="${war.dir}/js/viewer.min.js">
-			<sources dir="${war.dir}/js">
-				<file name="atlas-viewer.min.js"/>
+			<sources dir="${basedir}">
+				<file name="base-viewer.min.js"/>
 			</sources>
 
 			<sources dir="${basedir}">
@@ -202,7 +198,7 @@
 		<delete file="${basedir}/.tmp0.js"/>
 		<delete file="${basedir}/.tmp1.js"/>
 
-		<jscomp compilationLevel="simple" debug="false" forceRecompile="true" output="${war.dir}/js/atlas.min.js">
+		<jscomp compilationLevel="simple" debug="false" forceRecompile="true" output="${basedir}/base.min.js">
 			<sources dir="${war.dir}/js/spin">
 				<file name="spin.min.js" />
 			</sources>
@@ -215,10 +211,6 @@
 				<file name="pako.min.js" />
 				<file name="base64.js" />
 			</sources>
-			
-			<sources dir="${war.dir}/js/json">
-				<file name="json2.min.js"/>
-			</sources>
 
 			<sources dir="${war.dir}/js/diagramly">
 				<file name="Init.js" />
@@ -258,7 +250,7 @@
 		</jscomp>
 
 		<loadfile property="version" srcFile="../../VERSION"/>
-		<replace file="${war.dir}/js/atlas.min.js" token="@DRAWIO-VERSION@" value="${version}"/>
+		<replace file="${basedir}/base.min.js" token="@DRAWIO-VERSION@" value="${version}"/>
 		
 		<jscomp compilationLevel="simple" debug="false" forceRecompile="true" output="${war.dir}/js/extensions.min.js">
 			<sources dir="${war.dir}/js/diagramly">
@@ -330,9 +322,9 @@
 				<file name="DriveFile.js" />
 				<file name="DriveLibrary.js" />
 				<file name="DriveClient.js" />
-				<file name="DropboxClient.js" />
 				<file name="DropboxFile.js" />
 				<file name="DropboxLibrary.js" />
+				<file name="DropboxClient.js" />
 				<file name="OneDriveFile.js" />
 				<file name="OneDriveLibrary.js" />
 				<file name="OneDriveClient.js" />

+ 4 - 8
etc/sandstorm/README.md

@@ -5,12 +5,8 @@
 - `cd build`
 - `vagrant-spk setupvm diy`
 - `vagrant-spk vm up`
-- `cd ..` `./stage.sh` to stage the build files. gfind is gnu find on OS X.
-- Log into the vagrant box `cd build` `vagrant-spk vm ssh`
-- `sudo apt-get update`
-- `sudo apt-get install build-essential`
-- `sudo apt-get install autoconf`
-- `sudo apt-get install libtool-bin`
-- `sudo apt-get install git`
+- Invoke `../stage.sh` to stage the build files. gfind is gnu find on OS X.
+- Log into the vagrant box `vagrant-spk vm ssh`
+- `sudo apt-get install g++`
 - [Install latest capnp](https://capnproto.org/install.html)
-- In the build directory `vagrant-spk dev`
+- In the build directory `vagrant-spk dev`

+ 62 - 58
src/com/mxgraph/io/gliffy/importer/GliffyDiagramConverter.java

@@ -80,10 +80,12 @@ public class GliffyDiagramConverter
 	private void start()
 	{
 		// creates a diagram object from the JSON string
-		this.gliffyDiagram = new GsonBuilder().create().fromJson(diagramString, Diagram.class);
+		this.gliffyDiagram = new GsonBuilder().create().fromJson(diagramString,
+				Diagram.class);
+
+		collectVerticesAndConvert(vertices, gliffyDiagram.stage.getObjects(),
+				null);
 
-		collectVerticesAndConvert(vertices, gliffyDiagram.stage.getObjects(), null);
-		
 		//sort objects by the order specified in the Gliffy diagram
 		sortObjectsByOrder(gliffyDiagram.stage.getObjects());
 
@@ -109,7 +111,7 @@ public class GliffyDiagramConverter
 	private void importObject(GliffyObject obj, GliffyObject gliffyParent)
 	{
 		mxCell parent = gliffyParent != null ? gliffyParent.mxObject : null;
-		
+
 		drawioDiagram.addCell(obj.mxObject, parent);
 
 		if (obj.hasChildren())
@@ -120,19 +122,21 @@ public class GliffyDiagramConverter
 				// their order value is "auto"
 				sortObjectsByOrder(obj.children);
 			}
-			
-			for (GliffyObject child : obj.children) {
+
+			for (GliffyObject child : obj.children)
+			{
 				importObject(child, obj);
 			}
 		}
-		
+
 		if (obj.isLine())
 		{
 			// gets the terminal cells for the edge
 			mxCell startTerminal = getTerminalCell(obj, true);
 			mxCell endTerminal = getTerminalCell(obj, false);
 
-			drawioDiagram.addCell(obj.getMxObject(), parent, null, startTerminal, endTerminal);
+			drawioDiagram.addCell(obj.getMxObject(), parent, null,
+					startTerminal, endTerminal);
 
 			setWaypoints(obj, startTerminal, endTerminal);
 		}
@@ -140,31 +144,32 @@ public class GliffyDiagramConverter
 
 	private void sortObjectsByOrder(Collection<GliffyObject> values)
 	{
-		Collections.sort((List<GliffyObject>) values, new Comparator<GliffyObject>()
-		{
-			public int compare(GliffyObject o1, GliffyObject o2)
-			{
-				Float o1o;
-				Float o2o;
-				try
+		Collections.sort((List<GliffyObject>) values,
+				new Comparator<GliffyObject>()
 				{
-					if (o1.order == null || o2.order == null)
+					public int compare(GliffyObject o1, GliffyObject o2)
 					{
-						return 0;
-					}
+						Float o1o;
+						Float o2o;
+						try
+						{
+							if (o1.order == null || o2.order == null)
+							{
+								return 0;
+							}
+
+							o1o = Float.parseFloat(o1.order);
+							o2o = Float.parseFloat(o2.order);
+
+							return o1o.compareTo(o2o);
+						}
+						catch (NumberFormatException e)
+						{
+							return o1.order.compareTo(o2.order);
+						}
 
-					o1o = Float.parseFloat(o1.order);
-					o2o = Float.parseFloat(o2.order);
-					
-					return o1o.compareTo(o2o);
-				}
-				catch (NumberFormatException e)
-				{
-					return o1.order.compareTo(o2.order);
-				} 
-
-			}
-		});
+					}
+				});
 	}
 
 	private mxCell getTerminalCell(GliffyObject gliffyEdge, boolean start)
@@ -176,25 +181,27 @@ public class GliffyDiagramConverter
 			return null;
 		}
 
-		Constraint con = start ? cons.getStartConstraint() : cons.getEndConstraint();
+		Constraint con = start ? cons.getStartConstraint()
+				: cons.getEndConstraint();
 
 		if (con == null)
 		{
 			return null;
 		}
 
-		ConstraintData cst = start ? con.getStartPositionConstraint() : con.getEndPositionConstraint();
+		ConstraintData cst = start ? con.getStartPositionConstraint()
+				: con.getEndPositionConstraint();
 		int nodeId = cst.getNodeId();
 		GliffyObject gliffyEdgeTerminal = vertices.get(nodeId);
-		
+
 		//edge could be terminated with another edge, so import it as a dangling edge
-		if(gliffyEdgeTerminal == null)
+		if (gliffyEdgeTerminal == null)
 		{
 			return null;
 		}
-		
+
 		mxCell mxEdgeTerminal = gliffyEdgeTerminal.getMxObject();
-		
+
 		return mxEdgeTerminal;
 	}
 
@@ -205,14 +212,15 @@ public class GliffyDiagramConverter
 	 * @param startTerminal starting point
 	 * @param endTerminal ending point
 	 */
-	private void setWaypoints(GliffyObject object, mxCell startTerminal, mxCell endTerminal)
+	private void setWaypoints(GliffyObject object, mxCell startTerminal,
+			mxCell endTerminal)
 	{
 		mxCell cell = object.getMxObject();
 		mxGeometry geo = drawioDiagram.getModel().getGeometry(cell);
 		geo.setRelative(true);
 
 		List<float[]> points = object.getGraphic().getLine().controlPath;
-		
+
 		if (points.size() < 2)
 		{
 			return;
@@ -222,7 +230,8 @@ public class GliffyDiagramConverter
 
 		for (float[] point : points)
 		{
-			mxPoints.add(new mxPoint((int) point[0] + (int) object.x, (int) point[1] + (int) object.y));
+			mxPoints.add(new mxPoint((int) point[0] + (int) object.x,
+					(int) point[1] + (int) object.y));
 		}
 
 		if (startTerminal == null)
@@ -245,26 +254,27 @@ public class GliffyDiagramConverter
 		}
 
 		drawioDiagram.getModel().setGeometry(cell, geo);
-		
+
 	}
 
 	/**
 	 * Creates a map of all vertices so they can be easily accessed when looking
 	 * up terminal cells for edges
 	 */
-	private void collectVerticesAndConvert(Map<Integer, GliffyObject> vertices, Collection<GliffyObject> objects, GliffyObject parent)
+	private void collectVerticesAndConvert(Map<Integer, GliffyObject> vertices,
+			Collection<GliffyObject> objects, GliffyObject parent)
 	{
 		for (GliffyObject object : objects)
 		{
 			object.parent = parent;
-			
+
 			convertGliffyObject(object, parent);
-			
-			if(!object.isLine())
+
+			if (!object.isLine())
 			{
 				vertices.put(object.id, object);
 			}
-			
+
 			// don't collect for swimlanes and mindmaps, their children are treated differently
 			if (object.isGroup() || (object.isLine() && object.hasChildren()))
 			{
@@ -284,21 +294,15 @@ public class GliffyDiagramConverter
 		mxCodec codec = new mxCodec();
 		Element node = (Element) codec.encode(drawioDiagram.getModel());
 		node.setAttribute("style", "default-style2");
-		node.setAttribute("background", gliffyDiagram.stage.getBackgroundColor());
+		node.setAttribute("background",
+				gliffyDiagram.stage.getBackgroundColor());
 		node.setAttribute("grid", gliffyDiagram.stage.isGridOn() ? "1" : "0");
-		node.setAttribute("guides", gliffyDiagram.stage.isDrawingGuidesOn() ? "1" : "0");
+		node.setAttribute("guides",
+				gliffyDiagram.stage.isDrawingGuidesOn() ? "1" : "0");
 		String xml = mxXmlUtils.getXml(node);
 		return xml;
 	}
 
-	/**
-	 * Returns true if the gradient is ignored for this shape.
-	 */
-	private boolean isGradientIgnored(GliffyObject gliffyObject, GliffyObject parent)
-	{
-		return false; //gliffyObject.tid == null || gliffyObject.tid.startsWith("com.gliffy.shape.venn.outline.default");
-	}
-	
 	/**
 	 * Performs the object conversion
 	 * 
@@ -355,13 +359,13 @@ public class GliffyDiagramConverter
 				if(style.lastIndexOf("strokeColor") == -1)
 					style.append("strokeColor=" + shape.strokeColor).append(";");
 
-				if (shape.gradient && !isGradientIgnored(gliffyObject, parent))
+				if (shape.gradient && !gliffyObject.isGradientIgnored())
 				{
-					style.append("gradientColor=#FFFFFF;gradientDirection=north;");
+					style.append("gradientColor=" + gliffyObject.getGradientColor() + ";gradientDirection=north;");
 				}
 
 				// opacity value is wrong for venn circles, so ignore it and use the one in the mapping
-				if (!gliffyObject.isVennsCircle())
+				if (!gliffyObject.isVennCircle())
 				{
 					style.append("opacity=" + shape.opacity * 100).append(";");
 					style.append(DashStyleMapping.get(shape.dashStyle));

+ 39 - 4
src/com/mxgraph/io/gliffy/model/GliffyObject.java

@@ -233,7 +233,6 @@ public class GliffyObject
 			Graphic g = getFirstChildGraphic();
 			return  g != null && g.getType().equals(Graphic.Type.SHAPE);
 		}
-
 	}
 
 	public boolean isSvg()
@@ -243,7 +242,7 @@ public class GliffyObject
 
 	public boolean isSwimlane()
 	{
-		return uid != null && uid.contains(SWIMLANE);
+		return uid != null && uid.startsWith(SWIMLANE);
 	}
 
 	public boolean isText()
@@ -256,11 +255,47 @@ public class GliffyObject
 		return graphic != null && graphic.getType().equals(Graphic.Type.IMAGE);
 	}
 
-	public boolean isVennsCircle()
+	public boolean isVennCircle()
 	{
-		return uid != null && uid.contains("com.gliffy.shape.venn");
+		return uid != null && uid.startsWith("com.gliffy.shape.venn");
 	}
 	
+	public String getGradientColor()
+	{
+		String gradientColor = "#FFFFFF";
+		
+		// Gradient colors are lighter version of the fill color except for radial
+		// venn shapes, where white is used with a radial gradient (we use linear)
+		if (graphic != null && graphic.Shape != null && uid != null &&
+			!uid.startsWith("com.gliffy.shape.radial"))
+		{
+			String hex = graphic.Shape.fillColor;
+			
+			if (hex != null && hex.length() == 7 && hex.charAt(0) == '#')
+			{
+				long clr = Long.parseLong(hex.substring(1), 16);
+				
+				long r = Math.min(0xFF0000, ((clr & 0xFF0000) + 0xAA0000)) & 0xFF0000;
+				long g = Math.min(0x00FF00, ((clr & 0x00FF00) + 0x00AA00)) & 0x00FF00;
+				long b = Math.min(0x0000FF, ((clr & 0x0000FF) + 0x0000AA)) & 0x0000FF;
+
+				gradientColor = String.format("#%06X", 0xFFFFFF & (r + g + b));
+			}
+		}
+		
+		return gradientColor;
+	}
+
+	/**
+	 * LATER: Add more cases where gradient is ignored.
+	 */
+	public boolean isGradientIgnored()
+	{
+		return uid != null &&
+			(uid.startsWith("com.gliffy.shape.venn.outline") ||
+			uid.startsWith("com.gliffy.shape.venn.flat"));
+	}
+
 	public boolean isUnrecognizedGraphicType() 
 	{
 		return graphic != null && graphic.type == null;

+ 3 - 3
src/com/mxgraph/io/mxVsdxCodec.java

@@ -25,9 +25,9 @@ import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 import org.xml.sax.SAXException;
 
+import com.mxgraph.io.vsdx.Shape;
 import com.mxgraph.io.vsdx.ShapePageId;
 import com.mxgraph.io.vsdx.VsdxShape;
-import com.mxgraph.io.vsdx.mxMasterShape;
 import com.mxgraph.io.vsdx.mxPathDebug;
 import com.mxgraph.io.vsdx.mxVsdxConnect;
 import com.mxgraph.io.vsdx.mxVsdxConstants;
@@ -907,7 +907,7 @@ public class mxVsdxCodec
 		String txtAngleV = shape.getAttribute(mxVsdxConstants.TEXT_X_FORM,
 				mxVsdxConstants.TXT_ANGLE, "V", "");
 
-		mxMasterShape masterShape = shape.getMaster() != null ? shape
+		Shape masterShape = shape.getMaster() != null ? shape
 				.getMaster().getMasterShape() : null;
 
 		if (masterShape != null)
@@ -1043,7 +1043,7 @@ public class mxVsdxCodec
 		String txtHV = rootShape.getAttribute(mxVsdxConstants.CONTROL,
 				mxVsdxConstants.TXT_HEIGHT, "V", "");
 
-		mxMasterShape masterShape = shape.getMaster() != null ? shape
+		Shape masterShape = shape.getMaster() != null ? shape
 				.getMaster().getMasterShape() : null;
 
 		if (masterShape != null)

+ 4 - 9
src/com/mxgraph/io/vsdx/Section.java

@@ -17,21 +17,16 @@ public class Section
 		this.elem = elem;
 	}
 	
-	public Element getCell(String[] keys)
+	public Element getIndexedCell(Integer index, String cellKey)
 	{
-		if (keys == null || keys.length != 3)
-		{
-			return null;
-		}
-		
 		ArrayList<Element> rows = mxVsdxUtils.getDirectChildNamedElements(this.elem, "row");
 		
 		for (int i=0; i < rows.size(); i++)
 		{
 			Element row = rows.get(i);
-			String n = row.getAttribute("N");
+			String n = row.getAttribute("ix");
 			
-			if (n.equals(keys[1]))
+			if (n.equals(index))
 			{
 				ArrayList<Element> cells = mxVsdxUtils.getDirectChildNamedElements(row, "cell");
 				
@@ -40,7 +35,7 @@ public class Section
 					Element cell = cells.get(j);
 					n = cell.getAttribute("N");
 
-					if (n.equals(keys[2]))
+					if (n.equals(cellKey))
 					{
 						return cell;
 					}

+ 30 - 2
src/com/mxgraph/io/vsdx/Shape.java

@@ -13,11 +13,17 @@ import java.util.Map;
 
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
 
 import com.mxgraph.io.mxVsdxCodec;
 
-public class Shape extends Style {
-
+public class Shape extends Style
+{
+	/**
+	 * The text element of the shape, if any
+	 */
+	protected Element text;
+	
 	protected double width = 0;
 	
 	protected double height = 0;
@@ -148,6 +154,10 @@ public class Shape extends Style {
 				fdChild = fdChild.getNextSibling();
 			}
 		}
+		else if (childName.equals(mxVsdxConstants.TEXT))
+		{
+			this.text = elem;
+		}
 	}
 
 	/**
@@ -876,6 +886,24 @@ public class Shape extends Style {
 		return result;
 	}
 
+	/**
+	 * Returns the value of the Text element.
+	 * @return Value of the Text element.
+	 */
+	public String getText()
+	{
+		return this.text != null ? text.getTextContent() : null;
+	}
+
+	/**
+	 * Returns the children Nodes of Text.
+	 * @return List with the children of the Text element.
+	 */
+	public NodeList getTextChildren()
+	{
+		return this.text != null ? text.getChildNodes() : null;
+	}
+
 	/**
 	 * Checks if the shape has defined a width element.
 	 * @return Returns <code>true</code> if the shape has defined a width element.

+ 45 - 69
src/com/mxgraph/io/vsdx/Style.java

@@ -26,8 +26,6 @@ public abstract class Style
 	
 	protected Integer Id;
 	
-	protected Element text = null;
-	
 	// .vsdx cells elements that contain one style each
 	protected Map<String, Element> cellElements = new HashMap<String, Element>();
 	
@@ -72,6 +70,8 @@ public abstract class Style
 		styleTypes.put(mxVsdxConstants.LEFT_MARGIN, mxVsdxConstants.TEXT_STYLE);
 		styleTypes.put(mxVsdxConstants.RIGHT_MARGIN, mxVsdxConstants.TEXT_STYLE);
 		styleTypes.put(mxVsdxConstants.TOP_MARGIN, mxVsdxConstants.TEXT_STYLE);
+		styleTypes.put(mxVsdxConstants.PARAGRAPH, mxVsdxConstants.TEXT_STYLE);
+		styleTypes.put(mxVsdxConstants.TOP_MARGIN, mxVsdxConstants.TEXT_STYLE);
 	};
 	
 	/**
@@ -164,10 +164,6 @@ public abstract class Style
 		{
 			this.parseSection(elem);
 		}
-		else if (childName.equals(mxVsdxConstants.TEXT))
-		{
-			this.text = elem;
-		}
 	}
 
 	/**
@@ -449,18 +445,38 @@ public abstract class Style
 		return result;
 	}
 
-	/**
-	 * Finds a cell key in a Section hierarchy
-	 * @param keys the Section/Row/Cell keys
-	 * @return the Cell Element
-	 */
-	protected Element getSectionCell(String[] keys)
+	protected Element getCellElement(String cellKey, Integer index, String sectKey)
 	{
-		Section section = this.sections.get(keys[0]);
+		Section sect = this.sections.get(sectKey);
+		
+		if (sect == null)
+		{
+			return null;
+		}
+		
+		Element elem = sect.getIndexedCell(index, cellKey);
+		
+		if (elem == null)
+		{
+			String styleType = Style.styleTypes.get(sectKey);
+			Style parentStyle = this.styleParents.get(styleType);
+			
+			if (parentStyle != null)
+			{
+				Element parentElem = parentStyle.getCellElement(cellKey, index, sectKey);
+				
+				if (parentElem != null)
+				{
+					// Only return if non-null. Just in case (and not sure if that's valid) there is an
+					// inherit formula that doesn't resolve to anything
+					return parentElem;
+				}
+			}
+		}
 		
-		return section.getCell(keys);
+		return elem;
 	}
-	
+
 	/**
 	 * Locates the first entry for the specified style string in the style hierarchy.
 	 * The order is to look locally, then delegate the request to the relevant parent style
@@ -470,14 +486,6 @@ public abstract class Style
 	 */
 	protected Element getCellElement(String key)
 	{
-		String[] keys = key.split(".");
-		
-		if (keys.length > 1)
-		{
-			// Section being referenced
-			return getSectionCell(keys);
-		}
-
 		Element elem = this.cellElements.get(key);
 		boolean inherit = false;
 		
@@ -632,38 +640,6 @@ public abstract class Style
 			return getNumericalValue(this.getCellElement(mxVsdxConstants.LINE_COLOR_TRANS), 0);
 	}
 
-	/**
-	 * Returns the value of the Text element.
-	 * @return Value of the Text element.
-	 */
-	public String getText()
-	{
-		if (this.text != null)
-		{
-			return text.getTextContent();
-		}
-
-		return null;
-	}
-
-	/**
-	 * Returns the children Nodes of Text.
-	 * @return List with the children of the Text element.
-	 */
-	public List<Node> getTextChildren()
-	{
-		List<Node> list = null;
-		NodeList child = null;
-
-		if (this.text != null)
-		{
-			child = text.getChildNodes();
-			list = mxVsdxUtils.copyNodeList(child);
-		}
-
-		return list;
-	}
-
 	/**
 	 * Returns the NameU attribute.
 	 * @return Value of the NameU attribute.
@@ -720,7 +696,7 @@ public abstract class Style
 	 */
 	public boolean hasTextColor(String charIX)
 	{
-		return hasIndexedProperty(mxVsdxConstants.CHAR, charIX, mxVsdxConstants.COLOR);
+		return hasIndexedProperty(mxVsdxConstants.CHARACTER, charIX, mxVsdxConstants.COLOR);
 	}
 
 	/**
@@ -730,7 +706,7 @@ public abstract class Style
 	 */
 	public String getTextColor(String charIX)
 	{
-		String color = getChildText(mxVsdxConstants.CHAR, charIX, mxVsdxConstants.COLOR, "#000000");
+		String color = getChildText(mxVsdxConstants.CHARACTER, charIX, mxVsdxConstants.COLOR, "#000000");
 
 		if (!color.startsWith("#"))
 		{
@@ -783,7 +759,7 @@ public abstract class Style
 	 */
 	public boolean hasTextSize(String charIX)
 	{
-		return hasIndexedProperty(mxVsdxConstants.CHAR, charIX, mxVsdxConstants.SIZE);
+		return hasIndexedProperty(mxVsdxConstants.CHARACTER, charIX, mxVsdxConstants.SIZE);
 	}
 
 	/**
@@ -793,7 +769,7 @@ public abstract class Style
 	 */
 	public String getTextSize(String charIX)
 	{
-		return String.valueOf(getChildNumericalValue(mxVsdxConstants.CHAR, charIX, mxVsdxConstants.SIZE, 0));
+		return String.valueOf(getChildNumericalValue(mxVsdxConstants.CHARACTER, charIX, mxVsdxConstants.SIZE, 0));
 	}
 
 	/**
@@ -803,7 +779,7 @@ public abstract class Style
 	 */
 	public boolean hasTextStyle(String charIX)
 	{
-		return hasIndexedProperty(mxVsdxConstants.CHAR, charIX, mxVsdxConstants.STYLE);
+		return hasIndexedProperty(mxVsdxConstants.CHARACTER, charIX, mxVsdxConstants.STYLE);
 	}
 
 	/**
@@ -813,7 +789,7 @@ public abstract class Style
 	 */
 	public String getTextStyle(String charIX)
 	{
-		return getChildText(mxVsdxConstants.CHAR, charIX, mxVsdxConstants.STYLE, "");
+		return getChildText(mxVsdxConstants.CHARACTER, charIX, mxVsdxConstants.STYLE, "");
 	}
 
 	/**
@@ -823,7 +799,7 @@ public abstract class Style
 	 */
 	public boolean hasTextFont(String charIX)
 	{
-		return hasIndexedProperty(mxVsdxConstants.CHAR, charIX, mxVsdxConstants.FONT);
+		return hasIndexedProperty(mxVsdxConstants.CHARACTER, charIX, mxVsdxConstants.FONT);
 	}
 
 	/**
@@ -833,7 +809,7 @@ public abstract class Style
 	 */
 	public String getTextFont(String charIX)
 	{
-		return pm.getFont(getChildText(mxVsdxConstants.CHAR, charIX, mxVsdxConstants.FONT, ""));
+		return pm.getFont(getChildText(mxVsdxConstants.CHARACTER, charIX, mxVsdxConstants.FONT, ""));
 	}
 
 	/**
@@ -843,7 +819,7 @@ public abstract class Style
 	 */
 	public boolean hasTextPos(String charIX)
 	{
-		return hasIndexedProperty(mxVsdxConstants.CHAR, charIX, mxVsdxConstants.POS);
+		return hasIndexedProperty(mxVsdxConstants.CHARACTER, charIX, mxVsdxConstants.POS);
 	}
 
 	/**
@@ -853,7 +829,7 @@ public abstract class Style
 	 */
 	public int getTextPos(String charIX)
 	{
-		String val = getChildText(mxVsdxConstants.CHAR, charIX, mxVsdxConstants.POS, "");
+		String val = getChildText(mxVsdxConstants.CHARACTER, charIX, mxVsdxConstants.POS, "");
 
 		if (!val.equals(""))
 		{
@@ -870,7 +846,7 @@ public abstract class Style
 	 */
 	public boolean hasTextStrike(String charIX)
 	{
-		return hasIndexedProperty(mxVsdxConstants.CHAR, charIX, mxVsdxConstants.STRIKETHRU);
+		return hasIndexedProperty(mxVsdxConstants.CHARACTER, charIX, mxVsdxConstants.STRIKETHRU);
 	}
 
 	/**
@@ -880,7 +856,7 @@ public abstract class Style
 	 */
 	public boolean getTextStrike(String charIX)
 	{
-		String val = getChildText(mxVsdxConstants.CHAR, charIX, mxVsdxConstants.STRIKETHRU, "");
+		String val = getChildText(mxVsdxConstants.CHARACTER, charIX, mxVsdxConstants.STRIKETHRU, "");
 		return val.equals("1");
 	}
 
@@ -891,7 +867,7 @@ public abstract class Style
 	 */
 	public boolean hasTextCase(String charIX)
 	{
-		return hasIndexedProperty(mxVsdxConstants.CHAR, charIX, mxVsdxConstants.CASE);
+		return hasIndexedProperty(mxVsdxConstants.CHARACTER, charIX, mxVsdxConstants.CASE);
 	}
 
 	/**
@@ -901,7 +877,7 @@ public abstract class Style
 	 */
 	public int getTextCase(String charIX)
 	{
-		String val = getChildText(mxVsdxConstants.CHAR, charIX, mxVsdxConstants.CASE, "");
+		String val = getChildText(mxVsdxConstants.CHARACTER, charIX, mxVsdxConstants.CASE, "");
 
 		if (!val.equals(""))
 		{

+ 19 - 7
src/com/mxgraph/io/vsdx/VsdxShape.java

@@ -59,7 +59,7 @@ public class VsdxShape extends Shape
 	/**
 	 * Master Shape referenced by the shape.
 	 */
-	protected mxMasterShape masterShape = null;
+	protected Shape masterShape = null;
 
 	/**
 	 * Master element referenced by the shape.
@@ -229,7 +229,7 @@ public class VsdxShape extends Shape
 	public String getTextLabel()
 	{
 		String masterId = this.getMasterId();
-		mxMasterShape masterShape = null;
+		Shape masterShape = null;
 
 		if (master != null)
 		{
@@ -822,7 +822,7 @@ public class VsdxShape extends Shape
 				}
 				
 				String masterId = this.getMasterId();
-				mxMasterShape masterShape = null;
+				Shape masterShape = null;
 	
 				if (this.master != null)
 				{
@@ -1514,7 +1514,7 @@ public class VsdxShape extends Shape
 		String txtPinXF = this.getAttribute(mxVsdxConstants.TEXT_X_FORM, mxVsdxConstants.TXT_PIN_X, "F", "");
 		String txtPinYF = this.getAttribute(mxVsdxConstants.TEXT_X_FORM, mxVsdxConstants.TXT_PIN_X, "F", "");
 
-		mxMasterShape masterShape = master != null ? master.getMasterShape() : null;
+		Shape masterShape = master != null ? master.getMasterShape() : null;
 
 		if (masterShape != null)
 		{
@@ -1547,7 +1547,7 @@ public class VsdxShape extends Shape
 		String xConF = this.getAttribute(mxVsdxConstants.CONTROL, mxVsdxConstants.X_CON, "F", "");
 		String yConF = this.getAttribute(mxVsdxConstants.CONTROL, mxVsdxConstants.Y_CON, "F", "");
 
-		mxMasterShape masterShape = master != null ? master.getMasterShape() : null;
+		Shape masterShape = master != null ? master.getMasterShape() : null;
 
 		if (masterShape != null)
 		{
@@ -1580,7 +1580,7 @@ public class VsdxShape extends Shape
 		String txtWidthF = this.getAttribute(mxVsdxConstants.TEXT_X_FORM, mxVsdxConstants.TXT_WIDTH, "F", "");
 		String txtHeightF = this.getAttribute(mxVsdxConstants.TEXT_X_FORM, mxVsdxConstants.TXT_HEIGHT, "F", "");
 
-		mxMasterShape masterShape = master != null ? master.getMasterShape() : null;
+		Shape masterShape = master != null ? master.getMasterShape() : null;
 
 		if (masterShape != null)
 		{
@@ -1634,7 +1634,7 @@ public class VsdxShape extends Shape
 	{
 		String txtAngleValue = this.getAttribute(mxVsdxConstants.TEXT_X_FORM, mxVsdxConstants.TXT_ANGLE, "V", "");
 
-		mxMasterShape masterShape = master != null ? master.getMasterShape() : null;
+		Shape masterShape = master != null ? master.getMasterShape() : null;
 
 		if (masterShape != null)
 		{
@@ -2021,4 +2021,16 @@ public class VsdxShape extends Shape
 		
 		return elem;
 	}
+	
+	protected Element getCellElement(String cellKey, Integer index, String sectKey)
+	{
+		Element elem = super.getCellElement(cellKey, index, sectKey);
+		
+		if (elem == null && this.masterShape != null)
+		{
+			return this.masterShape.getCellElement(cellKey, index, sectKey);
+		}
+		
+		return elem;
+	}
 }

+ 0 - 465
src/com/mxgraph/io/vsdx/mxMasterShape.java

@@ -1,465 +0,0 @@
-package com.mxgraph.io.vsdx;
-
-import org.w3c.dom.Element;
-
-/**
- * This class is a wrapper for a shape element.<br/>
- * Contains references to the stylesheets indicated in the shape.<br/>
- * If a property is not found in the shape Element but it may be found in a stylesheet,
- * the property is searched in such stylesheet.
- */
-public class mxMasterShape extends Shape
-{
-	public mxMasterShape(Element s, mxVsdxModel model)
-	{
-		super(s, model);
-	}
-
-	@Override
-	public String getTextColor(String charIX)
-	{
-		if (super.hasTextColor(charIX))
-		{
-			return super.getTextColor(charIX);
-		}
-		else if (textParent != null)
-		{
-			return textParent.getTextColor(charIX);
-		}
-		
-		return "#000000";
-	}
-
-	@Override
-	public boolean hasTextColor(String charIX)
-	{
-		boolean has = super.hasTextColor(charIX);
-		
-		if ((textParent != null) && !has)
-		{
-			has = textParent.hasTextColor(charIX);
-		}
-		
-		return has;
-	}
-
-	@Override
-	public String getTextFont(String charIX)
-	{
-		if (super.hasTextFont(charIX))
-		{
-			return super.getTextFont(charIX);
-		}
-		else if (textParent != null)
-		{
-			return textParent.getTextFont(charIX);
-		}
-		
-		return "";
-	}
-
-	@Override
-	public String getTextSize(String charIX)
-	{
-		if (super.hasTextSize(charIX))
-		{
-			return super.getTextSize(charIX);
-		}
-		else if (textParent != null)
-		{
-			return textParent.getTextSize(charIX);
-		}
-		
-		return "";
-	}
-
-	@Override
-	public boolean hasTextFont(String charIX)
-	{
-		boolean has = super.hasTextFont(charIX);
-		
-		if ((textParent != null) && !has)
-		{
-			has = textParent.hasTextFont(charIX);
-		}
-		
-		return has;
-	}
-
-	@Override
-	public boolean hasTextSize(String charIX)
-	{
-		boolean has = super.hasTextSize(charIX);
-		
-		if ((textParent != null) && !has)
-		{
-			has = textParent.hasTextSize(charIX);
-		}
-		
-		return has;
-	}
-
-	@Override
-	public int getTextPos(String charIX)
-	{
-		if (super.hasTextPos(charIX))
-		{
-			return super.getTextPos(charIX);
-		}
-		else if (textParent != null)
-		{
-			return textParent.getTextPos(charIX);
-		}
-		
-		return 0;
-	}
-
-	@Override
-	public boolean getTextStrike(String charIX)
-	{
-		if (super.hasTextStrike(charIX))
-		{
-			return super.getTextStrike(charIX);
-		}
-		else if (textParent != null)
-		{
-			return textParent.getTextStrike(charIX);
-		}
-		
-		return false;
-	}
-
-	@Override
-	public String getTextStyle(String charIX)
-	{
-		if (super.hasTextStyle(charIX))
-		{
-			return super.getTextStyle(charIX);
-		}
-		else if (textParent != null)
-		{
-			return textParent.getTextStyle(charIX);
-		}
-		
-		return "";
-	}
-
-	@Override
-	public boolean hasTextPos(String charIX)
-	{
-		boolean has = super.hasTextPos(charIX);
-		
-		if ((textParent != null) && !has)
-		{
-			has = textParent.hasTextPos(charIX);
-		}
-		
-		return has;
-	}
-
-	@Override
-	public boolean hasTextStrike(String charIX)
-	{
-		boolean has = super.hasTextPos(charIX);
-		
-		if ((textParent != null) && !has)
-		{
-			has = textParent.hasTextPos(charIX);
-		}
-		
-		return has;
-	}
-
-	@Override
-	public boolean hasTextStyle(String charIX)
-	{
-		boolean has = super.hasTextStyle(charIX);
-		
-		if ((textParent != null) && !has)
-		{
-			has = textParent.hasTextStyle(charIX);
-		}
-		
-		return has;
-	}
-
-	@Override
-	public int getHorizontalAlign(String paraIX)
-	{
-		if (super.hasHorizontalAlign(paraIX))
-		{
-			return super.getHorizontalAlign(paraIX);
-		}
-		else if (textParent != null)
-		{
-			return textParent.getHorizontalAlign(paraIX);
-		}
-		
-		return 0;
-	}
-
-	@Override
-	public String getIndentFirst(String paraIX)
-	{
-		if (super.hasIndentFirst(paraIX))
-		{
-			return super.getIndentFirst(paraIX);
-		}
-		else if (textParent != null)
-		{
-			return textParent.getIndentFirst(paraIX);
-		}
-		
-		return "0";
-	}
-
-	@Override
-	public boolean hasHorizontalAlign(String paraIX)
-	{
-		boolean has = super.hasHorizontalAlign(paraIX);
-		
-		if ((textParent != null) && !has)
-		{
-			has = textParent.hasHorizontalAlign(paraIX);
-		}
-		
-		return has;
-	}
-
-	@Override
-	public boolean hasIndentFirst(String paraIX)
-	{
-		boolean has = super.hasHorizontalAlign(paraIX);
-		
-		if ((textParent != null) && !has)
-		{
-			has = textParent.hasHorizontalAlign(paraIX);
-		}
-		
-		return has;
-	}
-
-	@Override
-	public String getIndentLeft(String paraIX)
-	{
-		if (super.hasIndentLeft(paraIX))
-		{
-			return super.getIndentLeft(paraIX);
-		}
-		else if (textParent != null)
-		{
-			return textParent.getIndentLeft(paraIX);
-		}
-		
-		return "0";
-	}
-
-	@Override
-	public boolean hasIndentLeft(String paraIX)
-	{
-		boolean has = super.hasIndentLeft(paraIX);
-		
-		if ((textParent != null) && !has)
-		{
-			has = textParent.hasIndentLeft(paraIX);
-		}
-		
-		return has;
-	}
-
-	@Override
-	public String getIndentRight(String paraIX)
-	{
-		if (super.hasIndentRight(paraIX))
-		{
-			return super.getIndentRight(paraIX);
-		}
-		else if (textParent != null)
-		{
-			return textParent.getIndentRight(paraIX);
-		}
-		
-		return "0";
-	}
-
-	@Override
-	public boolean hasIndentRight(String paraIX)
-	{
-		boolean has = super.hasIndentRight(paraIX);
-		
-		if ((textParent != null) && !has)
-		{
-			has = textParent.hasIndentRight(paraIX);
-		}
-		
-		return has;
-	}
-
-	@Override
-	public String getSpAfter(String paraIX)
-	{
-		if (super.hasSpAfter(paraIX))
-		{
-			return super.getSpAfter(paraIX);
-		}
-		else if (textParent != null)
-		{
-			return textParent.getSpAfter(paraIX);
-		}
-		
-		return "0";
-	}
-
-	@Override
-	public String getSpBefore(String paraIX)
-	{
-		if (super.hasSpBefore(paraIX))
-		{
-			return super.getSpBefore(paraIX);
-		}
-		else if (textParent != null)
-		{
-			return textParent.getSpBefore(paraIX);
-		}
-		
-		return "0";
-	}
-
-	@Override
-	public boolean hasSpAfter(String paraIX)
-	{
-		boolean has = super.hasSpAfter(paraIX);
-		
-		if ((textParent != null) && !has)
-		{
-			has = textParent.hasSpAfter(paraIX);
-		}
-		
-		return has;
-	}
-
-	@Override
-	public boolean hasSpBefore(String paraIX)
-	{
-		boolean has = super.hasSpBefore(paraIX);
-		
-		if ((textParent != null) && !has)
-		{
-			has = textParent.hasSpBefore(paraIX);
-		}
-		
-		return has;
-	}
-
-	@Override
-	public double getSpLine(String paraIX)
-	{
-		if (super.hasSpLine(paraIX))
-		{
-			return super.getSpLine(paraIX);
-		}
-		else if (textParent != null)
-		{
-			return textParent.getSpLine(paraIX);
-		}
-		
-		return 0;
-	}
-
-	@Override
-	public boolean hasSpLine(String paraIX)
-	{
-		boolean has = super.hasSpLine(paraIX);
-		
-		if ((textParent != null) && !has)
-		{
-			has = textParent.hasSpLine(paraIX);
-		}
-		
-		return has;
-	}
-
-	@Override
-	public String getRTLText(String paraIX)
-	{
-		if (super.hasRTLText(paraIX))
-		{
-			return super.getRTLText(paraIX);
-		}
-		else if (textParent != null)
-		{
-			return textParent.getRTLText(paraIX);
-		}
-		
-		return "ltr";
-	}
-
-	@Override
-	public boolean hasRTLText(String paraIX)
-	{
-		boolean has = super.hasRTLText(paraIX);
-		
-		if ((textParent != null) && !has)
-		{
-			has = textParent.hasRTLText(paraIX);
-		}
-		
-		return has;
-	}
-
-	@Override
-	public String getFlags(String paraIX)
-	{
-		if (super.hasFlags(paraIX))
-		{
-			return super.getFlags(paraIX);
-		}
-		else if (textParent != null)
-		{
-			return textParent.getFlags(paraIX);
-		}
-		
-		return "ltr";
-	}
-
-	@Override
-	public boolean hasFlags(String paraIX)
-	{
-		boolean has = super.hasFlags(paraIX);
-		
-		if ((textParent != null) && !has)
-		{
-			has = textParent.hasFlags(paraIX);
-		}
-		
-		return has;
-	}
-
-	@Override
-	public String getLetterSpace(String paraIX)
-	{
-		if (super.hasLetterSpace(paraIX))
-		{
-			return super.getLetterSpace(paraIX);
-		}
-		else if (textParent != null)
-		{
-			return textParent.getLetterSpace(paraIX);
-		}
-		
-		return "";
-	}
-
-	@Override
-	public boolean hasLetterSpace(String paraIX)
-	{
-		boolean has = super.hasLetterSpace(paraIX);
-		
-		if ((textParent != null) && !has)
-		{
-			has = textParent.hasLetterSpace(paraIX);
-		}
-		
-		return has;
-	}
-}

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

@@ -10,7 +10,7 @@ import org.w3c.dom.Element;
  * All  method that recieve a index like param ignores it. Stylesheets only have
  * one element of each class.
  */
-public class mxStyleSheet extends mxMasterShape
+public class mxStyleSheet extends Shape
 {
 	public mxStyleSheet(Element s, mxVsdxModel model)
 	{

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

@@ -20,7 +20,7 @@ public class mxVsdxConstants
 	public static String BOTTOM_MARGIN = "BottomMargin";
 	public static String BULLET = "Bullet";
 	public static String CASE = "Case";
-	public static String CHAR = "Char";
+	public static String CHARACTER = "Character";
 	public static String COLOR = "Color";
 	public static String COLOR_ENTRY = "ColorEntry";
 	public static String COLORS = "Colors";

+ 5 - 5
src/com/mxgraph/io/vsdx/mxVsdxMaster.java

@@ -26,13 +26,13 @@ public class mxVsdxMaster
 	 */
 	protected String Id = null;
 	
-	protected mxMasterShape masterShape = null;
+	protected Shape masterShape = null;
 	
 	/*
 	 * Map that contains the shapes in Master element wrapped for instances of mxDelegateShape.
 	 * The key is the shape's ID.
 	 */
-	protected HashMap<String, mxMasterShape> childShapes = new HashMap<String, mxMasterShape>();
+	protected HashMap<String, Shape> childShapes = new HashMap<String, Shape>();
 
 	/**
 	 * Create a new instance of mxMasterElement and retrieves all the shapes contained
@@ -113,7 +113,7 @@ public class mxVsdxMaster
 					{
 						Element shape = (Element)shapesChild;
 						String shapeId = shape.getAttribute("ID");
-						mxMasterShape masterShape = new mxMasterShape(shape, model);
+						Shape masterShape = new Shape(shape, model);
 						this.masterShape = (this.masterShape == null) ? masterShape : this.masterShape;
 						childShapes.put(shapeId, masterShape);
 						processMasterShape(shape, model);
@@ -133,7 +133,7 @@ public class mxVsdxMaster
 	 * Returns the first shape in the Master
 	 * @return First shape in the Master wrapped in a instance of mxMasterShape
 	 */
-	public mxMasterShape getMasterShape()
+	public Shape getMasterShape()
 	{
 		return this.masterShape;
 	}
@@ -143,7 +143,7 @@ public class mxVsdxMaster
 	 * @param id Shape's ID
 	 * @return The shape in the master element with ID = 'id' wrapped in a instance of mxMasterShape
 	 */
-	public mxMasterShape getSubShape(String id)
+	public Shape getSubShape(String id)
 	{
 		return childShapes.get(id);
 	}

+ 30 - 60
src/com/mxgraph/io/vsdx/mxVsdxTextParser.java

@@ -1,9 +1,13 @@
+/**
+ * Copyright (c) 2006-2016, JGraph Ltd
+ * Copyright (c) 2006-2016, Gaudenz Alder
+ */
 package com.mxgraph.io.vsdx;
 
 import java.util.HashMap;
-import java.util.List;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
 
 /**
  * This class allows get the text contained in a shape formated with tags HTML.<br/>
@@ -19,7 +23,7 @@ public class mxVsdxTextParser
 	/**
 	 * Master Shape of the shape.
 	 */
-	mxMasterShape masterShape;
+	Shape masterShape;
 
 	/**
 	 * Style-sheet with the Text Style.
@@ -57,7 +61,7 @@ public class mxVsdxTextParser
 	 * @param masterShape Master Shape of the shape.
 	 * @param styleSheet Style-sheet with the Text Style. 
 	 */
-	public mxVsdxTextParser(VsdxShape shape, mxMasterShape masterShape,	mxStyleSheet styleSheet, mxPropertiesManager pm)
+	public mxVsdxTextParser(VsdxShape shape, Shape masterShape,	mxStyleSheet styleSheet, mxPropertiesManager pm)
 	{
 		this.shape = shape;
 		this.masterShape = masterShape;
@@ -71,36 +75,37 @@ public class mxVsdxTextParser
 	 */
 	public String getHtmlTextContent()
 	{
-		List<Node> child = null;
 		boolean masterShapeOnly = false;
 		String ret = "";
-		child = shape.getTextChildren();
+		NodeList txtChildren = shape.getTextChildren();
 
-		if (child == null && masterShape != null)
+		if (txtChildren == null && masterShape != null)
 		{
-			child = masterShape.getTextChildren();
-			masterShapeOnly = (child != null);
+			txtChildren = masterShape.getTextChildren();
+			masterShapeOnly = (txtChildren != null);
 		}
 		
 		boolean first = true;
 		
-		if (child != null)
+		if (txtChildren != null && txtChildren.getLength() > 0)
 		{
-			for (Node e : child)
+			for (int index = 0; index < txtChildren.getLength(); index++)
 			{
-				if (e.getNodeName().equals("cp"))
+				Node node = txtChildren.item(index);
+	
+				if (node.getNodeName().equals("cp"))
 				{
-					Element elem = (Element) e;
+					Element elem = (Element)node;
 					cp = elem.getAttribute("IX");
 				}
-				else if (e.getNodeName().equals("tp"))
+				else if (node.getNodeName().equals("tp"))
 				{
-					Element elem = (Element) e;
+					Element elem = (Element)node;
 					tp = elem.getAttribute("IX");
 				}
-				else if (e.getNodeName().equals("pp"))
+				else if (node.getNodeName().equals("pp"))
 				{
-					Element elem = (Element) e;
+					Element elem = (Element)node;
 					pp = elem.getAttribute("IX");
 					
 					if (first)
@@ -115,18 +120,18 @@ public class mxVsdxTextParser
 					String para = "<p>";
 					ret += getTextParagraphFormated(para);
 				}
-				else if (e.getNodeName().equals("fld"))
+				else if (node.getNodeName().equals("fld"))
 				{
-					Element elem = (Element) e;
+					Element elem = (Element)node;
 					fld = elem.getAttribute("IX");
 					String text = elem.getTextContent();
 					text = textToList(text, pp);
 					text = text.replaceAll("\n", "<br/>");
 					ret += getTextCharFormated(text);
 				}
-				else if (e.getNodeName().equals("#text"))
+				else if (node.getNodeName().equals("#text"))
 				{
-					String text = e.getTextContent();
+					String text = node.getTextContent();
 					
 					// There's a case in master shapes where the text element has the raw value "N".
 					// The source tool doesn't render this. Example is ALM_Information_flow.vdx, the two label
@@ -150,17 +155,6 @@ public class mxVsdxTextParser
 		String end = first ? "" : "</p>";
 		ret += end;
 		
-//		if (shape.isVertex())
-//		{
-//		ret = mxVdxUtils.surroundByTags(ret, "tr");
-//		ret = mxVdxUtils.surroundByTags(ret, "table");
-//			HashMap<String, String> styleMap = new HashMap<String, String>();
-//			styleMap.put("width", "100%");
-//			styleMap.put("height", "100%");
-//			styleMap.put("max-width", shape.getDimensions().getX() * 0.71 + "px");
-//			ret = insertAttributes(ret, styleMap);
-//		}
-		
 		return ret;
 	}
 
@@ -616,11 +610,6 @@ public class mxVsdxTextParser
 		String direction = "direction:" + this.getCharDirection(cp) + ";";
 		String space = "letter-spacing:" + (Double.parseDouble(this.getCharSpace(cp)) / 0.71) + "px;";
 		int pos = this.getPos(cp);
-		boolean bold = this.isBold(cp);
-		boolean italic = this.isItalic(cp);
-		boolean underline = this.isUnderline(cp);
-		boolean strike = this.isStrikeThru(cp);
-		boolean smallCap = this.isSmallCaps(cp);
 		int tCase = this.getCase(cp);
 
 		if (tCase == 1)
@@ -632,11 +621,6 @@ public class mxVsdxTextParser
 			text = mxVsdxUtils.toInitialCapital(text);
 		}
 		
-		if (smallCap)
-		{
-			text = mxVsdxUtils.toSmallCaps(text, this.getTextSize(cp));
-		}
-
 		if (pos == 1)
 		{
 			text = mxVsdxUtils.surroundByTags(text, "sup");
@@ -646,25 +630,11 @@ public class mxVsdxTextParser
 			text = mxVsdxUtils.surroundByTags(text, "sub");
 		}
 		
-		if (bold)
-		{
-			text = mxVsdxUtils.surroundByTags(text, "b");
-		}
-		
-		if (italic)
-		{
-			text = mxVsdxUtils.surroundByTags(text, "i");
-		}
-		
-		if (underline)
-		{
-			text = mxVsdxUtils.surroundByTags(text, "u");
-		}
-		
-		if (strike)
-		{
-			text = mxVsdxUtils.surroundByTags(text, "s");
-		}
+		text = this.isBold(cp) ? mxVsdxUtils.surroundByTags(text, "b") : text;
+		text = this.isItalic(cp) ? mxVsdxUtils.surroundByTags(text, "i") : text;
+		text = this.isUnderline(cp) ? mxVsdxUtils.surroundByTags(text, "u") : text;
+		text = this.isStrikeThru(cp) ? mxVsdxUtils.surroundByTags(text, "s") : text;
+		text = this.isSmallCaps(cp) ? mxVsdxUtils.toSmallCaps(text, this.getTextSize(cp)) : text;
 
 		ret += "<font style=\"" + size + font + color + direction + space + "\">" + text + "</font>";
 		return ret;

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

@@ -135,24 +135,6 @@ public class mxVsdxUtils
 		return ret;
 	}
 
-	/**
-	 * Map the child of parent using node name as key
-	 * @param parent parent whose children will be mapped
-	 * @return Map of (NodeName, child)
-	 */
-	public static List<Node> copyNodeList(NodeList nodeList)
-	{
-		ArrayList<Node> copy = new ArrayList<Node>();
-		int length = nodeList.getLength();
-
-		for (int i = 0; i < length; i++)
-		{
-			copy.add((Node) nodeList.item(i));
-		}
-
-		return copy;
-	}
-
 	/**
 	 * Returns a collection of direct child Elements that match the specified tag name
 	 * @param parent the parent whose direct children will be processed

+ 0 - 7
src/com/mxgraph/online/OpenServlet.java

@@ -13,7 +13,6 @@ import java.io.PrintWriter;
 import java.net.URLDecoder;
 import java.util.Arrays;
 import java.util.Hashtable;
-import java.util.List;
 import java.util.Map;
 
 import javax.servlet.ServletException;
@@ -25,17 +24,11 @@ import org.apache.commons.fileupload.FileItemIterator;
 import org.apache.commons.fileupload.FileItemStream;
 import org.apache.commons.fileupload.servlet.ServletFileUpload;
 import org.apache.commons.fileupload.util.Streams;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
 
 import com.mxgraph.io.mxCodec;
 import com.mxgraph.io.mxGraphMlCodec;
 import com.mxgraph.io.mxVsdxCodec;
 import com.mxgraph.io.gliffy.importer.GliffyDiagramConverter;
-import com.mxgraph.model.mxIGraphModel;
-import com.mxgraph.util.mxRectangle;
-import com.mxgraph.util.mxUndoableEdit.mxUndoableChange;
 import com.mxgraph.util.mxXmlUtils;
 import com.mxgraph.view.mxGraph;
 import com.mxgraph.view.mxGraphHeadless;

+ 1 - 1
war/cache.manifest

@@ -1,7 +1,7 @@
 CACHE MANIFEST
 
 # THIS FILE WAS GENERATED. DO NOT MODIFY!
-# 11/23/2016 01:33 PM
+# 12/01/2016 11:51 AM
 
 /app.html
 /index.html?offline=1

文件差異過大導致無法顯示
+ 98 - 100
war/js/app.min.js


文件差異過大導致無法顯示
+ 76 - 71
war/js/atlas-viewer.min.js


文件差異過大導致無法顯示
+ 92 - 99
war/js/atlas.min.js


+ 20 - 392
war/js/diagramly/App.js

@@ -656,31 +656,6 @@ if (urlParams['embed'] != '1')
 App.prototype.init = function()
 {
 	EditorUi.prototype.init.apply(this, arguments);
-	
-	/**
-	 * Overrides export dialog for using cloud storage save.
-	 */
-	if (this.isLocalFileSave())
-	{
-		var ui = this;
-		
-		ExportDialog.saveLocalFile = function(data, filename, format)
-		{
-			var mime = 'text/xml';
-			
-			if (format === 'svg')
-			{
-				mime = 'image/svg+xml';
-			}
-			
-    		ui.saveLocalFile(data, filename, mime);
-    	};
-
-    	ExportDialog.saveRequest = function(data, filename, format, fn)
-    	{
-    		ui.saveRequest(data, filename, format, fn);
-    	};
-	}
 
 	/**
 	 * Specifies the default filename.
@@ -2909,21 +2884,16 @@ App.prototype.createFile = function(title, data, libs, mode, done, replace, fold
 
 	if (title != null && this.spinner.spin(document.body, mxResources.get('inserting')))
 	{
-		var bg = this.createBackground();
-		bg.style.zIndex = mxPopupMenu.prototype.zIndex;
-		mxUtils.setOpacity(bg, 50);
-		document.body.appendChild(bg);
-		
 		data = (data != null) ? data : this.emptyDiagramXml;
 		
-		var error = mxUtils.bind(this, function(resp)
+		var complete = mxUtils.bind(this, function()
 		{
-			if (bg.parentNode != null)
-			{
-				bg.parentNode.removeChild(bg);
-			}
-			
 			this.spinner.stop();
+		});
+		
+		var error = mxUtils.bind(this, function(resp)
+		{
+			complete();
 			
 			if (resp == null && this.getCurrentFile() == null && this.dialog == null)
 			{
@@ -2941,8 +2911,7 @@ App.prototype.createFile = function(title, data, libs, mode, done, replace, fold
 
 			this.drive.insertFile(title, data, folderId, mxUtils.bind(this, function(file)
 			{
-				bg.parentNode.removeChild(bg);
-				this.spinner.stop();
+				complete();
 				this.fileCreated(file, libs, replace, done);
 			}), error);
 		}
@@ -2950,8 +2919,7 @@ App.prototype.createFile = function(title, data, libs, mode, done, replace, fold
 		{
 			this.dropbox.insertFile(title, data, mxUtils.bind(this, function(file)
 			{
-				bg.parentNode.removeChild(bg);
-				this.spinner.stop();
+				complete();
 				this.fileCreated(file, libs, replace, done);
 			}), error);
 		}
@@ -2959,15 +2927,13 @@ App.prototype.createFile = function(title, data, libs, mode, done, replace, fold
 		{
 			this.oneDrive.insertFile(title, data, mxUtils.bind(this, function(file)
 			{
-				bg.parentNode.removeChild(bg);
-				this.spinner.stop();
+				complete();
 				this.fileCreated(file, libs, replace, done);
 			}), error, false, folderId);
 		}
 		else if (mode == App.MODE_BROWSER)
 		{
-			bg.parentNode.removeChild(bg);
-			this.spinner.stop();
+			complete();
 			
 			var fn = mxUtils.bind(this, function()
 			{
@@ -2997,8 +2963,7 @@ App.prototype.createFile = function(title, data, libs, mode, done, replace, fold
 		}
 		else
 		{
-			bg.parentNode.removeChild(bg);
-			this.spinner.stop();
+			complete();
 			this.fileCreated(new LocalFile(this, data, title), libs, replace, done);
 		}
 	}
@@ -3031,20 +2996,19 @@ App.prototype.fileCreated = function(file, libs, replace, done)
 	// to save the file again since it needs the newly created file ID for redirecting in HTML
 	if (this.spinner.spin(document.body, mxResources.get('inserting')))
 	{
-		var bg = this.createBackground();
-		bg.style.zIndex = mxPopupMenu.prototype.zIndex;
-		mxUtils.setOpacity(bg, 50);
-		document.body.appendChild(bg);
-		
 		var data = file.getData();
 		file.setData(this.createFileData((data.length > 0) ?
 			this.editor.extractGraphModel(mxUtils.parseXml(data).documentElement, true) : null,
 			null, file, window.location.protocol + '//' + window.location.hostname + url));
-
-		var fn = mxUtils.bind(this, function()
+		
+		var complete = mxUtils.bind(this, function()
 		{
-			bg.parentNode.removeChild(bg);
 			this.spinner.stop();
+		});
+		
+		var fn = mxUtils.bind(this, function()
+		{
+			complete();
 			
 			var fn2 = mxUtils.bind(this, function()
 			{
@@ -3097,8 +3061,7 @@ App.prototype.fileCreated = function(file, libs, replace, done)
 				fn();
 			}), mxUtils.bind(this, function(resp)
 			{
-				bg.parentNode.removeChild(bg);
-				this.spinner.stop();
+				complete();
 				this.handleError(resp);
 			}));
 		}
@@ -3436,136 +3399,6 @@ App.prototype.restoreLibraries = function()
 	}
 };
 
-/**
- *
- */
-App.prototype.exportImage = function(scale, transparentBackground, ignoreSelection, addShadow, editable)
-{
-	if (this.spinner.spin(document.body, mxResources.get('exporting')))
-	{
-		var selectionEmpty = this.editor.graph.isSelectionEmpty();
-		ignoreSelection = (ignoreSelection != null) ? ignoreSelection : selectionEmpty;
-		
-		try
-		{
-		   	this.exportToCanvas(mxUtils.bind(this, function(canvas)
-		   	{
-		   		this.spinner.stop();
-		   		
-		   		try
-		   		{
-		   			// TODO: Should call App.getFileData here
-		   			this.saveCanvas(canvas, (editable) ? this.getFileData(true, null, null, null, ignoreSelection) : null);
-		   		}
-		   		catch (e)
-		   		{
-		   			// Fallback to server-side image export
-		   			if (e.message == 'Invalid image')
-		   			{
-		   				this.downloadFile('png');
-		   			}
-		   			else
-		   			{
-			   			this.handleError(e);
-		   			}
-		   		}
-		   	}), null, null, null, mxUtils.bind(this, function(e)
-		   	{
-		   		this.spinner.stop();
-		   		this.handleError(e);
-		   	}), null, ignoreSelection, scale || 1, transparentBackground, addShadow);
-		}
-		catch (e)
-		{
-			this.spinner.stop();
-			this.handleError(e);
-		}
-	}
-};
-
-/**
- *
- */
-EditorUi.prototype.exportSvg = function(scale, transparentBackground, ignoreSelection, addShadow, editable, embedImages)
-{
-	var selectionEmpty = this.editor.graph.isSelectionEmpty();
-	ignoreSelection = (ignoreSelection != null) ? ignoreSelection : selectionEmpty;
-	var bg = (transparentBackground) ? null : this.editor.graph.background;
-	
-	if (bg == mxConstants.NONE)
-	{
-		bg = null;
-	}
-	
-	// Handles special case where background is null but transparent is false
-	if (bg == null && transparentBackground == false)
-	{
-		bg = '#ffffff';
-	}
-	
-	// Sets or disables alternate text for foreignObjects. Disabling is needed
-	// because PhantomJS seems to ignore switch statements and paint all text.
-	var svgRoot = this.editor.graph.getSvg(bg, scale, null, null, null, ignoreSelection);
-	
-	if (addShadow)
-	{
-		this.editor.addSvgShadow(svgRoot);
-	}
-
-	var file = this.getCurrentFile();
-	var filename = (file != null && file.getTitle() != null) ? file.getTitle() : this.defaultFilename;
-	
-	var dot = filename.lastIndexOf('.');
-	
-	if (dot > 0)
-	{
-		filename = filename.substring(0, dot);
-	}
-	
-	filename += '.svg';
-	
-	if (this.spinner.spin(document.body, mxResources.get('export')))
-	{
-		var doSave = mxUtils.bind(this, function(svgRoot)
-		{
-			this.spinner.stop();
-			
-			if (editable)
-			{
-				svgRoot.setAttribute('content', this.getFileData(true, null, null, null, ignoreSelection));
-			}
-			
-			var svg = '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n' +
-				mxUtils.getXml(svgRoot);
-			
-			// TODO: Depends on App
-    		if (this.isLocalFileSave() || svg.length <= MAX_REQUEST_SIZE)
-    		{
-    	    	this.saveData(filename, 'svg', svg, 'image/svg+xml');
-    		}
-    		else
-    		{
-    			this.handleError({message: mxResources.get('drawingTooLarge')}, mxResources.get('error'), mxUtils.bind(this, function()
-    			{
-    				mxUtils.popup(svg);
-    			}));
-    		}
-		});
-		
-		this.convertMath(this.editor.graph, svgRoot, false, mxUtils.bind(this, function()
-		{
-			if (embedImages)
-			{
-				this.convertImages(svgRoot, doSave);
-			}
-			else
-			{
-				doSave(svgRoot);
-			}
-		}));
-	}
-};
-
 /**
  * Translates this point by the given vector.
  * 
@@ -3718,18 +3551,6 @@ App.prototype.base64ToBlob = function(base64Data, contentType)
     return new Blob(byteArrays, {type: contentType});
 };
 
-/**
- * Returns true if files should be saved using <saveLocalFile>.
- */
-App.prototype.isLocalFileSave = function()
-{
-	return (urlParams['save'] != 'remote' && (mxClient.IS_IE ||
-		(typeof window.Blob !== 'undefined' && typeof window.URL !== 'undefined')) &&
-		document.documentMode != 9 && document.documentMode != 8 &&
-		document.documentMode != 7 && !mxClient.IS_QUIRKS) ||
-		this.isOfflineApp() || mxClient.IS_IOS;
-};
-
 /**
  * Translates this point by the given vector.
  * 
@@ -3840,7 +3661,7 @@ App.prototype.pickFolder = function(mode, fn, enabled)
 	}
 	else
 	{
-		fn(null);
+		EditorUi.prototype.pickFolder.apply(this, arguments);
 	}
 };
 
@@ -3924,199 +3745,6 @@ App.prototype.exportFile = function(data, filename, mimeType, base64Encoded, mod
 	}
 };
 
-/**
- * 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.doSaveLocalFile = function(data, filename, mimeType, base64Encoded)
-{
-	// Newer versions of IE
-	if (window.MSBlobBuilder && navigator.msSaveOrOpenBlob)
-	{
-		var builder = new MSBlobBuilder();
-		builder.append(data);
-		var blob = builder.getBlob(mimeType);
-		navigator.msSaveOrOpenBlob(blob, filename);
-	}
-	// Older versions of IE (binary not supported)
-	else if (mxClient.IS_IE)
-	{
-		var win = window.open('about:blank', '_blank');
-		
-		if (win == null)
-		{
-			mxUtils.popup(data, true);
-		}
-		else
-		{
-			win.document.write(data);
-			win.document.close();
-			win.document.execCommand('SaveAs', true, filename);
-			win.close();
-		}
-	}
-	else if (mxClient.IS_IOS)
-	{
-		// Poor man's saveAs in iOS via context menu of selected output
-    	var dlg = new TextareaDialog(this, filename + ':', data, null, null, mxResources.get('close'));
-    	dlg.textarea.style.width = '600px';
-    	dlg.textarea.style.height = '380px';
-		this.showDialog(dlg.container, 620, 460, true, true);
-		dlg.init();
-		document.execCommand('selectall', false, null);
-	}
-	else if (!this.isOffline() && mxClient.IS_SF)
-	{
-		var param = (typeof(pako) === 'undefined') ? '&xml=' + encodeURIComponent(data) :
-			'&data=' + encodeURIComponent(this.editor.graph.compress(data));
-		
-		new mxXmlRequest(SAVE_URL, 'mime=' + mimeType + '&filename=' +
-			encodeURIComponent(filename) +
-			param).simulate(document, '_blank');
-	}
-	else
-	{
-		var a = document.createElement('a');
-		a.href = URL.createObjectURL((base64Encoded) ?
-			this.base64ToBlob(data, mimeType) :
-			new Blob([data], {type: mimeType}));
-		a.download = filename;
-		document.body.appendChild(a);
-		
-		// Workaround for link opens in same window in Safari
-		if (mxClient.IS_SF)
-		{
-			a.setAttribute('target', '_blank');
-		}
-		
-		try
-		{
-			a.click();
-			
-			window.setTimeout(function()
-			{
-				URL.revokeObjectURL(a.href);
-			}, 0);
-			a.parentNode.removeChild(a);
-		}
-		catch (e)
-		{
-			// ignore
-		}
-	}
-};
-
-/**
- * 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.saveLocalFile = function(data, filename, mimeType, base64Encoded)
-{
-	if (this.isOfflineApp() || this.isOffline())
-	{
-		this.doSaveLocalFile(data, filename, mimeType, base64Encoded);
-	}
-	else
-	{
-		var allowTab = !mxClient.IS_IOS || !navigator.standalone;
-		
-		var dlg = new CreateDialog(this, filename, mxUtils.bind(this, function(newTitle, mode)
-		{
-			try
-			{
-				// Opens a new window
-				if (mode == '_blank')
-				{
-					// Workaround for "Access denied" after URL.createObjectURL
-					// and blank window for window.open with data URI in MS Edge
-					// and empty window for IE 11 and 10
-					if (mxClient.IS_EDGE || document.documentMode == 11 || document.documentMode == 10)
-					{
-			    		var param = (typeof(pako) === 'undefined') ? '&xml=' + encodeURIComponent(data) :
-			    			'&data=' + encodeURIComponent(this.editor.graph.compress(data));
-			    		
-			    		new mxXmlRequest(SAVE_URL, 'mime=' + mimeType + param).simulate(document, '_blank');
-					}
-					else
-					{
-						// Cannot use URL.createObjectURL since it kills gradients in FF
-						window.open('data:' + mimeType + ((base64Encoded) ? ';base64,' +
-							data : ';charset=utf8,' + encodeURIComponent(data)));
-					}
-				}
-				else if (mode == App.MODE_DEVICE)
-				{
-					this.doSaveLocalFile(data, newTitle, mimeType, base64Encoded);
-				} 
-				else if (newTitle != null && newTitle.length > 0)
-				{
-					this.pickFolder(mode, mxUtils.bind(this, function(folderId)
-					{
-						this.exportFile(data, newTitle, mimeType, base64Encoded, mode, folderId);
-					}));
-				}
-			}
-			catch (e)
-			{
-				this.handleError(e);
-			}
-		}), mxUtils.bind(this, function()
-		{
-			this.hideDialog();
-		}), mxResources.get('saveAs'), mxResources.get('download'), false, false, allowTab);
-		this.showDialog(dlg.container, 380, 280, true, true);
-		dlg.init();
-	}
-};
-
-/**
- * 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.saveData = function(filename, format, data, mime)
-{
-	if (this.isLocalFileSave())
-	{
-		this.saveLocalFile(data, filename, mime);
-	}
-	else
-	{
-		this.saveRequest(data, filename, format, mxUtils.bind(this, function(newTitle, base64)
-		{
-    		var param = (typeof(pako) === 'undefined') ? '&xml=' + encodeURIComponent(data) :
-    			'&data=' + encodeURIComponent(this.editor.graph.compress(data));
-    		
-    		return new mxXmlRequest(SAVE_URL, 'format=' + format + ((newTitle != null) ?
-				'&filename=' + encodeURIComponent(newTitle) : '') + param);
-		}));
-	}
-};
-
-/**
- * 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.formatFileSize = function(size)
-{
-    var units = [' kB', ' MB', ' GB', ' TB', 'PB', 'EB', 'ZB', 'YB'];
-	var i = -1;
-    do
-    {
-    	size = size / 1024;
-        i++;
-    } while (size > 1024);
-
-    return Math.max(size, 0.1).toFixed(1) + units[i];
-};
-
 /**
  * Translates this point by the given vector.
  * 

+ 15 - 12
war/js/diagramly/Devel.js

@@ -58,33 +58,36 @@ 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/util/mxJsCanvas.js');
+mxscript(drawDevUrl + 'js/diagramly/util/mxAsyncCanvas.js');
+
 mxscript(drawDevUrl + 'js/diagramly/DrawioFile.js');
 mxscript(drawDevUrl + 'js/diagramly/LocalFile.js');
 mxscript(drawDevUrl + 'js/diagramly/LocalLibrary.js');
 mxscript(drawDevUrl + 'js/diagramly/StorageFile.js');
 mxscript(drawDevUrl + 'js/diagramly/StorageLibrary.js');
+mxscript(drawDevUrl + 'js/diagramly/Dialogs.js');
+mxscript(drawDevUrl + 'js/diagramly/Editor.js');
+mxscript(drawDevUrl + 'js/diagramly/EditorUi.js');
+mxscript(drawDevUrl + 'js/diagramly/Settings.js');
+
+// Excluded in base.min.js
+mxscript(drawDevUrl + 'js/diagramly/DrawioUser.js');
 mxscript(drawDevUrl + 'js/diagramly/UrlLibrary.js');
 mxscript(drawDevUrl + 'js/diagramly/DriveRealtime.js');
 mxscript(drawDevUrl + 'js/diagramly/RealtimeMapping.js');
 mxscript(drawDevUrl + 'js/diagramly/DriveFile.js');
 mxscript(drawDevUrl + 'js/diagramly/DriveLibrary.js');
 mxscript(drawDevUrl + 'js/diagramly/DriveClient.js');
-mxscript(drawDevUrl + 'js/diagramly/DropboxClient.js');
 mxscript(drawDevUrl + 'js/diagramly/DropboxFile.js');
 mxscript(drawDevUrl + 'js/diagramly/DropboxLibrary.js');
-mxscript(drawDevUrl + 'js/diagramly/OneDriveClient.js');
+mxscript(drawDevUrl + 'js/diagramly/DropboxClient.js');
 mxscript(drawDevUrl + 'js/diagramly/OneDriveFile.js');
 mxscript(drawDevUrl + 'js/diagramly/OneDriveLibrary.js');
-mxscript(drawDevUrl + 'js/diagramly/Dialogs.js');
-mxscript(drawDevUrl + 'js/diagramly/Editor.js');
-mxscript(drawDevUrl + 'js/diagramly/EditorUi.js');
-mxscript(drawDevUrl + 'js/diagramly/Settings.js');
-mxscript(drawDevUrl + 'js/diagramly/App.js');
-mxscript(drawDevUrl + 'js/diagramly/DevTools.js');
+mxscript(drawDevUrl + 'js/diagramly/OneDriveClient.js');
 mxscript(drawDevUrl + 'js/diagramly/ChatWindow.js');
+
+mxscript(drawDevUrl + 'js/diagramly/App.js');
 mxscript(drawDevUrl + 'js/diagramly/Menus.js');
 mxscript(drawDevUrl + 'js/diagramly/Pages.js');
-
-mxscript(drawDevUrl + 'js/diagramly/util/mxJsCanvas.js');
-mxscript(drawDevUrl + 'js/diagramly/util/mxAsyncCanvas.js');
+mxscript(drawDevUrl + 'js/diagramly/DevTools.js');

+ 418 - 1
war/js/diagramly/EditorUi.js

@@ -2335,6 +2335,352 @@
    	    this.saveLocalFile(data.substring(data.lastIndexOf(',') + 1), filename, 'image/png', true);
 	};
 	
+	/**
+	 * Returns true if files should be saved using <saveLocalFile>.
+	 */
+	EditorUi.prototype.isLocalFileSave = function()
+	{
+		return (urlParams['save'] != 'remote' && (mxClient.IS_IE ||
+			(typeof window.Blob !== 'undefined' && typeof window.URL !== 'undefined')) &&
+			document.documentMode != 9 && document.documentMode != 8 &&
+			document.documentMode != 7 && !mxClient.IS_QUIRKS) ||
+			this.isOfflineApp() || mxClient.IS_IOS;
+	};
+	
+	/**
+	 * Translates this point by the given vector.
+	 * 
+	 * @param {number} dx X-coordinate of the translation.
+	 * @param {number} dy Y-coordinate of the translation.
+	 */
+	EditorUi.prototype.doSaveLocalFile = function(data, filename, mimeType, base64Encoded)
+	{
+		// Newer versions of IE
+		if (window.MSBlobBuilder && navigator.msSaveOrOpenBlob)
+		{
+			var builder = new MSBlobBuilder();
+			builder.append(data);
+			var blob = builder.getBlob(mimeType);
+			navigator.msSaveOrOpenBlob(blob, filename);
+		}
+		// Older versions of IE (binary not supported)
+		else if (mxClient.IS_IE)
+		{
+			var win = window.open('about:blank', '_blank');
+			
+			if (win == null)
+			{
+				mxUtils.popup(data, true);
+			}
+			else
+			{
+				win.document.write(data);
+				win.document.close();
+				win.document.execCommand('SaveAs', true, filename);
+				win.close();
+			}
+		}
+		else if (mxClient.IS_IOS)
+		{
+			// Poor man's saveAs in iOS via context menu of selected output
+	    	var dlg = new TextareaDialog(this, filename + ':', data, null, null, mxResources.get('close'));
+	    	dlg.textarea.style.width = '600px';
+	    	dlg.textarea.style.height = '380px';
+			this.showDialog(dlg.container, 620, 460, true, true);
+			dlg.init();
+			document.execCommand('selectall', false, null);
+		}
+		else if (!this.isOffline() && mxClient.IS_SF)
+		{
+			var param = (typeof(pako) === 'undefined') ? '&xml=' + encodeURIComponent(data) :
+				'&data=' + encodeURIComponent(this.editor.graph.compress(data));
+			
+			new mxXmlRequest(SAVE_URL, 'mime=' + mimeType + '&filename=' +
+				encodeURIComponent(filename) +
+				param).simulate(document, '_blank');
+		}
+		else
+		{
+			var a = document.createElement('a');
+			a.href = URL.createObjectURL((base64Encoded) ?
+				this.base64ToBlob(data, mimeType) :
+				new Blob([data], {type: mimeType}));
+			a.download = filename;
+			document.body.appendChild(a);
+			
+			// Workaround for link opens in same window in Safari
+			if (mxClient.IS_SF)
+			{
+				a.setAttribute('target', '_blank');
+			}
+			
+			try
+			{
+				a.click();
+				
+				window.setTimeout(function()
+				{
+					URL.revokeObjectURL(a.href);
+				}, 0);
+				a.parentNode.removeChild(a);
+			}
+			catch (e)
+			{
+				// ignore
+			}
+		}
+	};
+	
+	/**
+	 * Translates this point by the given vector.
+	 * 
+	 * @param {number} dx X-coordinate of the translation.
+	 * @param {number} dy Y-coordinate of the translation.
+	 */
+	EditorUi.prototype.saveLocalFile = function(data, filename, mimeType, base64Encoded)
+	{
+		if (this.isOfflineApp() || this.isOffline())
+		{
+			this.doSaveLocalFile(data, filename, mimeType, base64Encoded);
+		}
+		else
+		{
+			var allowTab = !mxClient.IS_IOS || !navigator.standalone;
+			var backends = typeof window.DriveClient === 'function' ||
+						typeof window.DropboxClient === 'function' ||
+						typeof window.OneDriveClient === 'function';
+			
+			var dlg = new CreateDialog(this, filename, mxUtils.bind(this, function(newTitle, mode)
+			{
+				try
+				{
+					// Opens a new window
+					if (mode == '_blank')
+					{
+						// Workaround for "Access denied" after URL.createObjectURL
+						// and blank window for window.open with data URI in MS Edge
+						// and empty window for IE 11 and 10
+						if (mxClient.IS_EDGE || document.documentMode == 11 || document.documentMode == 10)
+						{
+				    		var param = (typeof(pako) === 'undefined') ? '&xml=' + encodeURIComponent(data) :
+				    			'&data=' + encodeURIComponent(this.editor.graph.compress(data));
+				    		
+				    		new mxXmlRequest(SAVE_URL, 'mime=' + mimeType + param).simulate(document, '_blank');
+						}
+						else
+						{
+							// Cannot use URL.createObjectURL since it kills gradients in FF
+							window.open('data:' + mimeType + ((base64Encoded) ? ';base64,' +
+								data : ';charset=utf8,' + encodeURIComponent(data)));
+						}
+					}
+					else if (mode == App.MODE_DEVICE)
+					{
+						this.doSaveLocalFile(data, newTitle, mimeType, base64Encoded);
+					} 
+					else if (newTitle != null && newTitle.length > 0)
+					{
+						this.pickFolder(mode, mxUtils.bind(this, function(folderId)
+						{
+							this.exportFile(data, newTitle, mimeType, base64Encoded, mode, folderId);
+						}));
+					}
+				}
+				catch (e)
+				{
+					this.handleError(e);
+				}
+			}), mxUtils.bind(this, function()
+			{
+				this.hideDialog();
+			}), mxResources.get('saveAs'), mxResources.get('download'), false, false, allowTab);
+			this.showDialog(dlg.container, 380, (backends) ? 280 : 160, true, true);
+			dlg.init();
+		}
+	};
+
+	/**
+	 * Translates this point by the given vector.
+	 * 
+	 * @param {number} dx X-coordinate of the translation.
+	 * @param {number} dy Y-coordinate of the translation.
+	 */
+	EditorUi.prototype.saveData = function(filename, format, data, mime)
+	{
+		if (this.isLocalFileSave())
+		{
+			this.saveLocalFile(data, filename, mime);
+		}
+		else
+		{
+			this.saveRequest(data, filename, format, mxUtils.bind(this, function(newTitle, base64)
+			{
+	    		var param = (typeof(pako) === 'undefined') ? '&xml=' + encodeURIComponent(data) :
+	    			'&data=' + encodeURIComponent(this.editor.graph.compress(data));
+	    		
+	    		return new mxXmlRequest(SAVE_URL, 'format=' + format + ((newTitle != null) ?
+					'&filename=' + encodeURIComponent(newTitle) : '') + param);
+			}));
+		}
+	};
+	
+	/**
+	 * Translates this point by the given vector.
+	 * 
+	 * @param {number} dx X-coordinate of the translation.
+	 * @param {number} dy Y-coordinate of the translation.
+	 */
+	EditorUi.prototype.saveRequest = function(data, filename, format, fn)
+	{
+		var allowTab = !mxClient.IS_IOS || !navigator.standalone;
+		
+		var dlg = new CreateDialog(this, filename, mxUtils.bind(this, function(newTitle, mode)
+		{
+			if (mode == '_blank' || newTitle != null && newTitle.length > 0)
+			{
+				var base64 = (mode == App.MODE_DEVICE || mode == null || mode == '_blank') ? '0' : '1';
+				var xhr = fn((mode == '_blank') ? null : newTitle, base64);
+				
+				if (mode == App.MODE_DEVICE || mode == '_blank')
+				{
+					xhr.simulate(document, '_blank');
+				}
+				else
+				{
+					// TODO: Move to App
+					this.pickFolder(mode, mxUtils.bind(this, function(folderId)
+					{
+						if (this.spinner.spin(document.body, mxResources.get('saving')))
+						{
+							// LATER: Catch possible mixed content error
+							// see http://stackoverflow.com/questions/30646417/catching-mixed-content-error
+							xhr.send(mxUtils.bind(this, function()
+							{
+								this.spinner.stop();
+								
+								if (xhr.getStatus() < 200 || xhr.getStatus() > 299)
+								{
+									this.handleError({message: mxResources.get('errorSavingFile')});
+								}
+								else
+								{
+									try
+									{
+										var mimeType = (format == 'pdf') ? 'application/pdf' : 'image/' + format;
+										this.exportFile(xhr.getText(), newTitle, mimeType, true, mode, folderId);
+									}
+									catch (e)
+									{
+										this.handleError(e);
+									}
+								}
+							}), function(resp)
+							{
+								this.spinner.stop();
+								this.handleError(resp);
+							});
+						}
+					}));
+				}
+			}
+		}), mxUtils.bind(this, function()
+		{
+			this.hideDialog();
+		}), mxResources.get('saveAs'), mxResources.get('download'), false, false, allowTab);
+		this.showDialog(dlg.container, 380, 270, true, true);
+		dlg.init();
+	};
+	
+	/**
+	 * Hook for subclassers.
+	 */
+	EditorUi.prototype.pickFolder = function(mode, fn, enabled)
+	{
+		fn(null);
+	};
+
+	/**
+	 *
+	 */
+	EditorUi.prototype.exportSvg = function(scale, transparentBackground, ignoreSelection, addShadow, editable, embedImages)
+	{
+		var selectionEmpty = this.editor.graph.isSelectionEmpty();
+		ignoreSelection = (ignoreSelection != null) ? ignoreSelection : selectionEmpty;
+		var bg = (transparentBackground) ? null : this.editor.graph.background;
+		
+		if (bg == mxConstants.NONE)
+		{
+			bg = null;
+		}
+		
+		// Handles special case where background is null but transparent is false
+		if (bg == null && transparentBackground == false)
+		{
+			bg = '#ffffff';
+		}
+		
+		// Sets or disables alternate text for foreignObjects. Disabling is needed
+		// because PhantomJS seems to ignore switch statements and paint all text.
+		var svgRoot = this.editor.graph.getSvg(bg, scale, null, null, null, ignoreSelection);
+		
+		if (addShadow)
+		{
+			this.editor.addSvgShadow(svgRoot);
+		}
+	
+		var file = this.getCurrentFile();
+		var filename = (file != null && file.getTitle() != null) ? file.getTitle() : this.defaultFilename;
+		
+		var dot = filename.lastIndexOf('.');
+		
+		if (dot > 0)
+		{
+			filename = filename.substring(0, dot);
+		}
+		
+		filename += '.svg';
+		
+		if (this.spinner.spin(document.body, mxResources.get('export')))
+		{
+			var doSave = mxUtils.bind(this, function(svgRoot)
+			{
+				this.spinner.stop();
+				
+				if (editable)
+				{
+					svgRoot.setAttribute('content', this.getFileData(true, null, null, null, ignoreSelection));
+				}
+				
+				var svg = '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n' +
+					mxUtils.getXml(svgRoot);
+				
+	    		if (this.isLocalFileSave() || svg.length <= MAX_REQUEST_SIZE)
+	    		{
+	    	    	this.saveData(filename, 'svg', svg, 'image/svg+xml');
+	    		}
+	    		else
+	    		{
+	    			this.handleError({message: mxResources.get('drawingTooLarge')}, mxResources.get('error'), mxUtils.bind(this, function()
+	    			{
+	    				mxUtils.popup(svg);
+	    			}));
+	    		}
+			});
+			
+			this.convertMath(this.editor.graph, svgRoot, false, mxUtils.bind(this, function()
+			{
+				if (embedImages)
+				{
+					this.convertImages(svgRoot, doSave);
+				}
+				else
+				{
+					doSave(svgRoot);
+				}
+			}));
+		}
+	};
+
 	/**
 	 * 
 	 */
@@ -3077,6 +3423,52 @@
 				mxUtils.getXml(svgRoot);
 		}
 	};
+	
+	/**
+	 *
+	 */
+	EditorUi.prototype.exportImage = function(scale, transparentBackground, ignoreSelection, addShadow, editable)
+	{
+		if (this.spinner.spin(document.body, mxResources.get('exporting')))
+		{
+			var selectionEmpty = this.editor.graph.isSelectionEmpty();
+			ignoreSelection = (ignoreSelection != null) ? ignoreSelection : selectionEmpty;
+			
+			try
+			{
+			   	this.exportToCanvas(mxUtils.bind(this, function(canvas)
+			   	{
+			   		this.spinner.stop();
+			   		
+			   		try
+			   		{
+			   			this.saveCanvas(canvas, (editable) ? this.getFileData(true, null, null, null, ignoreSelection) : null);
+			   		}
+			   		catch (e)
+			   		{
+			   			// Fallback to server-side image export
+			   			if (e.message == 'Invalid image')
+			   			{
+			   				this.downloadFile('png');
+			   			}
+			   			else
+			   			{
+				   			this.handleError(e);
+			   			}
+			   		}
+			   	}), null, null, null, mxUtils.bind(this, function(e)
+			   	{
+			   		this.spinner.stop();
+			   		this.handleError(e);
+			   	}), null, ignoreSelection, scale || 1, transparentBackground, addShadow);
+			}
+			catch (e)
+			{
+				this.spinner.stop();
+				this.handleError(e);
+			}
+		}
+	};
 
 	/**
 	 *
@@ -4432,7 +4824,32 @@
 			// LATER: Add shadow for labels in graph.container (eg. math, NO_FO), scaling
 			this.editor.addSvgShadow(graph.view.canvas.ownerSVGElement, null, true);
 		}
-		
+				
+		/**
+		 * Overrides export dialog for using ui functions for save.
+		 */
+		if (typeof ExportDialog !== 'undefined' && this.isLocalFileSave())
+		{
+			var ui = this;
+			
+			ExportDialog.saveLocalFile = function(data, filename, format)
+			{
+				var mime = 'text/xml';
+				
+				if (format === 'svg')
+				{
+					mime = 'image/svg+xml';
+				}
+				
+	    		ui.saveLocalFile(data, filename, mime);
+	    	};
+	
+	    	ExportDialog.saveRequest = function(data, filename, format, fn)
+	    	{
+	    		ui.saveRequest(data, filename, format, fn);
+	    	};
+		}
+
 		/**
 		 * Specifies the default filename.
 		 */

+ 2 - 3
war/js/diagramly/Menus.js

@@ -303,7 +303,8 @@
 		var copiedStyles = ['rounded', 'shadow', 'dashed', 'dashPattern', 'fontFamily', 'fontSize', 'fontColor', 'fontStyle', 'align',
 		                    'verticalAlign', 'strokeColor', 'strokeWidth', 'fillColor', 'gradientColor', 'swimlaneFillColor',
 		                    'textOpacity', 'gradientDirection', 'glass', 'labelBackgroundColor', 'labelBorderColor', 'opacity',
-		                    'spacing', 'spacingTop', 'spacingLeft', 'spacingBottom', 'spacingRight'];
+		                    'spacing', 'spacingTop', 'spacingLeft', 'spacingBottom', 'spacingRight', 'endFill', 'endArrow',
+		                    'endSize', 'startStill', 'startArrow', 'startSize'];
 		
 		editorUi.actions.addAction('copyStyle', function()
 		{
@@ -333,8 +334,6 @@
 			 			}
 			 		}
 				}
-				
-				editorUi.copiedEdgeStyle = null;
 			}
 		}, null, null, 'Ctrl+Shift+C');
 

文件差異過大導致無法顯示
+ 2 - 2
war/js/embed-static.min.js


文件差異過大導致無法顯示
+ 2 - 2
war/js/reader.min.js


文件差異過大導致無法顯示
+ 78 - 73
war/js/viewer.min.js