Browse Source

10.0.1 release

Gaudenz Alder 6 years ago
parent
commit
04350b0a48

+ 17 - 0
ChangeLog

@@ -1,3 +1,20 @@
+17-DEC-2018: 10.0.1
+
+- Fixes Google auth for OneDrive files
+
+17-DEC-2018: 10.0.0
+
+- Enables collaborative editing
+- Hides footer
+
+16-DEC-2018: 9.6.6
+
+- Adds darker stroke colors for metro palettes
+
+15-DEC-2018: 9.6.5
+
+- Fixes for collaborative editing (beta)
+
 14-DEC-2018: 9.6.4
 
 - Uses mxGraph 3.9.12

+ 1 - 1
VERSION

@@ -1 +1 @@
-9.6.4
+10.0.1

+ 17 - 14
src/main/java/com/mxgraph/online/OpenServlet.java

@@ -146,21 +146,24 @@ public class OpenServlet extends HttpServlet
 					xml = extractXmlFromPng(
 							upfile.getBytes(Utils.CHARSET_FOR_URL_ENCODING));
 				}
-				else if (ENABLE_GRAPHML_SUPPORT && upfile.matches(graphMlRegex))
+				else if (upfile != null)
 				{
-					// Creates a graph that contains a model but does not validate
-					// since that is not needed for the model and not allowed on GAE
-					mxGraph graph = new mxGraphHeadless();
-
-					mxGraphMlCodec.decode(mxXmlUtils.parseXml(upfile), graph);
-					xml = mxXmlUtils
-							.getXml(new mxCodec().encode(graph.getModel()));
-				}
-				else if (ENABLE_GLIFFY_SUPPORT && upfile.matches(gliffyRegex))
-				{
-					GliffyDiagramConverter converter = new GliffyDiagramConverter(
-							upfile);
-					xml = converter.getGraphXml();
+					if (ENABLE_GRAPHML_SUPPORT && upfile.matches(graphMlRegex))
+					{
+						// Creates a graph that contains a model but does not validate
+						// since that is not needed for the model and not allowed on GAE
+						mxGraph graph = new mxGraphHeadless();
+	
+						mxGraphMlCodec.decode(mxXmlUtils.parseXml(upfile), graph);
+						xml = mxXmlUtils
+								.getXml(new mxCodec().encode(graph.getModel()));
+					}
+					else if (ENABLE_GLIFFY_SUPPORT && upfile.matches(gliffyRegex))
+					{
+						GliffyDiagramConverter converter = new GliffyDiagramConverter(
+								upfile);
+						xml = converter.getGraphXml();
+					}
 				}
 
 				// Fallback to old data parameter

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

@@ -1,7 +1,7 @@
 CACHE MANIFEST
 
 # THIS FILE WAS GENERATED. DO NOT MODIFY!
-# 12/14/2018 01:24 PM
+# 12/17/2018 01:49 PM
 
 app.html
 index.html?offline=1

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


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


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


+ 10 - 65
src/main/webapp/js/diagramly/App.js

@@ -914,20 +914,7 @@ App.prototype.init = function()
 
 		initTrelloClient();
 	}
-	
-	// TEMPORARY REALTIME NOTICE FOR AFFECTED FILE TYPES
-	this.editor.addListener('fileLoaded', mxUtils.bind(this, function()
-	{
-		var file = this.getCurrentFile();
-		
-		if (file != null && (file.constructor == DriveFile ||
-			file.constructor == OneDriveFile ||
-			file.constructor == GitHubFile))
-		{
-			this.showFooterRealtimeNotice();
-		}
-	}));
-	
+
 	/**
 	 * Creates drive client with all required libraries are available.
 	 */
@@ -963,7 +950,7 @@ App.prototype.init = function()
 								if (td != null)
 								{
 									td.innerHTML = '<a href="https://support.draw.io/display/DO/2014/11/27/Switching+application+in+Google+Drive" ' +
-										'target="_blank" title="IMPORTANT NOTICE" >IMPORTANT NOTICE</a>';
+										'target="_blank" title="IMPORTANT NOTICE">IMPORTANT NOTICE</a>';
 								}
 							}
 						}));
@@ -1188,7 +1175,7 @@ App.prototype.checkLicense = function()
 				'&ts=' + new Date().getTime(),
 			mxUtils.bind(this, function(req)
 			{
-// NOTE: RESPONSE IS IGNORED TO SHOW TEMPORARY REALTIME NOTICE
+// NOTE: RESPONSE IS IGNORED SINCE FOOTER IS HIDDEN
 //				var registered = false;
 //				var exp = null;
 //				
@@ -1276,51 +1263,6 @@ App.prototype.handleLicense = function(lic, domain)
 	return expiry;
 };
 
-/**
- * Returns true if the current domain is for the new drive app.
- */
-App.prototype.showFooterRealtimeNotice = function()
-{
-	var footer = document.getElementById('geFooter');
-	
-	if (footer != null && this.footerAlert == null)
-	{
-		var alert = this.createRealtimeNotice();
-		alert.style.zIndex = '1';
-		alert.style.padding = '18px 0 14px 0';
-		alert.style.width = 'auto';
-		alert.style.top = '0px';
-		alert.style.left = '0px';
-		alert.style.right = '170px';
-
-		footer.appendChild(alert);
-		this.footerAlert = alert;
-	}
-};
-
-/**
- * Returns true if the current domain is for the new drive app.
- */
-App.prototype.createRealtimeNotice = function()
-{
-	var alert = document.createElement('a');
-	alert.className = 'geStatusAlert';
-	alert.style.display = 'block';
-	alert.style.position = 'absolute';
-	alert.style.overflow = 'hidden';
-	alert.style.cursor = 'pointer';
-	alert.style.bottom = '0';
-	alert.style.textAlign = 'center';
-	alert.style.textDecoration = 'none';
-	alert.style.fontWeight = 'bold';
-	
-	alert.setAttribute('href', 'https://desk.draw.io/support/solutions/articles/16000087215');
-	alert.setAttribute('target', '_blank');
-	mxUtils.write(alert, mxResources.get('collaborativeEditingNotice'));
-	
-	return alert;
-};
-
 /**
  * 
  */
@@ -1346,8 +1288,9 @@ App.prototype.updateActionStates = function()
 	EditorUi.prototype.updateActionStates.apply(this, arguments);
 
 	var file = this.getCurrentFile();
-	this.actions.get('revisionHistory').setEnabled(file != null && ((file.constructor == DriveFile &&
-			file.isEditable()) || file.constructor == DropboxFile));
+	this.actions.get('revisionHistory').setEnabled(file != null &&
+		((file.constructor == DriveFile && file.isEditable()) ||
+		file.constructor == DropboxFile));
 };
 
 /**
@@ -1357,7 +1300,8 @@ App.prototype.updateDraft = function()
 {
 	if (isLocalStorage && localStorage != null)
 	{
-		localStorage.setItem('.draft', JSON.stringify({modified: new Date().getTime(), data: this.getFileData()}));
+		localStorage.setItem('.draft', JSON.stringify({modified:
+			new Date().getTime(), data: this.getFileData()}));
 	}
 };
 
@@ -4106,7 +4050,8 @@ App.prototype.updateButtonContainer = function()
 		var file = this.getCurrentFile();
 		
 		// Synchronize
-		if (file != null && DrawioFile.SYNC != 'none')
+		if (file != null && (DrawioFile.SYNC == 'manual' ||
+			DrawioFile.SYNC == 'auto'))
 		{
 			var visible = ((DrawioFile.SYNC == 'manual' ||
 				(file.sync != null && !file.sync.enabled &&

+ 159 - 160
src/main/webapp/js/diagramly/DiffSync.js

@@ -18,38 +18,15 @@ EditorUi.DIFF_REMOVE = 'r';
 EditorUi.DIFF_UPDATE = 'u';
 
 /**
- * Removes all labels, user objects and styles from the given node in-place.
+ * Shared codec.
  */
