Browse Source

6.6.4 release

Former-commit-id: d7765ba405a341820893bad847f06fe2c5cd9fa3
Gaudenz Alder 8 years ago
parent
commit
a793f6c3d9

+ 9 - 0
ChangeLog

@@ -1,3 +1,12 @@
+24-MAY-2017: 6.6.4
+
+- Fixes possible data loss for back button
+- Enables OneDrive support on iOS
+
+22-MAY-2017: 6.6.3
+
+- vsdx export improvements
+
 19-MAY-2017: 6.6.2
 
 - Adds trees plugin

+ 1 - 1
VERSION

@@ -1 +1 @@
-6.6.2
+6.6.4

+ 33 - 19
src/com/mxgraph/online/OpenServlet.java

@@ -24,6 +24,7 @@ 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.apache.commons.lang3.StringEscapeUtils;
 
 import com.mxgraph.io.mxCodec;
 import com.mxgraph.io.mxGraphMlCodec;
@@ -128,10 +129,10 @@ public class OpenServlet extends HttpServlet
 						filename = item.getName();
 						vsdx = filename.toLowerCase().endsWith(".vsdx");
 						vssx = filename.toLowerCase().endsWith(".vssx");
-						
+
 						if (vsdx || vssx)
 						{
-							upfile = Streams.asString(stream, "ISO-8859-1");  
+							upfile = Streams.asString(stream, "ISO-8859-1");
 						}
 						else
 						{
@@ -152,11 +153,11 @@ public class OpenServlet extends HttpServlet
 				}
 
 				String xml = null;
-				
+
 				if (filename.toLowerCase().endsWith(".png"))
 				{
-					xml = extractXmlFromPng(upfile
-							.getBytes(Utils.CHARSET_FOR_URL_ENCODING));
+					xml = extractXmlFromPng(
+							upfile.getBytes(Utils.CHARSET_FOR_URL_ENCODING));
 				}
 				else if (ENABLE_GRAPHML_SUPPORT && upfile.matches(graphMlRegex))
 				{
@@ -165,12 +166,14 @@ public class OpenServlet extends HttpServlet
 					mxGraph graph = new mxGraphHeadless();
 
 					mxGraphMlCodec.decode(mxXmlUtils.parseXml(upfile), graph);
-					xml = mxXmlUtils.getXml(new mxCodec().encode(graph.getModel()));
+					xml = mxXmlUtils
+							.getXml(new mxCodec().encode(graph.getModel()));
 				}
 				else if (ENABLE_VSDX_SUPPORT && vsdx)
 				{
 					mxVsdxCodec vdxCodec = new mxVsdxCodec();
-					xml = vdxCodec.decodeVsdx(upfile.getBytes("ISO-8859-1"), Utils.CHARSET_FOR_URL_ENCODING);
+					xml = vdxCodec.decodeVsdx(upfile.getBytes("ISO-8859-1"),
+							Utils.CHARSET_FOR_URL_ENCODING);
 
 					// Replaces VSDX extension
 					int dot = filename.lastIndexOf('.');
@@ -179,7 +182,8 @@ public class OpenServlet extends HttpServlet
 				else if (ENABLE_VSSX_SUPPORT && vssx)
 				{
 					mxVssxCodec vssxCodec = new mxVssxCodec();
-					xml = vssxCodec.decodeVssx(upfile.getBytes("ISO-8859-1"), Utils.CHARSET_FOR_URL_ENCODING);
+					xml = vssxCodec.decodeVssx(upfile.getBytes("ISO-8859-1"),
+							Utils.CHARSET_FOR_URL_ENCODING);
 
 					// Replaces VSDX extension
 					int dot = filename.lastIndexOf('.');
@@ -210,10 +214,12 @@ public class OpenServlet extends HttpServlet
 					{
 						// Workaround for replacement char and null byte in IE9 request
 						xml = xml.replaceAll("[\\uFFFD\\u0000]*", "");
-						writeScript(writer, "try{window.parent.setCurrentXml(decodeURIComponent('"
-							+ Utils.encodeURIComponent(xml, Utils.CHARSET_FOR_URL_ENCODING)
-							+ "'), decodeURIComponent('" + Utils.encodeURIComponent(filename, Utils.CHARSET_FOR_URL_ENCODING)
-							+ "'));}catch(e){window.parent.showOpenAlert({message:window.parent.mxResources.get('notAUtf8File')});}");
+						writeScript(writer,
+								"try{window.parent.setCurrentXml(decodeURIComponent('"
+										+ encodeString(xml)
+										+ "'), decodeURIComponent('"
+										+ encodeString(filename)
+										+ "'));}catch(e){window.parent.showOpenAlert({message:window.parent.mxResources.get('notAUtf8File')});}");
 					}
 				}
 				else
@@ -223,9 +229,9 @@ public class OpenServlet extends HttpServlet
 			}
 			else
 			{
-				response.setStatus(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE);
-				writeScript(
-						writer,
+				response.setStatus(
+						HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE);
+				writeScript(writer,
 						"window.parent.showOpenAlert(window.parent.mxResources.get('drawingTooLarge'));");
 			}
 		}