-EditorUi.prototype.viewStateWhitelist = ['background', 'backgroundImage', 'foldingEnabled',
-	'pageScale', 'mathEnabled', 'shadowVisible', 'pageFormat'];
+EditorUi.prototype.codec = new mxCodec();
 
 /**
- * Removes any values, styles and geometries from the given XML node.
+ * Removes all labels, user objects and styles from the given node in-place.
  */
-EditorUi.prototype.anonymizeNode = function(node)
-{
-	var nodes = node.getElementsByTagName('mxCell');
-	
-	for (var i = 0; i < nodes.length; i++)
-	{
-		nodes[i].removeAttribute('value');
-		nodes[i].removeAttribute('style');
-		
-		if (nodes[i].parentNode != null && nodes[i].parentNode.nodeName != 'root' &&
-			nodes[i].parentNode.parentNode != null)
-		{
-			nodes[i].setAttribute('id', nodes[i].parentNode.getAttribute('id'));
-			nodes[i].parentNode.parentNode.replaceChild(nodes[i], nodes[i].parentNode);
-		}
-	}
-	
-	var geos = node.getElementsByTagName('mxGeometry');
-	
-	while (geos.length > 0)
-	{
-		geos[0].parentNode.removeChild(geos[0]);
-	}
-};
+EditorUi.prototype.viewStateWhitelist = ['background', 'backgroundImage', 'foldingEnabled',
+	'pageScale', 'mathEnabled', 'shadowVisible', 'pageFormat'];
 
 /**
  * Removes all labels, user objects and styles from the given node in-place.
@@ -105,19 +82,58 @@ EditorUi.prototype.patchPages = function(pages, diff, markPages, shadow)
 		}
 	}
 	
-	var addPage = mxUtils.bind(this, function(page, pageDiff)
+	// Restores existing order
+	var prev = '';
+	
+	for (var i = 0; i < pages.length; i++)
+	{
+		var pageId = pages[i].getId();
+		
+		if (moved[prev] == null && !removed[pages[i].getId()] &&
+			(diff[EditorUi.DIFF_UPDATE] == null ||
+			diff[EditorUi.DIFF_UPDATE][pageId] == null ||
+			diff[EditorUi.DIFF_UPDATE][pageId].previous == null))
+		{
+			moved[prev] = pageId;
+		}
+		
+		prev = pageId;
+	}
+	
+	var addPage = mxUtils.bind(this, function(page)
 	{
 		var id = (page != null) ? page.getId() : '';
 		
 		if (page != null)
 		{
 			newPages.push(page);
-			
+			var pageDiff = (diff[EditorUi.DIFF_UPDATE] != null) ?
+					diff[EditorUi.DIFF_UPDATE][id] : null;
+
 			if (pageDiff != null)
 			{
-				this.patchPage(page, pageDiff.cells, markPages,
-					shadowLookup[page.getId()]);
-				this.patchViewState(page, pageDiff.view);
+				this.updatePageRoot(page);
+				
+				if (pageDiff.name != null)
+				{
+					page.setName(pageDiff.name);
+				}
+
+				if (pageDiff.view != null)
+				{
+					this.patchViewState(page, pageDiff.view);
+				}
+				
+				if (pageDiff.cells != null)
+				{
+					this.patchPage(page, pageDiff.cells, shadowLookup[page.getId()]);
+				}
+				
+				if (markPages && (pageDiff.cells != null ||
+					pageDiff.view != null))
+				{
+					page.needsUpdate = true;
+				}
 			}
 		}
 		
@@ -167,25 +183,6 @@ EditorUi.prototype.patchPages = function(pages, diff, markPages, shadow)
 	
 	addPage();
 
-	for (var i = 0; i < pages.length; i++)
-	{
-		if (!removed[pages[i].getId()])
-		{
-			var pageDiff = (diff[EditorUi.DIFF_UPDATE] != null) ?
-				diff[EditorUi.DIFF_UPDATE][pages[i].getId()] : null;
-			
-			if (pageDiff != null && pageDiff.name != null)
-			{
-				pages[i].setName(pageDiff.name);
-			}
-			
-			if (pageDiff == null || pageDiff.previous == null)
-			{
-				addPage(pages[i], pageDiff);
-			}
-		}
-	}
-	
 	// Handles orphaned moved pages
 	for (var id in moved)
 	{
@@ -220,7 +217,7 @@ EditorUi.prototype.patchViewState = function(page, diff)
 		{
 			page.viewState = this.editor.graph.getViewState();
 		}
-		
+
 		for (var key in diff)
 		{
 			page.viewState[key] = JSON.parse(diff[key]);
@@ -303,99 +300,89 @@ EditorUi.prototype.createParentLookup = function(model, diff)
 /**
  * Removes all labels, user objects and styles from the given node in-place.
  */
-EditorUi.prototype.patchPage = function(page, diff, markPage, shadowPage)
+EditorUi.prototype.patchPage = function(page, diff, shadowPage)
 {
-	this.updatePageRoot(page);
+	var model = (page == this.currentPage) ? this.editor.graph.model : new mxGraphModel(page.root);
+	var shadowModel = (shadowPage != null) ? new mxGraphModel(shadowPage.root) : null;
+	var parentLookup = this.createParentLookup(model, diff);
 
-	if (diff != null)
+	model.beginUpdate();
+	try
 	{
-		var model = (page == this.currentPage) ? this.editor.graph.model : new mxGraphModel(page.root);
-		var shadowModel = (shadowPage != null) ? new mxGraphModel(shadowPage.root) : null;
-		var parentLookup = this.createParentLookup(model, diff);
-
-		model.beginUpdate();
-		try
+		// Handles new root cells
+		var temp = parentLookup[''];
+		var cellDiff = (temp != null && temp.inserted != null) ? temp.inserted[''] : null;
+		var root = null;
+		
+		if (cellDiff != null)
 		{
-			// Handles new root cells
-			var temp = parentLookup[''];
-			var cellDiff = (temp != null && temp.inserted != null) ? temp.inserted[''] : null;
-			var root = null;
-			
-			if (cellDiff != null)
-			{
-				root = this.getCellForJson(cellDiff);
-			}
-			
-			// Handles cells becoming root (very unlikely but possible)
-			if (root == null)
-			{
-				var id = (temp != null && temp.moved != null) ? temp.moved[''] : null;
-				
-				if (id != null)
-				{
-					root = model.getCell(id);
-				}
-			}
+			root = this.getCellForJson(cellDiff);
+		}
+		
+		// Handles cells becoming root (very unlikely but possible)
+		if (root == null)
+		{
+			var id = (temp != null && temp.moved != null) ? temp.moved[''] : null;
 			
-			if (root != null)
+			if (id != null)
 			{
-				model.setRoot(root);
-				page.root = root;
+				root = model.getCell(id);
 			}
-			
-			// Applies structural changes to the cell hierarchy
-			this.patchCellRecursive(page, model, model.root, parentLookup, diff, shadowModel);
+		}
+		
+		if (root != null)
+		{
+			model.setRoot(root);
+			page.root = root;
+		}
 
-			// Removes cells
-			if (diff[EditorUi.DIFF_REMOVE] != null)
+		// Removes cells
+		if (diff[EditorUi.DIFF_REMOVE] != null)
+		{
+			for (var i = 0; i < diff[EditorUi.DIFF_REMOVE].length; i++)
 			{
-				for (var i = 0; i < diff[EditorUi.DIFF_REMOVE].length; i++)
+				var cell = model.getCell(diff[EditorUi.DIFF_REMOVE][i]);
+				
+				if (cell != null)
 				{
-					var cell = model.getCell(diff[EditorUi.DIFF_REMOVE][i]);
-					
-					if (cell != null)
-					{
-						model.remove(cell);
-					}
+					model.remove(cell);
 				}
 			}
+		}
+		
+		// Patches cell structure
+		this.patchCellRecursive(page, model, model.root, parentLookup, diff, shadowModel);
 
-			// Applies patches and changes terminals
-			if (diff[EditorUi.DIFF_UPDATE] != null)
+		// Applies patches and changes terminals after all cells are inserted
+		if (diff[EditorUi.DIFF_UPDATE] != null)
+		{
+			for (var id in diff[EditorUi.DIFF_UPDATE])
 			{
-				for (var id in diff[EditorUi.DIFF_UPDATE])
-				{
-					this.patchCell(model, model.getCell(id), diff[EditorUi.DIFF_UPDATE][id],
-						(shadowModel != null) ? shadowModel.getCell(id) : null);
-				}
+				this.patchCell(model, model.getCell(id), diff[EditorUi.DIFF_UPDATE][id],
+					(shadowModel != null) ? shadowModel.getCell(id) : null);
 			}
+		}
 
-			// Sets terminals for inserted cells
-			if (diff[EditorUi.DIFF_INSERT] != null)
+		// Sets terminals for inserted cells after all cells are inserted
+		if (diff[EditorUi.DIFF_INSERT] != null)
+		{
+			for (var i = 0; i < diff[EditorUi.DIFF_INSERT].length; i++)
 			{
-				for (var i = 0; i < diff[EditorUi.DIFF_INSERT].length; i++)
+				var cellDiff = diff[EditorUi.DIFF_INSERT][i];
+				var cell = model.getCell(cellDiff.id);
+				
+				if (cell != null)
 				{
-					var cellDiff = diff[EditorUi.DIFF_INSERT][i];
-					var cell = model.getCell(cellDiff.id);
-					
-					if (cell != null)
-					{
-						model.setTerminal(cell, model.getCell(cellDiff.source), true);
-						model.setTerminal(cell, model.getCell(cellDiff.target), false);
-					}
+					model.setTerminal(cell, model.getCell(cellDiff.source), true);
+					model.setTerminal(cell, model.getCell(cellDiff.target), false);
 				}
 			}
-			
-			if (markPage)
-			{
-				page.needsUpdate = true;
-			}
-		}
-		finally
-		{
-			model.endUpdate();
 		}
 	}
+	finally
+	{
+		model.endUpdate();
+	}
 };
 
 /**
@@ -406,66 +393,74 @@ EditorUi.prototype.patchCellRecursive = function(page, model, cell, parentLookup
 	var temp = parentLookup[cell.getId()];
 	var inserted = (temp != null && temp.inserted != null) ? temp.inserted : {};
 	var moved = (temp != null && temp.moved != null) ? temp.moved : {};
-	var children = (cell.children != null) ? cell.children.slice() : [];
-	var processed = {};
 	var index = 0;
 	
-	var addCell = mxUtils.bind(this, function(child, doInsert)
+	// Restores existing order
+	var childCount = model.getChildCount(cell);
+	var prev = '';
+	
+	for (var i = 0; i < childCount; i++)
+	{
+		var cellId = model.getChildAt(cell, i).getId();
+		
+		if (moved[prev] == null &&
+			(diff[EditorUi.DIFF_UPDATE] == null ||
+			diff[EditorUi.DIFF_UPDATE][cellId] == null ||
+			(diff[EditorUi.DIFF_UPDATE][cellId].previous == null &&
+			diff[EditorUi.DIFF_UPDATE][cellId].parent == null)))
+		{
+			moved[prev] = cellId;
+		}
+		
+		prev = cellId;
+	}
+
+	var addCell = mxUtils.bind(this, function(child)
 	{
 		var id = (child != null) ? child.getId() : '';
 		
 		if (child != null)
 		{
-			if (doInsert)
+			if (model.getChildAt(cell, index) != child)
 			{
 				model.add(cell, child, index);
 			}
 
-			index++;
 			this.patchCellRecursive(page, model, child,
 				parentLookup, diff, shadowModel);
+			index++;
 		}
 
 		var mov = moved[id];
 		
 		if (mov != null)
 		{
-			addCell(model.getCell(mov), true);
-			processed[mov] = true;
 			delete moved[id];
+			addCell(model.getCell(mov));
 		}
 		
 		var ins = inserted[id];
 		
 		if (ins != null)
 		{
-			addCell(this.getCellForJson(ins), true);
 			delete inserted[id];
+			addCell(this.getCellForJson(ins));
 		}
 	});
 	
 	addCell();
-	
-	for (var i = 0; i < children.length; i++)
-	{
-		if (processed[children[i].getId()] == null &&
-			moved[children[i].getId()] == null)
-		{
-			addCell(children[i], false);
-		}
-	}
 
 	// Handles orphaned moved pages
 	for (var id in moved)
 	{
-		addCell(model.getCell(moved[id]), true);
+		addCell(model.getCell(moved[id]));
 		delete moved[id];
 	}
 
 	// Handles orphaned inserted pages
 	for (var id in inserted)
 	{
-		addCell(this.getCellForJson(inserted[id]), true);
+		addCell(this.getCellForJson(inserted[id]));
 		delete inserted[id];
 	}
 };
@@ -500,8 +495,6 @@ EditorUi.prototype.patchCell = function(model, cell, diff, shadowCell)
 	
 	if (cell != null && diff != null)
 	{
-		var codec = new mxCodec();
-		
 		// Last write wins for value and style
 		if (diff.value != null)
 		{
@@ -542,7 +535,8 @@ EditorUi.prototype.patchCell = function(model, cell, diff, shadowCell)
 		
 		if (diff.geometry != null)
 		{
-			model.setGeometry(cell, codec.decode(mxUtils.parseXml(diff.geometry).documentElement));
+			model.setGeometry(cell, this.codec.decode(mxUtils.parseXml(
+				diff.geometry).documentElement));
 		}
 		
 		if (diff.source != null)
@@ -589,6 +583,7 @@ EditorUi.prototype.getPagesForNode = function(node)
  */
 EditorUi.prototype.diffPages = function(oldPages, newPages)
 {
+	var result = {};
 	var lookup = {};
 	var diff = {};
 	var prev = null;
@@ -663,9 +658,7 @@ EditorUi.prototype.diffPages = function(oldPages, newPages)
 			previous: (newPage.prev != null) ?
 			newPage.prev.getId() : ''});
 	}