@@ -233,8 +239,7 @@ public class OpenServlet extends HttpServlet
 		{
 			e.printStackTrace();
 			response.setStatus(HttpServletResponse.SC_NOT_FOUND);
-			writeScript(
-					writer,
+			writeScript(writer,
 					"window.parent.showOpenAlert(window.parent.mxResources.get('invalidOrMissingFile'));");
 		}
 
@@ -242,6 +247,15 @@ public class OpenServlet extends HttpServlet
 		writer.close();
 	}
 
+	/**
+	 * URI encodes the given string for JavaScript.
+	 */
+	protected String encodeString(String s)
+	{
+		return StringEscapeUtils.escapeEcmaScript(
+				Utils.encodeURIComponent(s, Utils.CHARSET_FOR_URL_ENCODING));
+	};
+
 	/**
 	 * Writes the given string as a script in a HTML page to the given print writer.
 	 */
@@ -259,8 +273,8 @@ public class OpenServlet extends HttpServlet
 	// NOTE: Key length must not be longer than 79 bytes (not checked)
 	protected String extractXmlFromPng(byte[] data)
 	{
-		Map<String, String> textChunks = decodeCompressedText(new ByteArrayInputStream(
-				data));
+		Map<String, String> textChunks = decodeCompressedText(
+				new ByteArrayInputStream(data));
 
 		return (textChunks != null) ? textChunks.get("mxGraphModel") : null;
 	}

+ 1 - 1
war/cache.manifest

@@ -1,7 +1,7 @@
 CACHE MANIFEST
 
 # THIS FILE WAS GENERATED. DO NOT MODIFY!
-# 05/19/2017 03:49 PM
+# 05/24/2017 04:23 PM
 
 app.html
 index.html?offline=1

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


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


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


+ 24 - 12
war/js/diagramly/App.js

@@ -320,8 +320,7 @@ App.getStoredMode = function()
 				// Loads OneDrive for all browsers but IE6/IOS if not disabled or if enabled and in embed mode
 				if (typeof window.OneDriveClient === 'function')
 				{
-					if (urlParams['od'] != '0' && !/(iPad|iPhone|iPod)/.test(navigator.userAgent) &&
-						(navigator.userAgent.indexOf('MSIE') < 0 || document.documentMode >= 10))
+					if (urlParams['od'] != '0' && (navigator.userAgent.indexOf('MSIE') < 0 || document.documentMode >= 10))
 					{
 						// Immediately loads client
 						if (App.mode == App.MODE_ONEDRIVE || (window.location.hash != null &&
@@ -627,8 +626,7 @@ App.main = function(callback)
 			if (typeof window.OneDriveClient === 'function' &&
 				(typeof OneDrive === 'undefined' && window.DrawOneDriveClientCallback != null &&
 				(((urlParams['embed'] != '1' && urlParams['od'] != '0') || (urlParams['embed'] == '1' &&
-				urlParams['od'] == '1')) && !/(iPad|iPhone|iPod)/.test(navigator.userAgent) &&
-				(navigator.userAgent.indexOf('MSIE') < 0 || document.documentMode >= 10))))
+				urlParams['od'] == '1')) && (navigator.userAgent.indexOf('MSIE') < 0 || document.documentMode >= 10))))
 			{
 				mxscript(App.ONEDRIVE_URL, window.DrawOneDriveClientCallback);
 			}
@@ -2413,7 +2411,7 @@ App.prototype.showSplash = function(force)
 	}
 	else if (this.mode == null || force)
 	{
-		var rowLimit = (serviceCount <= 4) ? 4 : 3;
+		var rowLimit = (serviceCount <= 4) ? 2 : 3;
 		
 		var dlg = new StorageDialog(this, mxUtils.bind(this, function()
 		{
@@ -2921,9 +2919,14 @@ App.prototype.saveFile = function(forceDialog)
 			var filename = (file.getTitle() != null) ? file.getTitle() : this.defaultFilename;
 			var allowTab = !mxClient.IS_IOS || !navigator.standalone;
 			var prev = this.mode;
-			
 			var serviceCount = this.getServiceCount(true);
-			var rowLimit = (serviceCount <= 4) ? 4 : 3;
+			
+			if (isLocalStorage)
+			{
+				serviceCount++;
+			}
+			
+			var rowLimit = (serviceCount <= 4) ? 2 : 3;
 			
 			var dlg = new CreateDialog(this, filename, mxUtils.bind(this, function(name, mode)
 			{
@@ -3376,7 +3379,12 @@ App.prototype.loadFile = function(id, sameWindow, file)
 	
 	var fn2 = mxUtils.bind(this, function()
 	{
-		if (this.spinner.spin(document.body, mxResources.get('loading')))
+		if (id == null || id.length == 0)
+		{
+			this.editor.setStatus('');
+			this.fileLoaded(null);
+		}
+		else if (this.spinner.spin(document.body, mxResources.get('loading')))
 		{
 			// Handles files from localStorage
 			if (id.charAt(0) == 'L')
@@ -3597,15 +3605,19 @@ App.prototype.loadFile = function(id, sameWindow, file)
 		}
 		else
 		{
-			this.confirm(mxResources.get('allChangesLost'), null, fn2,
-				mxResources.get('cancel'), mxResources.get('discardChanges'));
+			this.confirm(mxResources.get('allChangesLost'), mxUtils.bind(this, function()
+			{
+				if (currentFile != null)
+				{
+					window.location.hash = currentFile.getHash();
+				}
+			}), fn2, mxResources.get('cancel'), mxResources.get('discardChanges'));
 		}
 	});
 	
 	if (id == null || id.length == 0)
 	{
-		this.editor.setStatus('');
-		this.fileLoaded(null);
+		fn();
 	}
 	else if (currentFile != null && currentFile.isModified() && !sameWindow)
 	{

+ 3 - 31
war/js/diagramly/Editor.js

@@ -114,44 +114,16 @@
 		'Tessa Valet,HR Director,tva,Office 4,Evan Miller,me@example.com,#d5e8d4,#82b366,,https://www.draw.io,https://cdn3.iconfinder.com/data/icons/user-avatars-1/512/users-3-128.png\n';
 	
 	/**
-	 * Global configuration of the Editor. Possible configuration values are:
-	 * 
-	 * - presetColors: array of color codes for upper palette, default is ColorDialog.prototype.presetColors (no leading #)
-	 * - defaultColors: array of color codes for the lower palette, default is ColorDialog.prototype.defaultColors (no leading #)
-	 * - defaultFonts: array of available font family names, default is Menus.prototype.defaultFonts
-	 * - defaultColorSchemes: array of array of available color schemes, default is StyleFormatPanel.prototype.defaultColorSchemes
-	 * 
-	 * (code)
-	 * Editor.configure({defaultFonts: ['Helvetica', 'Verdana', 'Times New Roman', 'Garamond',
-	 * 		'Comic Sans MS', 'Courier New', 'Georgia', 'Lucida Console', 'Tahoma'],
-	 *   presetColors: ['E6D0DE', 'CDA2BE', 'B5739D', 'E1D5E7', 'C3ABD0', 'A680B8', 'D4E1F5',
-	 *   	'A9C4EB', '7EA6E0', 'D5E8D4', '9AC7BF', '67AB9F', 'D5E8D4', 'B9E0A5', '97D077', 'FFF2CC',
-	 *   	'FFE599', 'FFD966', 'FFF4C3', 'FFCE9F', 'FFB570', 'F8CECC', 'F19C99', 'EA6B66'],
-	 * 	 defaultColorSchemes: [[null, {fill: '#f5f5f5', stroke: '#666666'},
-	 * 		{fill: '#dae8fc', stroke: '#6c8ebf'}, {fill: '#d5e8d4', stroke: '#82b366'},
-	 * 		{fill: '#ffe6cc', stroke: '#d79b00'}, {fill: '#fff2cc', stroke: '#d6b656'},
-	 * 		{fill: '#f8cecc', stroke: '#b85450'}, {fill: '#e1d5e7', stroke: '#9673a6'}],
-	 * 	    [null,
-	 * 		{fill: '#f5f5f5', stroke: '#666666', gradient: '#b3b3b3'},
-	 * 		{fill: '#dae8fc', stroke: '#6c8ebf', gradient: '#7ea6e0'},
-	 * 		{fill: '#d5e8d4', stroke: '#82b366', gradient: '#97d077'},
-	 * 		{fill: '#ffcd28', stroke: '#d79b00', gradient: '#ffa500'},
-	 * 		{fill: '#fff2cc', stroke: '#d6b656', gradient: '#ffd966'},
-	 * 		{fill: '#f8cecc', stroke: '#b85450', gradient: '#ea6b66'},
-	 * 		{fill: '#e6d0de', stroke: '#996185', gradient: '#d5739d'}],
-	 * 		[null, {fill: '#eeeeee', stroke: '#36393d'},
-	 * 		{fill: '#f9f7ed', stroke: '#36393d'}, {fill: '#ffcc99', stroke: '#36393d'},
-	 * 		{fill: '#cce5ff', stroke: '#36393d'}, {fill: '#ffff88', stroke: '#36393d'},
-	 * 		{fill: '#cdeb8b', stroke: '#36393d'}, {fill: '#ffcccc', stroke: '#36393d'}]]});
-	 * (end)
+	 * Global configuration of the Editor
+	 * see https://desk.draw.io/solution/articles/16000058316
 	 */
 	Editor.configure = function(config)
 	{
 		// LATER: DefaultFont and DefaultFontSize should override Graph's stylesheet,
 		// default edge and vertex styles would have to be set before loading mxSettings
+		Menus.prototype.defaultFonts = config.defaultFonts || Menus.prototype.defaultFonts;
 		ColorDialog.prototype.presetColors = config.presetColors || ColorDialog.prototype.presetColors;
 		ColorDialog.prototype.defaultColors = config.defaultColors || ColorDialog.prototype.defaultColors;
-		Menus.prototype.defaultFonts = config.defaultFonts || Menus.prototype.defaultFonts;
 		StyleFormatPanel.prototype.defaultColorSchemes = config.defaultColorSchemes || StyleFormatPanel.prototype.defaultColorSchemes;
 	};
 

File diff suppressed because it is too large
+ 320 - 178
war/js/diagramly/vsdx/VsdxExport.js


+ 507 - 87
war/js/diagramly/vsdx/mxVsdxCanvas2D.js

@@ -8,16 +8,9 @@
  *
  * Constructs a new abstract canvas.
  */
-function mxVsdxCanvas2D(root)
+function mxVsdxCanvas2D(zip)
 {
 	mxAbstractCanvas2D.call(this);
-
-	/**
-	 * Variable: root
-	 * 
-	 * Reference to the container for the SVG content.
-	 */
-	this.root = root;
 };
 
 /**
@@ -26,15 +19,228 @@ function mxVsdxCanvas2D(root)
 mxUtils.extend(mxVsdxCanvas2D, mxAbstractCanvas2D);
 
 
+/**
+ * Variable: textEnabled
+ * 
+ * Specifies if text output should be enabled. Default is true.
+ */
+mxVsdxCanvas2D.prototype.textEnabled = true;
+
+/**
+ * Function: init
+ *  
+ * Initialize the canvas for a new vsdx file.
+ */
+mxVsdxCanvas2D.prototype.init = function (zip)
+{
+	this.filesLoading = 0;
+	this.zip = zip;
+};
+
+/**
+ * Function: createGeoSec
+ *  
+ * Create a new geo section.
+ */
+mxVsdxCanvas2D.prototype.createGeoSec = function ()
+{
+	if (this.geoSec != null)
+	{
+		this.shape.appendChild(this.geoSec);
+	}
+	
+	var geoSec = this.xmlDoc.createElement("Section");
+	
+	geoSec.setAttribute("N", "Geometry");
+	geoSec.setAttribute("IX", this.geoIndex++);
+	
+	this.geoSec = geoSec;
+	this.geoStepIndex = 1;
+	this.lastX = 0;
+	this.lastY = 0;
+	this.lastMoveToX = 0;
+	this.lastMoveToY = 0;
+};
+
+
+/**
+ * Function: newShape
+ *  
+ * Create a new shape.
+ */
+mxVsdxCanvas2D.prototype.newShape = function (shape, xmGeo, xmlDoc)
+{
+	this.geoIndex = 0;
+	this.shape = shape;
+	this.xmGeo = xmGeo;
+	this.xmlDoc = xmlDoc;
+	this.geoSec = null;
+	this.shapeImg = null;
+	this.shapeType = "Shape";
+	
+	this.createGeoSec();
+};
+
+/**
+ * Function: endShape
+ *  
+ * End current shape.
+ */
+mxVsdxCanvas2D.prototype.endShape = function ()
+{
+	if (this.shapeImg != null)
+	{
+		this.addForeignData(this.shapeImg.type, this.shapeImg.id);
+	}
+};
+
 
 /**
- * Function: createElement
+ * Function: newPage
+ *  
+ * Start a new page.
+ */
+mxVsdxCanvas2D.prototype.newPage = function ()
+{
+	this.images = [];
+};
+
+/**
+ * Function: newPage
+ *  
+ * Start a new page.
+ */
+mxVsdxCanvas2D.prototype.getShapeType = function ()
+{
+	return this.shapeType;
+};
+
+/**
+ * Function: getShapeGeo
+ *  
+ * return the current geo section.
+ */
+mxVsdxCanvas2D.prototype.getShapeGeo = function ()
+{
+	return this.geoSec;
+};
+
+/**
+ * Function: createCellElemScaled
  * 
- * Creates the given element using the owner document of <root>.
+ * Creates a cell element and scale the value.
  */
-mxXmlCanvas2D.prototype.createElement = function(name)
+mxVsdxCanvas2D.prototype.createCellElemScaled = function (name, val, formula)
 {
-	return this.root.ownerDocument.createElement(name);
+	return this.createCellElem(name, val / VsdxExport.prototype.CONVERSION_FACTOR, formula);
+};
+
+/**
+ * Function: createCellElem
+ * 
+ * Creates a cell element.
+ */
+mxVsdxCanvas2D.prototype.createCellElem = function (name, val, formula)
+{
+	var cell = this.xmlDoc.createElement("Cell");
+	cell.setAttribute("N", name);
+	cell.setAttribute("V", val);
+	
+	if (formula) cell.setAttribute("F", formula);
+
+	return cell;
+};
+
+mxVsdxCanvas2D.prototype.createRowRel = function(type, index, x, y, a, b, c , d) 
+{
+	var row = this.xmlDoc.createElement("Row");
+	row.setAttribute("T", type);
+	row.setAttribute("IX", index);
+	row.appendChild(this.createCellElem("X", x));
+	row.appendChild(this.createCellElem("Y", y));
+	
+	if (a != null) row.appendChild(this.createCellElem("A", a));
+	if (b != null) row.appendChild(this.createCellElem("B", b));
+	if (c != null) row.appendChild(this.createCellElem("C", c));
+	if (d != null) row.appendChild(this.createCellElem("D", d));
+	
+	return row;
+};
+
+
+/**
+ * Function: begin
+ * 
+ * Extends superclass to create path.
+ */
+mxVsdxCanvas2D.prototype.begin = function()
+{
+	if (this.geoStepIndex > 1)	this.createGeoSec();
+};
+
+/**
+ * Function: rect
+ * 
+ * Private helper function to create SVG elements
+ */
+mxVsdxCanvas2D.prototype.rect = function(x, y, w, h)
+{
+	if (this.geoStepIndex > 1)	this.createGeoSec();
+	
+	var s = this.state;
+	w = w * s.scale;
+	h = h * s.scale;
+
+	var geo = this.xmGeo;
+	x = ((x - geo.x + s.dx) * s.scale) /w;
+	y = ((geo.height - y + geo.y - s.dy) * s.scale) /h;
+
+	this.geoSec.appendChild(this.createRowRel("RelMoveTo", this.geoStepIndex++, x, y));
+	this.geoSec.appendChild(this.createRowRel("RelLineTo", this.geoStepIndex++, x + 1, y));
+	this.geoSec.appendChild(this.createRowRel("RelLineTo", this.geoStepIndex++, x + 1, y - 1));
+	this.geoSec.appendChild(this.createRowRel("RelLineTo", this.geoStepIndex++, x, y - 1));
+	this.geoSec.appendChild(this.createRowRel("RelLineTo", this.geoStepIndex++, x, y));	
+};
+
+/**
+ * Function: roundrect
+ * 
+ * Private helper function to create SVG elements
+ */
+mxVsdxCanvas2D.prototype.roundrect = function(x, y, w, h, dx, dy)
+{
+	this.rect(x, y, w, h);
+	//TODO this assume dx and dy are equal and only one rounding is needed
+	this.shape.appendChild(this.createCellElemScaled("Rounding", dx));
+};
+
+/**
+ * Function: ellipse
+ * 
+ * Private helper function to create SVG elements
+ */
+mxVsdxCanvas2D.prototype.ellipse = function(x, y, w, h)
+{
+	if (this.geoStepIndex > 1)	this.createGeoSec();
+	
+	var s = this.state;
+	w = w * s.scale;
+	h = h * s.scale;
+	
+	var geo = this.xmGeo;
+	var gh = geo.height * s.scale;
+	var gw = geo.width * s.scale;
+	x = (x - geo.x + s.dx) * s.scale;
+	y = gh + (-y + geo.y - s.dy) * s.scale;
+
+	var hr = h/gh;
+	var wr = w/gw;
+	
+	this.geoSec.appendChild(this.createRowRel("RelMoveTo", this.geoStepIndex++, x/gw, y/gh - hr * 0.5));
+	
+	var row = this.createRowRel("RelEllipticalArcTo", this.geoStepIndex++, x/gw, y/gh - hr * 0.5001, wr * 0.5 + x/gw, y/gh - hr, 0);
+	row.appendChild(this.createCellElem("D", w/h, "Width/Height*"+(wr/hr)));
+	this.geoSec.appendChild(row);
 };
 
 /**
@@ -49,12 +255,19 @@ mxXmlCanvas2D.prototype.createElement = function(name)
  */
 mxVsdxCanvas2D.prototype.moveTo = function(x, y)
 {
-	var elem = this.createElement('move');
-	elem.setAttribute('x', this.format(x));
-	elem.setAttribute('y', this.format(y));
-	this.root.appendChild(elem);
+	this.lastMoveToX = x;
+	this.lastMoveToY = y;
 	this.lastX = x;
-	this.lastY = y;
+	this.lastY = y;	
+
+	var geo = this.xmGeo;
+	var s = this.state;
+	x = (x - geo.x + s.dx) * s.scale;
+	y = (geo.height - y + geo.y - s.dy) * s.scale;
+	var h = geo.height * s.scale;
+	var w = geo.width * s.scale;
+
+	this.geoSec.appendChild(this.createRowRel("RelMoveTo", this.geoStepIndex++, x/w, y/h));
 };
 
 /**
@@ -69,12 +282,17 @@ mxVsdxCanvas2D.prototype.moveTo = function(x, y)
  */
 mxVsdxCanvas2D.prototype.lineTo = function(x, y)
 {
-	var elem = this.createElement('line');
-	elem.setAttribute('x', this.format(x));
-	elem.setAttribute('y', this.format(y));
-	this.root.appendChild(elem);
 	this.lastX = x;
-	this.lastY = y;
+	this.lastY = y;	
+
+	var geo = this.xmGeo;
+	var s = this.state;
+	x = (x - geo.x + s.dx) * s.scale;
+	y = (geo.height - y + geo.y - s.dy) * s.scale;
+	var h = geo.height * s.scale;
+	var w = geo.width * s.scale;
+
+	this.geoSec.appendChild(this.createRowRel("RelLineTo", this.geoStepIndex++, x/w, y/h));
 };
 
 /**
@@ -91,14 +309,27 @@ mxVsdxCanvas2D.prototype.lineTo = function(x, y)
  */
 mxVsdxCanvas2D.prototype.quadTo = function(x1, y1, x2, y2)
 {
-	var elem = this.createElement('quad');
-	elem.setAttribute('x1', this.format(x1));
-	elem.setAttribute('y1', this.format(y1));
-	elem.setAttribute('x2', this.format(x2));
-	elem.setAttribute('y2', this.format(y2));
-	this.root.appendChild(elem);
 	this.lastX = x2;
-	this.lastY = y2;
+	this.lastY = y2;	
+
+	var s = this.state;
+	var geo = this.xmGeo;
+
+	var h = geo.height * s.scale;
+	var w = geo.width * s.scale;
+
+	x1 = (x1 - geo.x + s.dx) * s.scale;
+	y1 = (geo.height - y1 + geo.y - s.dy) * s.scale;
+
+	x2 = (x2 - geo.x + s.dx) * s.scale;
+	y2 = (geo.height - y2 + geo.y - s.dy) * s.scale;
+
+	x1 = x1 / w;
+	y1 = y1 / h;
+	x2 = x2 / w;
+	y2 = y2 / h;
+
+	this.geoSec.appendChild(this.createRowRel("RelQuadBezTo", this.geoStepIndex++, x2, y2, x1, y1));
 };
 
 /**
@@ -117,16 +348,32 @@ mxVsdxCanvas2D.prototype.quadTo = function(x1, y1, x2, y2)
  */
 mxVsdxCanvas2D.prototype.curveTo = function(x1, y1, x2, y2, x3, y3)
 {
-	var elem = this.createElement('curve');
-	elem.setAttribute('x1', this.format(x1));
-	elem.setAttribute('y1', this.format(y1));
-	elem.setAttribute('x2', this.format(x2));
-	elem.setAttribute('y2', this.format(y2));
-	elem.setAttribute('x3', this.format(x3));
-	elem.setAttribute('y3', this.format(y3));
-	this.root.appendChild(elem);
 	this.lastX = x3;
-	this.lastY = y3;
+	this.lastY = y3;	
+
+	var s = this.state;
+	var geo = this.xmGeo;
+
+	var h = geo.height * s.scale;
+	var w = geo.width * s.scale;
+
+	x1 = (x1 - geo.x + s.dx) * s.scale;
+	y1 = (geo.height - y1 + geo.y - s.dy) * s.scale;
+
+	x2 = (x2 - geo.x + s.dx) * s.scale;
+	y2 = (geo.height - y2 + geo.y - s.dy) * s.scale;
+
+	x3 = (x3 - geo.x + s.dx) * s.scale;
+	y3 = (geo.height - y3 + geo.y - s.dy) * s.scale;
+
+	x1 = x1 / w;
+	y1 = y1 / h;
+	x2 = x2 / w;
+	y2 = y2 / h;
+	x3 = x3 / w;
+	y3 = y3 / h;
+
+	this.geoSec.appendChild(this.createRowRel("RelCubBezTo", this.geoStepIndex++, x3, y3, x1, y1, x2, y2));
 };
 
 /**
@@ -136,7 +383,143 @@ mxVsdxCanvas2D.prototype.curveTo = function(x1, y1, x2, y2, x3, y3)
  */
 mxVsdxCanvas2D.prototype.close = function()
 {
-	this.root.appendChild(this.createElement('close'));
+	//Closing with a line if last point != last MoveTo point
+	if (this.lastMoveToX != this.lastX || this.lastMoveToY != this.lastY)
+		this.lineTo(this.lastMoveToX, this.lastMoveToY);
+};
+
+/**
+ * Function: addForeignData
+ * 
+ * Add ForeignData to current shape using last image in the images array
+ */
+mxVsdxCanvas2D.prototype.addForeignData = function(type, index) 
+{
+	var foreignData = this.xmlDoc.createElement("ForeignData");
+	foreignData.setAttribute("ForeignType", "Bitmap");
+	
+	type = type.toUpperCase();
+	
+	if (type != "BMP")
+		foreignData.setAttribute("CompressionType", type);
+	
+	var rel = this.xmlDoc.createElement("Rel");
+	rel.setAttribute("r:id", "rId" + index);
+	
+	
+	foreignData.appendChild(rel);
+	this.shape.appendChild(foreignData);
+	this.shapeType = "Foreign";
+};
+
+/**
+ * Function: image
+ * 
+ * Add image to vsdx file as a media (Foreign Object)
+ */
+mxVsdxCanvas2D.prototype.image = function(x, y, w, h, src, aspect, flipH, flipV)
+{
+	//TODO image reusing, if the same image is used more than once, reuse it. Applicable for URLs specifically (but can also be applied to embedded ones)
+	var imgName = "image" + (this.images.length + 1) + "."; 
+	var type;
+	if (src.indexOf("data:") == 0)
+	{
+		var p = src.indexOf("base64,");
+		var base64 = src.substring(p + 7); //7 is the length of "base64,"
+		type = src.substring(11, p-1); //5 is the length of "data:image/"
+		imgName += type;
+		this.zip.file("visio/media/" + imgName, base64, {base64: true});
+	}
+	else if (window.XMLHttpRequest) //URL src, fetch it
+	{
+		src = this.converter.convert(src);
+		this.filesLoading++;
+		var that = this;
+		
+		var p = src.lastIndexOf(".");
+		type = src.substring(p+1);
+		imgName += type;
+		
+		//The old browsers binary workaround doesn't work with jszip and converting to base64 encoding doesn't work also
+		var xhr = new XMLHttpRequest();
+		xhr.open('GET', src, true);
+		xhr.responseType = 'arraybuffer';
+		xhr.onreadystatechange = function(e) 
+		{
+		    if (this.readyState == 4 && this.status == 200) {
+		    	that.zip.file("visio/media/" + imgName, this.response);
+		    	that.filesLoading--;
+		    }
+		};
+		xhr.send();
+	}
+
+	this.images.push(imgName);
+	
+	//TODO can a shape has more than one image?
+	this.shapeImg = {type: type, id: this.images.length};
+
+	//TODO support these!
+	aspect = (aspect != null) ? aspect : true;
+	flipH = (flipH != null) ? flipH : false;
+	flipV = (flipV != null) ? flipV : false;
+
+	var s = this.state;
+	w = w * s.scale;
+	h = h * s.scale;
+	
+	var geo = this.xmGeo;
+	x = (x - geo.x + s.dx) * s.scale;
+	y = (geo.height - y + geo.y - s.dy) * s.scale;
+
+	this.shape.appendChild(this.createCellElemScaled("ImgOffsetX", x));
+	this.shape.appendChild(this.createCellElemScaled("ImgOffsetY", y - h));
+	this.shape.appendChild(this.createCellElemScaled("ImgWidth", w));
+	this.shape.appendChild(this.createCellElemScaled("ImgHeight", h));
+	
+//	var s = this.state;
+//	x += s.dx;
+//	y += s.dy;
+//	
+//	if (s.alpha < 1 || s.fillAlpha < 1)
+//	{
+//		node.setAttribute('opacity', s.alpha * s.fillAlpha);
+//	}
+//	
+//	var tr = this.state.transform || '';
+//	
+//	if (flipH || flipV)
+//	{
+//		var sx = 1;
+//		var sy = 1;
+//		var dx = 0;
+//		var dy = 0;
+//		
+//		if (flipH)
+//		{
+//			sx = -1;
+//			dx = -w - 2 * x;
+//		}
+//		
+//		if (flipV)
+//		{
+//			sy = -1;
+//			dy = -h - 2 * y;
+//		}
+//		
+//		// Adds image tansformation to existing transform
+//		tr += 'scale(' + sx + ',' + sy + ')translate(' + (dx * s.scale) + ',' + (dy * s.scale) + ')';
+//	}
+//
+//	if (tr.length > 0)
+//	{
+//		node.setAttribute('transform', tr);
+//	}
+//	
+//	if (!this.pointerEvents)
+//	{
+//		node.setAttribute('pointer-events', 'none');
+//	}
 };
 
 /**
@@ -172,54 +555,88 @@ mxVsdxCanvas2D.prototype.text = function(x, y, w, h, str, align, valign, wrap, f
 		{
 			str = mxUtils.getOuterHtml(str);
 		}
+
+		//TODO support HTML text formatting and remaining attributes
+		var s = this.state;
+		var geo = this.xmGeo;
+
+		var strRect;
+//		if (h == 0 || w == 0)
+//		{
+//			strRect = mxUtils.getSizeForString(str);
+//		}
 		
-		var elem = this.createElement('text');
-		elem.setAttribute('x', this.format(x));
-		elem.setAttribute('y', this.format(y));
-		elem.setAttribute('w', this.format(w));
-		elem.setAttribute('h', this.format(h));
-		elem.setAttribute('str', str);
-		
-		if (align != null)
-		{
-			elem.setAttribute('align', align);
-		}
-		
-		if (valign != null)
-		{
-			elem.setAttribute('valign', valign);
-		}
-		
-		elem.setAttribute('wrap', (wrap) ? '1' : '0');
-		
-		if (format == null)
-		{
-			format = '';
-		}
-		
-		elem.setAttribute('format', format);
-		
-		if (overflow != null)
-		{
-			elem.setAttribute('overflow', overflow);
-		}
-		
-		if (clip != null)
-		{
-			elem.setAttribute('clip', (clip) ? '1' : '0');
-		}
-		
-		if (rotation != null)
-		{
-			elem.setAttribute('rotation', rotation);
-		}
+//		h = h > 0 ? h : strRect.height; 
+//		w = w > 0 ? w : strRect.width;
+		h = h > 0 ? h : geo.height; 
+		w = w > 0 ? w : geo.width;
+		w = w * s.scale;
+		h = h * s.scale;
 		
-		if (dir != null)
-		{
-			elem.setAttribute('dir', dir);
-		}
+		x = (x - geo.x + s.dx) * s.scale;
+		y = (geo.height - y + geo.y - s.dy) * s.scale;
+
+		var hw = w/2, hh = h/2;
+		this.shape.appendChild(this.createCellElemScaled("TxtPinX", x));
+		this.shape.appendChild(this.createCellElemScaled("TxtPinY", y));
+		this.shape.appendChild(this.createCellElemScaled("TxtWidth", w));
+		this.shape.appendChild(this.createCellElemScaled("TxtHeight", h));
+		this.shape.appendChild(this.createCellElemScaled("TxtLocPinX", hw));
+		this.shape.appendChild(this.createCellElemScaled("TxtLocPinY", hh));
+
+		this.shape.appendChild(this.createCellElemScaled("TxtAngle", rotation * Math.PI / 180));
 		
-		this.root.appendChild(elem);
+		var text = this.xmlDoc.createElement("Text");
+		text.textContent = str + "\n";
+		this.shape.appendChild(text);
+//		
+//		var elem = this.createElement('text');
+//		elem.setAttribute('x', this.format(x));
+//		elem.setAttribute('y', this.format(y));
+//		elem.setAttribute('w', this.format(w));
+//		elem.setAttribute('h', this.format(h));
+//		elem.setAttribute('str', str);
+//		
+//		if (align != null)
+//		{
+//			elem.setAttribute('align', align);
+//		}
+//		
+//		if (valign != null)
+//		{
+//			elem.setAttribute('valign', valign);
+//		}
+//		
+//		elem.setAttribute('wrap', (wrap) ? '1' : '0');
+//		
+//		if (format == null)
+//		{
+//			format = '';
+//		}
+//		
+//		elem.setAttribute('format', format);
+//		
+//		if (overflow != null)
+//		{
+//			elem.setAttribute('overflow', overflow);
+//		}
+//		
+//		if (clip != null)
+//		{
+//			elem.setAttribute('clip', (clip) ? '1' : '0');
+//		}
+//		
+//		if (rotation != null)
+//		{
+//			elem.setAttribute('rotation', rotation);
+//		}
+//		
+//		if (dir != null)
+//		{
+//			elem.setAttribute('dir', dir);
+//		}
+//		
+//		this.root.appendChild(elem);
 	}
 };
 
@@ -230,7 +647,8 @@ mxVsdxCanvas2D.prototype.text = function(x, y, w, h, str, align, valign, wrap, f
  */
 mxVsdxCanvas2D.prototype.stroke = function()
 {
-	this.root.appendChild(this.createElement('stroke'));
+	this.geoSec.appendChild(this.createCellElem("NoFill", "1"));
+	this.geoSec.appendChild(this.createCellElem("NoLine", "0"));
 };
 
 /**
@@ -240,7 +658,8 @@ mxVsdxCanvas2D.prototype.stroke = function()
  */
 mxVsdxCanvas2D.prototype.fill = function()
 {
-	this.root.appendChild(this.createElement('fill'));
+	this.geoSec.appendChild(this.createCellElem("NoFill", "0"));
+	this.geoSec.appendChild(this.createCellElem("NoLine", "1"));
 };
 
 /**
@@ -250,5 +669,6 @@ mxVsdxCanvas2D.prototype.fill = function()
  */
 mxVsdxCanvas2D.prototype.fillAndStroke = function()
 {
-	this.root.appendChild(this.createElement('fillstroke'));
+	this.geoSec.appendChild(this.createCellElem("NoFill", "0"));
+	this.geoSec.appendChild(this.createCellElem("NoLine", "0"));
 };

File diff suppressed because it is too large
+ 1 - 1
war/js/diagramly/vsdx/resources/[Content_Types].xml


File diff suppressed because it is too large
+ 3 - 4
war/js/diagramly/vsdx/resources/allConstants.json


File diff suppressed because it is too large
+ 1 - 1
war/js/diagramly/vsdx/resources/visio/document.xml


+ 0 - 2
war/js/diagramly/vsdx/resources/visio/pages/_rels/page1.xml.rels

@@ -1,2 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<Relationships xmlns='http://schemas.openxmlformats.org/package/2006/relationships' />

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


+ 2 - 2
war/js/mxgraph/Dialogs.js

@@ -20,8 +20,8 @@ function Dialog(editorUi, elt, w, h, modal, closable, onClose)
 	
 	var dh = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
 	
-	var left = Math.max(0, Math.round((document.body.scrollWidth - w) / 2));
-	var top = Math.max(0, Math.round((dh - h - editorUi.footerHeight) / 3));
+	var left = Math.max(1, Math.round((document.body.scrollWidth - w - 64) / 2));
+	var top = Math.max(1, Math.round((dh - h - editorUi.footerHeight) / 3));
 
 	// Keeps window size inside available space
 	if (!mxClient.IS_QUIRKS)

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


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