-	
-	var result = {};
-	
+
 	if (removed.length > 0)
 	{
 		result[EditorUi.DIFF_REMOVE] = removed;
@@ -831,8 +824,8 @@ EditorUi.prototype.diffViewState = function(oldPage, newPage)
  */
 EditorUi.prototype.getCellForJson = function(json)
 {
-	var codec = new mxCodec();
-	var geometry = (json.geometry != null) ? codec.decode(mxUtils.parseXml(json.geometry).documentElement) : null;
+	var geometry = (json.geometry != null) ? this.codec.decode(
+		mxUtils.parseXml(json.geometry).documentElement) : null;
 	var value = json.value;
 	
 	if (json.xmlValue != null)
@@ -844,8 +837,8 @@ EditorUi.prototype.getCellForJson = function(json)
 	cell.connectable = json.connectable != 0;
 	cell.collapsed = json.collapsed == 1;
 	cell.visible = json.visible != 0;
-	cell.vertex = json.vertex;
-	cell.edge = json.edge;
+	cell.vertex = json.vertex == 1;
+	cell.edge = json.edge == 1;
 	cell.id = json.id;
 	
 	return cell;
@@ -856,10 +849,18 @@ EditorUi.prototype.getCellForJson = function(json)
  */
 EditorUi.prototype.getJsonForCell = function(cell, previous)
 {
-	var result = {id: cell.getId(), vertex: (cell.vertex) ? 1 : 0,
-		edge: (cell.edge) ? 1 : 0};
-	var codec = new mxCodec();
+	var result = {id: cell.getId()};
+	
+	if (cell.vertex)
+	{
+		result.vertex = 1;
+	}
 
+	if (cell.edge)
+	{
+		result.edge = 1;
+	}
+	
 	if (cell.parent != null)
 	{
 		result['parent'] = cell.parent.getId();
@@ -887,7 +888,7 @@ EditorUi.prototype.getJsonForCell = function(cell, previous)
 
 	if (cell.geometry != null)
 	{
-		result['geometry'] = mxUtils.getXml(codec.encode(cell.geometry));
+		result['geometry'] = mxUtils.getXml(this.codec.encode(cell.geometry));
 	}
 
 	if (!cell.connectable)
@@ -995,12 +996,10 @@ EditorUi.prototype.diffCell = function(oldCell, newCell)
 		diff['connectable'] = (newCell.connectable) ? 1 : 0;
 	}
 	
-	var codec = new mxCodec();
-	
 	// FIXME: Proto only needed because source.geometry has no constructor (wrong type?)
 	if (!this.isObjectEqual(oldCell.geometry, newCell.geometry, new mxGeometry()))
 	{
-		diff['geometry'] = mxUtils.getXml(codec.encode(newCell.geometry));
+		diff['geometry'] = mxUtils.getXml(this.codec.encode(newCell.geometry));
 	}
 	
 	return diff;

+ 114 - 46
src/main/webapp/js/diagramly/DrawioFile.js

@@ -30,7 +30,7 @@ DrawioFile = function(ui, data)
  * - manual: manual sync
  * - auto: automatic sync
  */
-DrawioFile.SYNC = urlParams['sync'] || 'none';
+DrawioFile.SYNC = urlParams['sync'] || 'auto';
 
 /**
  * Specifies if last write wins should be used for values and styles.
@@ -228,12 +228,13 @@ DrawioFile.prototype.mergeFile = function(file, success, error)
 		var checksum = this.ui.getHashValueForPages(patchedShadow);
 		var current = this.ui.getHashValueForPages(this.shadowPages);
 		
-		EditorUi.debug('File.mergeFile', [this], 'patch', patch, 'checksum', current, checksum);
+		EditorUi.debug('File.mergeFile', [this], 'patch', patch, 'checksum',
+			current == checksum, checksum);
 		
 		if (checksum != current)
 		{
 			this.stats.checksumErrors++;
-			this.checksumError(error);
+			this.checksumError(error, [patch]);
 		}
 		else
 		{
@@ -265,25 +266,30 @@ DrawioFile.prototype.mergeFile = function(file, success, error)
 			error(e);
 		}
 
-		// Beta test error reports
-		var user = this.getCurrentUser();
-		var uid = (user != null) ? this.ui.hashValue(user.id) : 'no user';
-
-		EditorUi.sendReport('Error in mergeFile:\n' +
-			new Date().toISOString() + '\n' +
-			'User=' + uid + '\n' +
-			'File=' + this.getId() + '\n' +
-			'Mode=' + this.getMode() + '\n' +
-			'Size=' + this.getSize() + '\n' +
-			'Sync=' + DrawioFile.SYNC + '\n' +
-			'Stack:\n' + e.stack);
+		try
+		{
+			var user = this.getCurrentUser();
+			var uid = (user != null) ? this.ui.hashValue(user.id) : 'unknown';
+	
+			EditorUi.sendReport('Error in mergeFile ' + new Date().toISOString() + ':\n\n' +
+				'File=' + this.getId() + '\n' +
+				((this.sync != null) ? ('Client=' + this.sync.clientId + '\n') : '') +
+				'User=' + uid + '\n' +
+				'Size=' + this.getSize() + '\n' +
+				'Sync=' + DrawioFile.SYNC + '\n\n' +
+				'Stack:\n' + e.stack);
+		}
+		catch (e2)
+		{
+			// ignore
+		}
 	}
 };
 
 /**
  * Adds the listener for automatically saving the diagram for local changes.
  */
-DrawioFile.prototype.checksumError = function(error)
+DrawioFile.prototype.checksumError = function(error, patches)
 {
 	this.inConflictState = true;
 	this.invalidChecksum = true;
@@ -293,11 +299,53 @@ DrawioFile.prototype.checksumError = function(error)
 	{
 		this.sync.updateOnlineState();
 	}
-	
+
 	if (error != null)
 	{
 		error();
 	}
+	
+	try
+	{
+		if (patches != null)
+		{
+			for (var i = 0; i < patches.length; i++)
+			{
+				this.ui.anonymizePatch(patches[i]);
+			}
+		}
+
+		var enc = new mxCodec(mxUtils.createXmlDocument());
+		var file = enc.document.createElement('mxfile');
+		
+		if (this.shadowPages != null)
+		{
+			for (var i = 0; i < this.shadowPages.length; i++)
+			{
+				var temp = this.ui.anonymizeNode(enc.encode(
+					new mxGraphModel(this.shadowPages[i].root)));
+				temp.setAttribute('id', this.shadowPages[i].getId());
+				file.appendChild(temp);
+			}
+		}
+
+		var user = this.getCurrentUser();
+		var uid = (user != null) ? this.ui.hashValue(user.id) : 'unknown';
+
+		EditorUi.sendReport('Checksum Error ' + new Date().toISOString() + ':\n\n' +
+			'File=' + this.getId() + '\n' +
+			((this.sync != null) ? ('Client=' + this.sync.clientId + '\n') : '') +
+			'User=' + uid + '\n' +
+			'Size=' + this.getSize() + '\n' +
+			'Sync=' + DrawioFile.SYNC + '\n\n' +
+			'Data:\n' + mxUtils.getPrettyXml(file) + '\n' +
+			'Patches:\n' + JSON.stringify(patches, null, 2));
+	}
+	catch (e)
+	{
+		// ignore
+		console.error(e);
+	}
 };
 
 /**
@@ -427,7 +475,8 @@ DrawioFile.prototype.patch = function(patches, shadow)
 		var prev = this.changeListenerEnabled;
 		this.changeListenerEnabled = false;
 		
-		// Math changes require special handling
+		// Folding and math change require special handling
+		var fold = graph.foldingEnabled;
 		var math = graph.mathEnabled;
 		
 		// Updates text editor if cell changes during validation
@@ -475,25 +524,37 @@ DrawioFile.prototype.patch = function(patches, shadow)
 			undoMgr.indexOfNextAdd = nextAdd;
 			undoMgr.fireEvent(new mxEventObject(mxEvent.CLEAR));
 			
-			// Updates the graph and background
-			if (math != graph.mathEnabled)
-			{
-				this.ui.editor.updateGraphComponents();
-				graph.refresh();
-			}
-			else
+			if (this.ui.currentPage == null || this.ui.currentPage.needsUpdate)
 			{
-				graph.view.validate();
+				// Updates the graph and background
+				if (math != graph.mathEnabled)
+				{
+					this.ui.editor.updateGraphComponents();
+					graph.refresh();
+				}
+				else
+				{
+					if (fold != graph.foldingEnabled)
+					{
+						graph.view.revalidate();
+					}
+					else
+					{
+						
+						graph.view.validate();
+					}
+					
+					graph.sizeDidChange();
+				}
+				
+				// Updates view state in format panel if nothing is selected
+				if (this.ui.format != null && graph.isSelectionEmpty())
+				{
+					this.ui.format.refresh();
+				}
 			}
 			
-			graph.sizeDidChange();
 			this.ui.updateTabContainer();
-			
-			// Updates view state in format panel if nothing is selected
-			if (this.ui.format != null && graph.isSelectionEmpty())
-			{
-				this.ui.format.refresh();
-			}
 		}
 	}
 };
@@ -950,7 +1011,7 @@ DrawioFile.prototype.addUnsavedStatus = function(err)
 {
 	if (!this.inConflictState && this.ui.statusContainer != null)
 	{
-		if (err instanceof Error && err.message != null)
+		if (err instanceof Error && err.message != null && err.message != '')
 		{
 			this.ui.editor.setStatus('<div class="geStatusAlert" style="overflow:hidden;">' +
 				mxUtils.htmlEntities(mxResources.get('unsavedChanges')) +
@@ -980,7 +1041,7 @@ DrawioFile.prototype.addUnsavedStatus = function(err)
 			
 			this.ui.editor.setStatus('<div class="geStatusAlert" style="cursor:pointer;overflow:hidden;">' +
 				mxUtils.htmlEntities(mxResources.get('unsavedChangesClickHereToSave')) +
-				((msg != null) ? ' (' + mxUtils.htmlEntities(msg) + ')' : '') + '</div>');
+				((msg != null && msg != '') ? ' (' + mxUtils.htmlEntities(msg) + ')' : '') + '</div>');
 			
 			// Installs click handler for saving
 			var links = this.ui.statusContainer.getElementsByTagName('div');
@@ -1556,22 +1617,29 @@ DrawioFile.prototype.removeListeners = function()
  */
 DrawioFile.prototype.destroy = function()
 {
-	// Beta test error reports
 	try
 	{
-		if (this.reportEnabled && DrawioFile.SYNC != 'none')
+		if (!this.ui.isOffline() && this.reportEnabled &&
+			(DrawioFile.SYNC == 'auto' ||
+			DrawioFile.SYNC == 'manual'))
 		{
 			var user = this.getCurrentUser();
-			var uid = (user != null) ? this.ui.hashValue(user.id) : 'no user';
+			var uid = (user != null) ? this.ui.hashValue(user.id) : 'unknown';
+			this.stats.end = new Date().toISOString();
 			
-			EditorUi.sendReport('Sync Stats ' +
-				new Date().toISOString() + ':\n\n' +
-				'File=' + this.getId() + '\n' +
-				'User=' + uid + '\n' +
-				((this.sync != null) ? (this.sync.clientId + '\n') : '') +
-				'Size=' + this.getSize() + '\n' +
-				'Sync=' + DrawioFile.SYNC + '\n\n' +
-				'Stats=' + JSON.stringify(this.stats, null, 4));
+			if (this.stats.start != null)
+			{
+				this.stats.uptime = Math.round((new Date().getTime() -
+					new Date(this.stats.start).getTime()) / 1000);
+			}
+		
+			EditorUi.logEvent({category: 'RT-END-' + DrawioFile.SYNC,
+				action: 'file-' + this.getId() +
+				'-mode-' + this.getMode() +
+				'-size-' + this.getSize() +
+				'-user-' + uid +
+				((this.sync != null) ? ('-client-' + this.sync.clientId ) : ''),
+				label: this.stats});
 		}
 	}
 	catch (e)

+ 33 - 24
src/main/webapp/js/diagramly/DrawioFileSync.js

@@ -62,7 +62,7 @@ DrawioFileSync.prototype.catchupRetryCount = 0;
 /**
  * Specifies if descriptor change events should be ignored.
  */
-DrawioFileSync.prototype.maxCatchupRetries = 12;
+DrawioFileSync.prototype.maxCatchupRetries = 15;
 
 /**
  * Specifies if descriptor change events should be ignored.
@@ -72,7 +72,7 @@ DrawioFileSync.prototype.maxCacheReadyRetries = 2;
 /**
  * Specifies if descriptor change events should be ignored.
  */
-DrawioFileSync.prototype.cacheReadyDelay = 400;
+DrawioFileSync.prototype.cacheReadyDelay = 500;
 
 /**
  * Adds all listeners.
@@ -121,9 +121,16 @@ DrawioFileSync.prototype.start = function()
 				
 				if (!this.ui.isOffline())
 				{
-					EditorUi.logEvent({category: 'DiffSync-Start', action: DrawioFile.SYNC,
-						label: 'id.' + this.file.getId() + '.mode.' + this.file.getMode() +
-						'.size.' + this.file.getSize() + '.channel.' + this.channelId});
+					var user = this.file.getCurrentUser();
+					var uid = (user != null) ? this.ui.hashValue(user.id) : 'unknown';
+				
+					EditorUi.logEvent({category: 'RT-START-' + DrawioFile.SYNC,
+						action: 'file-' + this.file.getId() +
+						'-mode-' + this.file.getMode() +
+						'-size-' +this.file.getSize() +
+						'-user-' + uid +
+						'-client-' + this.clientId,
+						label: this.file.stats.start});
 				}
 			}
 			catch (e)
@@ -344,10 +351,7 @@ DrawioFileSync.prototype.resetUpdateStatusThread = function()
 	{
 		this.updateStatusThread = window.setInterval(mxUtils.bind(this, function()
 		{
-			this.ui.drive.checkToken(mxUtils.bind(this, function()
-			{
-				this.updateStatus();
-			}));
+			this.updateStatus();
 		}), this.updateStatusInterval);
 	}
 };
@@ -748,13 +752,14 @@ DrawioFileSync.prototype.merge = function(patches, checksum, etag, success, erro
 		
 		var current = (checksum != null) ? this.ui.getHashValueForPages(this.file.shadowPages) : null;
 		EditorUi.debug('Sync.merge', [this], 'from', this.file.getCurrentEtag(), 'to', etag,
-			'attempt', this.catchupRetryCount, 'diffs', patches, 'checksum', checksum);
+			'attempt', this.catchupRetryCount, 'diffs', patches, 'checksum',
+			checksum == current, checksum);
 
 		// Compares the checksum
 		if (checksum != null && checksum != current)
 		{
 			this.file.stats.mergeChecksumErrors++;
-			this.file.checksumError(error);
+			this.file.checksumError(error, patches);
 		}
 		else
 		{
@@ -784,19 +789,23 @@ DrawioFileSync.prototype.merge = function(patches, checksum, etag, success, erro
 			error(e);
 		}
 		
-		// Beta test error reports
-		var user = this.file.getCurrentUser();
-		var uid = (user != null) ? this.ui.hashValue(user.id) : 'no user';
-		
-		EditorUi.sendReport('Error in merge:\n' +
-			new Date().toISOString() + '\n' +
-			'Client=' + this.clientId + '\n' +
-			'User=' + uid + '\n' +
-			'File=' + this.file.getId() + '\n' +
-			'Mode=' + this.file.getMode() + '\n' +
-			'Size=' + this.file.getSize() + '\n' +
-			'Sync=' + DrawioFile.SYNC + '\n' +
-			'Stack:\n' + e.stack);
+		try
+		{
+			var user = this.file.getCurrentUser();
+			var uid = (user != null) ? this.ui.hashValue(user.id) : 'unknown';
+	
+			EditorUi.sendReport('Error in merge ' + new Date().toISOString() + ':\n\n' +
+				'File=' + this.file.getId() + '\n' +
+				'Client=' + this.clientId + '\n' +
+				'User=' + uid + '\n' +
+				'Size=' + this.file.getSize() + '\n' +
+				'Sync=' + DrawioFile.SYNC + '\n\n' +
+				'Stack:\n' + e.stack);
+		}
+		catch (e2)
+		{
+			// ignore
+		}
 	}
 };
 

+ 8 - 8
src/main/webapp/js/diagramly/Editor.js

@@ -1559,14 +1559,14 @@
 			{fill: '#dae8fc', stroke: '#6c8ebf'}, {fill: '#d5e8d4', stroke: '#82b366'},
 			{fill: '#ffe6cc', stroke: '#d79b00'}, {fill: '#fff2cc', stroke: '#d6b656'},
 			{fill: '#f8cecc', stroke: '#b85450'}, {fill: '#e1d5e7', stroke: '#9673a6'}],
-			[{fill: '#60a917', stroke: '#60a917', font: '#ffffff'}, {fill: '#008a00', stroke: '#008a00', font: '#ffffff'},
-			{fill: '#1ba1e2', stroke: '#1ba1e2', font: '#ffffff'}, {fill: '#0050ef', stroke: '#0050ef', font: '#ffffff'},
-			{fill: '#6a00ff', stroke: '#6a00ff', font: '#ffffff'}, {fill: '#aa00ff', stroke: '#aa00ff', font: '#ffffff'},
-			{fill: '#d80073', stroke: '#d80073', font: '#ffffff'}, {fill: '#a20025', stroke: '#a20025', font: '#ffffff'}],
-			[{fill: '#e51400', stroke: '#e51400', font: '#ffffff'}, {fill: '#fa6800', stroke: '#fa6800', font: '#ffffff'},
-			{fill: '#f0a30a', stroke: '#f0a30a', font: '#ffffff'}, {fill: '#e3c800', stroke: '#e3c800', font: '#ffffff'},
-			{fill: '#6d8764', stroke: '#6d8764', font: '#ffffff'}, {fill: '#647687', stroke: '#647687', font: '#ffffff'},
-			{fill: '#76608a', stroke: '#76608a', font: '#ffffff'}, {fill: '#a0522d', stroke: '#a0522d', font: '#ffffff'}],
+			[{fill: '#60a917', stroke: '#2D7600', font: '#ffffff'}, {fill: '#008a00', stroke: '#005700', font: '#ffffff'},
+			{fill: '#1ba1e2', stroke: '#006EAF', font: '#ffffff'}, {fill: '#0050ef', stroke: '#001DBC', font: '#ffffff'},
+			{fill: '#6a00ff', stroke: '#3700CC', font: '#ffffff'}, {fill: '#aa00ff', stroke: '#7700CC', font: '#ffffff'},
+			{fill: '#d80073', stroke: '#A50040', font: '#ffffff'}, {fill: '#a20025', stroke: '#6F0000', font: '#ffffff'}],
+			[{fill: '#e51400', stroke: '#B20000', font: '#ffffff'}, {fill: '#fa6800', stroke: '#C73500', font: '#ffffff'},
+			{fill: '#f0a30a', stroke: '#BD7000', font: '#ffffff'}, {fill: '#e3c800', stroke: '#B09500', font: '#ffffff'},
+			{fill: '#6d8764', stroke: '#3A5431', font: '#ffffff'}, {fill: '#647687', stroke: '#314354', font: '#ffffff'},
+			{fill: '#76608a', stroke: '#432D57', font: '#ffffff'}, {fill: '#a0522d', stroke: '#6D1F00', font: '#ffffff'}],
 			[null, {fill: mxConstants.NONE, stroke: '#36393d'},
 			{fill: '#fad7ac', stroke: '#b46504'}, {fill: '#fad9d5', stroke: '#ae4132'},
 			{fill: '#b0e3e6', stroke: '#0e8088'}, {fill: '#b1ddf0', stroke: '#10739e'},

+ 152 - 56
src/main/webapp/js/diagramly/EditorUi.js

@@ -31,7 +31,11 @@
 	 */
 	EditorUi.logError = function(message, url, linenumber, colno, err)
 	{
-		if (EditorUi.enableLogging)
+		if (urlParams['dev'] == '1')
+		{
+			EditorUi.debug('logError', message, url, linenumber, colno, err);
+		}
+		else if (EditorUi.enableLogging)
 		{
 			try
 			{
@@ -70,7 +74,11 @@
 	 */
 	EditorUi.logEvent = function(data)
 	{
-		if (EditorUi.enableLogging)
+		if (urlParams['dev'] == '1')
+		{
+			EditorUi.debug('logEvent', data);
+		}
+		else if (EditorUi.enableLogging)
 		{
 			try
 			{
@@ -92,9 +100,13 @@
 	 */
 	EditorUi.sendReport = function(data, maxLength)
 	{
-		try
+		if (urlParams['dev'] == '1')
+		{
+			EditorUi.debug('sendReport', data);
+		}
+		else if (EditorUi.enableLogging)
 		{
-			if (EditorUi.enableLogging)
+			try
 			{
 				maxLength = (maxLength != null) ? maxLength : 3000000;
 
@@ -107,10 +119,10 @@
 					'&url=' + encodeURIComponent(window.location.href) +
 					'&data=' + encodeURIComponent(data));
 			}
-		}
-		catch (e)
-		{
-			// ignore
+			catch (e)
+			{
+				// ignore
+			}
 		}
 	};
 
@@ -892,22 +904,7 @@
 				// Restores order of pages
 				for (var i = 0; i < this.pages.length; i++)
 				{
-					var mapping = this.pages[i].mapping;
-					
-					// Updates XML of all pages for realtime
-					if (this.currentPage != this.pages[i] && mapping != null && mapping.needsUpdate)
-					{
-						var enc = new mxCodec(mxUtils.createXmlDocument());
-						var temp = enc.encode(mapping.graphModel);
-					
-						// Uses the graph state from the realtime model
-						mapping.writeRealtimeToNode(temp);
-						mxUtils.setTextContent(this.pages[i].node, this.editor.graph.compressNode(temp));
-						
-						// Marks the page as up-to-date
-						mapping.needsUpdate = false;
-					}
-					else if (this.currentPage != this.pages[i] && this.pages[i].needsUpdate)
+					if (this.currentPage != this.pages[i] && this.pages[i].needsUpdate)
 					{
 						var enc = new mxCodec(mxUtils.createXmlDocument());
 						var temp = enc.encode(new mxGraphModel(this.pages[i].root));
@@ -927,6 +924,103 @@
 		return node;
 	};
 
+	/**
+	 * Removes any values, styles and geometries from the given XML node.
+	 */
+	EditorUi.prototype.anonymizePatch = function(patch)
+	{
+		delete patch[EditorUi.DIFF_INSERT];
+		delete patch[EditorUi.DIFF_REMOVE];
+		
+		if (patch[EditorUi.DIFF_UPDATE] != null)
+		{
+			for (var pageId in patch[EditorUi.DIFF_UPDATE])
+			{
+				var diff = patch[EditorUi.DIFF_UPDATE][pageId];
+				delete diff.name;
+				delete diff.view;
+				
+				if (diff.cells != null)
+				{
+					function anonymizeCellDiffs(key)
+					{
+						var cellDiffs = diff.cells[key];
+						
+						if (cellDiffs != null)
+						{
+							for (var cellId in cellDiffs)
+							{
+								delete cellDiffs[cellId].value;
+								delete cellDiffs[cellId].style;
+								delete cellDiffs[cellId].geometry;
+	
+								if (Object.keys(cellDiffs[cellId]).length == 0)
+								{
+									delete cellDiffs[cellId];
+								}
+							}
+							
+							if (Object.keys(cellDiffs).length == 0)
+							{
+								delete diff.cells[key];
+							}
+						}
+					};
+					
+					anonymizeCellDiffs(EditorUi.DIFF_INSERT);
+					anonymizeCellDiffs(EditorUi.DIFF_UPDATE);
+					
+					if (Object.keys(diff.cells).length == 0)
+					{
+						delete diff.cells;
+					}
+				}
+	
+				if (Object.keys(diff).length == 0)
+				{
+					delete patch[EditorUi.DIFF_UPDATE][pageId];
+				}
+			}
+			
+			if (Object.keys(patch[EditorUi.DIFF_UPDATE]).length == 0)
+			{
+				delete patch[EditorUi.DIFF_UPDATE];
+			}
+		}
+			
+		return patch;
+	};
+
+	/**
+	 * Removes any values, styles and geometries from the given XML node.
+	 */
+	EditorUi.prototype.anonymizeNode = function(node)
+	{
+		var nodes = node.getElementsByTagName('mxCell');
+		
+		for (var i = 0; i < nodes.length; i++)
+		{
+			nodes[i].removeAttribute('value');
+			nodes[i].removeAttribute('style');
+			
+			if (nodes[i].parentNode != null && nodes[i].parentNode.nodeName != 'root' &&
+				nodes[i].parentNode.parentNode != null)
+			{
+				nodes[i].setAttribute('id', nodes[i].parentNode.getAttribute('id'));
+				nodes[i].parentNode.parentNode.replaceChild(nodes[i], nodes[i].parentNode);
+			}
+		}
+		
+		var geos = node.getElementsByTagName('mxGeometry');
+		
+		while (geos.length > 0)
+		{
+			geos[0].parentNode.removeChild(geos[0]);
+		}
+		
+		return node;
+	};
+
 	/**
 	 * Translates this point by the given vector.
 	 * 
@@ -3047,9 +3141,11 @@
 	/**
 	 * EditorUi Overrides
 	 */
+	EditorUi.prototype.footerHeight = 0;
+	
     if (urlParams['offline'] == '1' || EditorUi.isElectronApp)
     {
-		EditorUi.prototype.footerHeight = 4;
+		//EditorUi.prototype.footerHeight = 4;
     }
     else
     {
@@ -3059,7 +3155,7 @@
     		Sidebar.prototype.thumbHeight = 64;
 		}
 
-		EditorUi.prototype.footerHeight = (screen.width >= 760 && screen.height >= 240) ? 46 : 0;
+		//EditorUi.prototype.footerHeight = (screen.width >= 760 && screen.height >= 240) ? 46 : 0;
 		
 		// Fetches footer from page
 		EditorUi.prototype.createFooter = function()
@@ -3068,28 +3164,28 @@
 			
 			if (footer != null)
 			{
-				footer.style.visibility = 'visible';
-				
-				// Adds button to hide the footer
-				var img = document.createElement('img');
-				img.setAttribute('border', '0');
-				img.setAttribute('src', Dialog.prototype.closeImage);
-				img.setAttribute('title', mxResources.get('hide'));
-				footer.appendChild(img)
-
-				if (mxClient.IS_QUIRKS)
-				{
-					img.style.position = 'relative';
-					img.style.styleFloat = 'right';
-					img.style.top = '-30px';
-					img.style.left = '164px';
-					img.style.cursor = 'pointer';
-				}
-				
-				mxEvent.addListener(img, 'click', mxUtils.bind(this, function()
-				{
-					this.hideFooter();
-				}));
+//				footer.style.visibility = 'hidden';
+//				
+//				// Adds button to hide the footer
+//				var img = document.createElement('img');
+//				img.setAttribute('border', '0');
+//				img.setAttribute('src', Dialog.prototype.closeImage);
+//				img.setAttribute('title', mxResources.get('hide'));
+//				footer.appendChild(img)
+//
+//				if (mxClient.IS_QUIRKS)
+//				{
+//					img.style.position = 'relative';
+//					img.style.styleFloat = 'right';
+//					img.style.top = '-30px';
+//					img.style.left = '164px';
+//					img.style.cursor = 'pointer';
+//				}
+//				
+//				mxEvent.addListener(img, 'click', mxUtils.bind(this, function()
+//				{
+//					this.hideFooter();
+//				}));
 			}
 
 			return footer;
@@ -3150,14 +3246,14 @@
      */
     EditorUi.prototype.hideFooter = function()
     {
-	    	var footer = document.getElementById('geFooter');
-		    	
-	    	if (footer != null)
-	    	{
-	    		this.footerHeight = 0;
-	    		footer.style.display = 'none';
-	    		this.refresh();
-	    	}
+    	var footer = document.getElementById('geFooter');
+	    	
+    	if (footer != null)
+    	{
+    		this.footerHeight = 0;
+    		footer.style.display = 'none';
+    		this.refresh();
+    	}
     };
 
     /**

+ 8 - 0
src/main/webapp/js/diagramly/OneDriveFile.js

@@ -46,6 +46,14 @@ OneDriveFile.prototype.getIdOf = function(itemObj, parent)
 	return (itemObj.parentReference.driveId? itemObj.parentReference.driveId + '/' : '') + (parent? itemObj.parentReference.id : itemObj.id);
 };
 
+/**
+ * Gets the channel ID for sync messages.
+ */
+OneDriveFile.prototype.getChannelId = function()
+{
+	return 'W-' + DrawioFile.prototype.getChannelId.apply(this, arguments);
+};
+
 /**
  * Translates this point by the given vector.
  * 

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


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

@@ -2621,7 +2621,7 @@ ChangePageSetup.prototype.execute = function()
 
     if (this.foldingEnabled != null && this.foldingEnabled != this.ui.editor.graph.foldingEnabled)
     {
-    		this.ui.setFoldingEnabled(this.foldingEnabled);
+    	this.ui.setFoldingEnabled(this.foldingEnabled);
         this.foldingEnabled = !this.foldingEnabled;
     }
 };
@@ -2889,7 +2889,6 @@ EditorUi.prototype.refresh = function(sizeDidChange)
 	}
 	
 	var effHsplitPosition = Math.max(0, Math.min(this.hsplitPosition, w - this.splitSize - 20));
-
 	var tmp = 0;
 	
 	if (this.menubar != null)
@@ -2934,6 +2933,7 @@ EditorUi.prototype.refresh = function(sizeDidChange)
 	this.hsplit.style.top = this.sidebarContainer.style.top;
 	this.hsplit.style.bottom = (this.footerHeight + off) + 'px';
 	this.hsplit.style.left = effHsplitPosition + 'px';
+	this.footerContainer.style.display = (this.footerHeight == 0) ? 'none' : '';
 	
 	if (this.tabContainer != null)
 	{

+ 0 - 5
src/main/webapp/js/mxgraph/Format.js

@@ -48,11 +48,6 @@ Format.prototype.init = function()
 	
 	this.update = mxUtils.bind(this, function(sender, evt)
 	{
-		if (this.editorUi.currentMenu != null)
-		{
-			this.editorUi.hideCurrentMenu();
-		}
-		
 		this.clearSelectionState();
 		this.refresh();
 	});

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


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


+ 66 - 22
src/main/webapp/plugins/random.js

@@ -3,20 +3,24 @@
  */
 Draw.loadPlugin(function(ui)
 {
+	var defaultDelay = 2000;
+	var defaultMax = 50;
+	var graph = ui.editor.graph;
+
 	// Adds resource for action
 	mxResources.parse('randomLabel=Random Label...');
-
+	
 	// Adds action
 	ui.actions.addAction('randomLabel', function()
 	{
-		var graph = ui.editor.graph;
 		var cells = graph.getSelectionCells().slice();
 		
 		if (cells.length > 0)
 		{
+			var delay = parseInt(prompt('Delay (ms)', defaultDelay));
+			var max = parseInt(prompt('Cycles', defaultMax));
 			var counter = 0;
-			var max = 50; //parseInt(prompt('Cycles', '50'));
-			
+
 			function schedule()
 			{
 				var jitter = 1 + 0.3 * (Math.random() - 0.5);
@@ -28,11 +32,26 @@ Draw.loadPlugin(function(ui)
 						graph.labelChanged(cells[i], 'Test ' + Math.round(Math.random() * 100));
 					}
 					
-					if (counter++ < max && ui.dialog == null)
+					if (ui.dialog != null)
 					{
-						schedule();
+						console.log('randomLabel halted');
 					}
-				}, 3500 * jitter);
+					else
+					{
+						ui.saveFile(null, function()
+						{
+							if (counter++ < max && ui.dialog == null)
+							{
+								console.log('randomLabel', counter);
+								schedule();
+							}
+							else
+							{
+								console.log('randomLabel halted');
+							}
+						});
+					}
+				}, delay * jitter);
 			}
 			
 			schedule();
@@ -49,16 +68,13 @@ Draw.loadPlugin(function(ui)
 	// Adds action
 	ui.actions.addAction('swapChildren', function()
 	{
-		var graph = ui.editor.graph;
 		var cells = graph.getSelectionCells().slice();
 		
 		if (cells.length > 1)
 		{
-
+			var delay = parseInt(prompt('Delay (ms)', defaultDelay));
+			var max = parseInt(prompt('Cycles', defaultMax));
 			var counter = 0;
-//			var max = 1;
-			var max = parseInt(prompt('Cycles', '100'));
-			
 			
 			function schedule()
 			{
@@ -117,11 +133,26 @@ Draw.loadPlugin(function(ui)
 						}
 					}
 					
-					if (counter++ < max && ui.dialog == null)
+					if (ui.dialog != null)
 					{
-						schedule();
+						console.log('swapChildren halted');
 					}
-				}, 15000 * jitter);
+					else
+					{
+						ui.saveFile(null, function()
+						{
+							if (counter++ < max && ui.dialog == null)
+							{
+								console.log('swapChildren', counter);
+								schedule();
+							}
+							else
+							{
+								console.log('swapChildren halted');
+							}
+						});
+					}
+				}, delay * jitter);
 			}
 			
 			schedule();
@@ -138,15 +169,13 @@ Draw.loadPlugin(function(ui)
 	// Adds action
 	ui.actions.addAction('reorderChildren', function()
 	{
-		var graph = ui.editor.graph;
 		var cells = graph.getSelectionCells().slice();
 		
 		if (cells.length > 0)
 		{
-
+			var delay = parseInt(prompt('Delay (ms)', defaultDelay));
+			var max = parseInt(prompt('Cycles', defaultMax));
 			var counter = 0;
-//			var max = 1;
-			var max = parseInt(prompt('Cycles', '100'));
 			
 			function schedule()
 			{
@@ -181,11 +210,26 @@ Draw.loadPlugin(function(ui)
 						}
 					}
 					
-					if (counter++ < max && ui.dialog == null)
+					if (ui.dialog != null)
 					{
-						schedule();
+						console.log('reorderChildren halted');
+					}
+					else
+					{
+						ui.saveFile(null, function()
+						{
+							if (counter++ < max && ui.dialog == null)
+							{
+								console.log('reorderChildren', counter);
+								schedule();
+							}
+							else
+							{
+								console.log('reorderChildren halted');
+							}
+						});
 					}
-				}, 5000 * jitter);
+				}, delay * jitter);
 			}
 			
 			schedule();