Browse Source

10.0.22 release

Former-commit-id: dda53dda284c738bd28c304d5011fedee76500da
Gaudenz Alder 6 years ago
parent
commit
62c97c7a55

+ 6 - 0
ChangeLog

@@ -1,3 +1,9 @@
+05-JAN-2019: 10.0.22
+
+- Adds initial change check in sync protocol
+- Ignores numeric JS errors in checksum
+- Fixes pending sync on inactive window
+
 04-JAN-2019: 10.0.21
 
 - Adds debug output for checksum errors

+ 1 - 1
VERSION

@@ -1 +1 @@
-10.0.21
+10.0.22

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

@@ -1,7 +1,7 @@
 CACHE MANIFEST
 
 # THIS FILE WAS GENERATED. DO NOT MODIFY!
-# 01/04/2019 06:00 PM
+# 01/05/2019 02:00 PM
 
 app.html
 index.html?offline=1

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


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


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


+ 54 - 18
src/main/webapp/js/diagramly/DrawioFile.js

@@ -1358,16 +1358,30 @@ DrawioFile.prototype.redirectToNewApp = function(error)
 		
 		var redirect = mxUtils.bind(this, function()
 		{
-			this.ui.spinner.spin(document.body, mxResources.get('loading'));
-			this.redirectDialogShowing = false;
+			var fn = mxUtils.bind(this, function()
+			{
+				this.redirectDialogShowing = false;
+				
+				if (window.location.href == url)
+				{
+					window.location.reload();
+				}
+				else
+				{
+					window.location.href = url;
+				}
+			});
 			
-			if (window.location.href == url)
+			if (this.isModified())
 			{
-				window.location.reload();
+				this.ui.confirm(mxResources.get('allChangesLost'), mxUtils.bind(this, function()
+				{
+					this.redirectDialogShowing = false;
+				}), fn, mxResources.get('cancel'), mxResources.get('discardChanges'));
 			}
 			else
 			{
-				window.location.href = url;
+				fn();
 			}
 		});
 		
@@ -1574,25 +1588,47 @@ DrawioFile.prototype.fileChanged = function()
  */
 DrawioFile.prototype.fileSaved = function(savedData, lastDesc, success, error)
 {
-	this.inConflictState = false;
-	this.invalidChecksum = false;
-	this.checkPages();
-	
-	if (this.sync == null)
+	try
 	{
-		this.shadowData = savedData;
-		this.shadowPages = null;
+		this.inConflictState = false;
+		this.invalidChecksum = false;
+		this.checkPages();
 		
-		if (success != null)
+		if (this.sync == null)
 		{
-			success();
+			this.shadowData = savedData;
+			this.shadowPages = null;
+			
+			if (success != null)
+			{
+				success();
+			}
+		}
+		else
+		{
+			this.sync.fileSaved(this.ui.getPagesForNode(
+				mxUtils.parseXml(savedData).documentElement),
+				lastDesc, success, error);
 		}
 	}
-	else
+	catch (e)
 	{
-		this.sync.fileSaved(this.ui.getPagesForNode(
-			mxUtils.parseXml(savedData).documentElement),
-			lastDesc, success, error);
+		this.inConflictState = true;
+		this.invalidChecksum = true;
+		
+		if (error != null)
+		{
+			error(e);
+		}
+
+		try
+		{
+			this.sendErrorReport('Error in fileSaved', null, e);
+		}
+		catch (e2)
+		{
+			// ignore
+		}
 	}
 };
 

+ 164 - 193
src/main/webapp/js/diagramly/DrawioFileSync.js

@@ -18,7 +18,7 @@ DrawioFileSync = function(file)
 	{
 		this.updateOnlineState();
 
-		if (this.channelId != null && this.isConnected())
+		if (this.isConnected())
 		{
 			this.fileChangedNotify();
 		}
@@ -31,14 +31,14 @@ DrawioFileSync = function(file)
 	{
 		if (document.visibilityState == 'hidden')
 		{
-			if (this.isConnected() && !this.paused)
+			if (this.isConnected())
 			{
 				this.stop();
 			}
 		}
-		else if (this.channelId == null)
+		else
 		{
-			this.start(this.paused);
+			this.start();
 		}
 	});
     
@@ -48,11 +48,7 @@ DrawioFileSync = function(file)
 	this.activityListener = mxUtils.bind(this, function(evt)
 	{
 		this.lastActivity = new Date();
-		
-		if (this.channelId == null)
-		{
-			this.start(this.paused);
-		}
+		this.start();
 	});
 
 	mxEvent.addListener(document, (mxClient.IS_POINTER) ? 'pointermove' : 'mousemove', this.activityListener);
@@ -72,7 +68,7 @@ DrawioFileSync = function(file)
  * be incremented if new messages are added or the format is changed.
  * This must be numeric to compare older vs newer protocol versions.
  */
-DrawioFileSync.PROTOCOL = 2;
+DrawioFileSync.PROTOCOL = 3;
 
 //Extends mxEventSource
 mxUtils.extend(DrawioFileSync, mxEventSource);
@@ -125,12 +121,7 @@ DrawioFileSync.prototype.maxCacheReadyRetries = 2;
 /**
  * Specifies if descriptor change events should be ignored.
  */
-DrawioFileSync.prototype.cacheReadyDelay = 600;
-
-/**
- * Specifies if notifications should be sent and received for changes.
- */
-DrawioFileSync.prototype.paused = false;
+DrawioFileSync.prototype.cacheReadyDelay = 500;
 
 /**
  * Inactivity timeout is 1 hour.
@@ -145,59 +136,56 @@ DrawioFileSync.prototype.lastActivity = null;
 /**
  * Adds all listeners.
  */
-DrawioFileSync.prototype.start = function(resumed)
+DrawioFileSync.prototype.start = function()
 {
-	if (document.visibilityState != 'hidden')
+	if (this.channelId == null)
 	{
-		this.lastModified = this.file.getLastModifiedDate();
 		this.channelId = this.file.getChannelId();
+	}
+	
+	if (this.key == null)
+	{
+		this.key = this.file.getChannelKey();
+	}
+	
+	if (this.pusher == null && this.channelId != null &&
+		document.visibilityState != 'hidden') 
+	{
+		this.pusher = this.ui.getPusher();
 		
-		if (this.channelId != null) 
+		if (this.pusher != null)
 		{
-			this.pusher = this.ui.getPusher();
-			
-			if (this.pusher != null)
+			try
 			{
-				try
+				// Error listener must be installed before trying to create channel
+				this.pusherErrorListener = mxUtils.bind(this, function(err)
 				{
-					// Error listener must be installed before trying to create channel
-					this.pusherErrorListener = mxUtils.bind(this, function(err)
+					if (err.error != null && err.error.data != null &&
+						err.error.data.code === 4004)
 					{
-						if (err.error != null && err.error.data != null &&
-							err.error.data.code === 4004)
-						{
-							EditorUi.logError('Error: Pusher Limit', null, this.file.getId());
-						}
-					});
-		
-					this.pusher.connection.bind('error', this.pusherErrorListener);
-				}
-				catch (e)
-				{
-					// ignore
-				}
+						EditorUi.logError('Error: Pusher Limit', null, this.file.getId());
+					}
+				});
+	
+				this.pusher.connection.bind('error', this.pusherErrorListener);
+			}
+			catch (e)
+			{
+				// ignore
+			}
+			
+			try
+			{
+				this.pusher.connect();
+				this.channel = this.pusher.subscribe(this.channelId);
+
+				EditorUi.debug('Sync.start', [this]);
 				
-				try
+				if (this.file.stats.start == null)
 				{
-					this.pusher.connect();
-					this.channel = this.pusher.subscribe(this.channelId);
-					this.key = this.file.getChannelKey();
-					this.lastActivity = new Date();
-					this.paused = false;
-					
-					if (resumed)
-					{
-						this.fileChangedNotify();
-					}
-					
-					if (this.file.stats.start == null)
-					{
-						this.file.stats.start = new Date().toISOString();
-					}
-					
-					EditorUi.debug('Sync.start', [this], resumed);
-					
-					if (!this.ui.isOffline() && !resumed)
+					this.file.stats.start = new Date().toISOString();
+
+					if (!this.ui.isOffline())
 					{
 						var user = this.file.getCurrentUser();
 						var uid = (user != null) ? this.ui.hashValue(user.id) : 'unknown';
@@ -211,21 +199,23 @@ DrawioFileSync.prototype.start = function(resumed)
 							label: this.file.stats.start});
 					}
 				}
-				catch (e)
-				{
-					// ignore
-				}
 			}
-	
-			this.installListeners();
-		    
-			window.setTimeout(mxUtils.bind(this, function()
+			catch (e)
 			{
-				this.resetUpdateStatusThread();
-				this.updateOnlineState();
-				this.updateStatus();
-			}, 0));
+				// ignore
+			}
+
+			this.installListeners();
 		}
+
+		window.setTimeout(mxUtils.bind(this, function()
+		{
+			this.lastModified = this.file.getLastModifiedDate();
+			this.lastActivity = new Date();
+			this.resetUpdateStatusThread();
+			this.updateOnlineState();
+			this.updateStatus();
+		}, 0));
 	}
 };
 
@@ -359,7 +349,7 @@ DrawioFileSync.prototype.updateOnlineState = function()
  */
 DrawioFileSync.prototype.updateStatus = function()
 {
-	if (!this.paused && this.isConnected() && this.lastActivity != null &&
+	if (this.isConnected() && this.lastActivity != null &&
 		(new Date().getTime() - this.lastActivity.getTime()) / 1000 >
 		this.inactivityTimeoutSeconds)
 	{
@@ -473,9 +463,6 @@ DrawioFileSync.prototype.resetUpdateStatusThread = function()
  */
 DrawioFileSync.prototype.installListeners = function()
 {
-	// Ignores old messages
-	var lastModifiedDate = null;
-
 	if (this.pusher != null)
 	{
 	    // Listens to remote model changes
@@ -484,22 +471,28 @@ DrawioFileSync.prototype.installListeners = function()
 			this.updateOnlineState();
 			this.updateStatus();
 			
-			if (this.isConnected() && !this.announced)
+			if (this.isConnected())
 			{
-				var user = this.file.getCurrentUser();
-				var join = {a: 'join'};
-				
-				if (user != null)
+				if (!this.announced)
 				{
-					join.name = user.displayName;
-					join.uid = user.id;
+					var user = this.file.getCurrentUser();
+					var join = {a: 'join'};
+					
+					if (user != null)
+					{
+						join.name = user.displayName;
+						join.uid = user.id;
+					}
+	
+					mxUtils.post(this.cacheUrl, this.getIdParameters() +
+						'&msg=' + encodeURIComponent(this.objectToString(
+						this.createMessage(join))));
+					this.file.stats.msgSent++;
+					this.announced = true;
 				}
 
-				mxUtils.post(this.cacheUrl, this.getIdParameters() +
-					'&msg=' + encodeURIComponent(this.objectToString(
-					this.createMessage(join))));
-				this.announced = true;
-				this.file.stats.msgSent++;
+				// Catchup on any lost edits
+				this.fileChangedNotify();
 			}
 		});
 		
@@ -527,7 +520,10 @@ DrawioFileSync.prototype.installListeners = function()
 						// Handles protocol mismatch
 						if (msg.v > DrawioFileSync.PROTOCOL)
 						{
-							this.file.redirectToNewApp();
+							this.file.redirectToNewApp(mxUtils.bind(this, function()
+							{
+								// Callback adds cancel option
+							}));
 						}
 						else if (msg.v === DrawioFileSync.PROTOCOL && msg.d != null)
 						{
@@ -675,12 +671,7 @@ DrawioFileSync.prototype.updateDescriptor = function(desc)
 {
 	this.file.setDescriptor(desc);
 	this.file.descriptorChanged();
-	
-	if (this.channelId == null)
-	{
-		// Checks channel ID and starts sync
-		this.start();
-	}
+	this.start();
 };
 
 /**
@@ -702,6 +693,7 @@ DrawioFileSync.prototype.catchup = function(etag, secret, success, error, abort)
 		// Cache entry may not have been uploaded to cache before new
 		// etag is visible to client so retry once after cache miss
 		var cacheReadyRetryCount = 0;
+		var failed = false;
 		
 		var doCatchup = mxUtils.bind(this, function()
 		{
@@ -751,7 +743,7 @@ DrawioFileSync.prototype.catchup = function(etag, secret, success, error, abort)
 										
 										if (value.v > DrawioFileSync.PROTOCOL)
 										{
-											this.file.redirectToNewApp(error);
+											failed = true;
 											temp = [];
 											break;
 										}
@@ -769,6 +761,7 @@ DrawioFileSync.prototype.catchup = function(etag, secret, success, error, abort)
 										}
 										else
 										{
+											failed = true;
 											temp = [];
 											break;
 										}
@@ -795,10 +788,10 @@ DrawioFileSync.prototype.catchup = function(etag, secret, success, error, abort)
 							}
 							// Retries if cache entry was not yet there
 							else if (cacheReadyRetryCount <= this.maxCacheReadyRetries &&
-								req.getStatus() != 401)
+								!failed && req.getStatus() != 401)
 							{
 								cacheReadyRetryCount++;
-								window.setTimeout(doCatchup, this.cacheReadyDelay);
+								window.setTimeout(doCatchup, (cacheReadyRetryCount + 1) * this.cacheReadyDelay);
 							}
 							else
 							{
@@ -829,14 +822,9 @@ DrawioFileSync.prototype.reload = function(success, error, abort)
 {
 	this.file.updateFile(mxUtils.bind(this, function()
 	{
-		if (this.channelId == null)
-		{
-			// Checks channel ID and starts sync
-			this.start();
-		}
-
 		this.lastModified = this.file.getLastModifiedDate();
 		this.updateStatus();
+		this.start();
 		
 		if (success != null)
 		{
@@ -889,7 +877,7 @@ DrawioFileSync.prototype.merge = function(patches, checksum, etag, success, erro
 					'from', this.file.getCurrentEtag(), 'to', etag,
 					'backup', this.file.backupPatch,
 					'attempt', this.catchupRetryCount,
-					'details', details,
+					'details', details, currentDetails,
 					'patches', patches,
 					'checksum', checksum == current, checksum);
 			}
@@ -957,7 +945,7 @@ DrawioFileSync.prototype.descriptorChanged = function(etag)
 {
 	this.lastModified = this.file.getLastModifiedDate();
 	
-	if (this.isConnected())
+	if (this.channelId != null)
 	{
 		var msg = this.objectToString(this.createMessage({a: 'desc',
 			m: this.lastModified.getTime()}));
@@ -1014,67 +1002,52 @@ DrawioFileSync.prototype.fileSaved = function(pages, lastDesc, success, error)
 	this.resetUpdateStatusThread();
 	this.catchupRetryCount = 0;
 	
-	if (this.isConnected() && !this.file.inConflictState && !this.redirectDialogShowing)
+	if (!this.ui.isOffline() && !this.file.inConflictState && !this.redirectDialogShowing)
 	{
-		// Computes diff and checksum
-		var shadow = (this.file.shadowPages != null) ?
-			this.file.shadowPages : this.ui.getPagesForNode(
-			mxUtils.parseXml(this.file.shadowData).documentElement)
-		var details = {v: EditorUi.VERSION, t: new Date().toISOString(), ua: navigator.userAgent};
-		var checksum = this.ui.getHashValueForPages(pages, details);
-		var diff = this.ui.diffPages(shadow, pages);
-		
-		// Data is stored in cache and message is sent to all listeners
-		var data = this.objectToString(this.createMessage({patch: diff, checksum: checksum, details: details}));
-		var msg = this.objectToString(this.createMessage({m: this.lastModified.getTime()}));
-		var secret = this.file.getDescriptorSecret(this.file.getDescriptor());
-		var etag = this.file.getDescriptorEtag(lastDesc);
-		var current = this.file.getCurrentEtag();
-		this.file.shadowPages = pages;
-		
-		mxUtils.post(this.cacheUrl, this.getIdParameters() +
-			'&from=' + encodeURIComponent(etag) + '&to=' + encodeURIComponent(current) +
-			'&msg=' + encodeURIComponent(msg) + ((secret != null) ? '&secret=' + encodeURIComponent(secret) : '') +
-			((data.length < this.maxCacheEntrySize) ? '&data=' + encodeURIComponent(data) : ''),
-			mxUtils.bind(this, function(req)
+		this.start();
+
+		if (this.channelId != null)
 		{
-			if (req.getStatus() >= 200 && req.getStatus() <= 299)
+			// Computes diff and checksum
+			var shadow = (this.file.shadowPages != null) ?
+				this.file.shadowPages : this.ui.getPagesForNode(
+				mxUtils.parseXml(this.file.shadowData).documentElement)
+			var details = {v: EditorUi.VERSION, t: new Date().toISOString(), ua: navigator.userAgent};
+			var checksum = this.ui.getHashValueForPages(pages, details);
+			var diff = this.ui.diffPages(shadow, pages);
+			
+			// Data is stored in cache and message is sent to all listeners
+			var data = this.objectToString(this.createMessage({patch: diff, checksum: checksum, details: details}));
+			var msg = this.objectToString(this.createMessage({m: this.lastModified.getTime()}));
+			var secret = this.file.getDescriptorSecret(this.file.getDescriptor());
+			var etag = this.file.getDescriptorEtag(lastDesc);
+			var current = this.file.getCurrentEtag();
+			this.file.stats.bytesSent += data.length;
+			this.file.stats.msgSent++;
+	
+			mxUtils.post(this.cacheUrl, this.getIdParameters() +
+				'&from=' + encodeURIComponent(etag) + '&to=' + encodeURIComponent(current) +
+				'&msg=' + encodeURIComponent(msg) + ((secret != null) ? '&secret=' + encodeURIComponent(secret) : '') +
+				((data.length < this.maxCacheEntrySize) ? '&data=' + encodeURIComponent(data) : ''),
+				mxUtils.bind(this, function(req)
 			{
-				if (success != null)
-				{
-					success();
-				}
-			}
-			else if (error != null)
+				// Ignores response
+			}));
+	
+			if (urlParams['test'] == '1')
 			{
-				error({message: req.getStatus()});
+				EditorUi.debug('Sync.fileSaved', [this],
+					'from', etag, 'to', current, data.length,
+					'bytes', 'diff', diff, 'checksum', checksum);
 			}
-		}));
-
-		if (urlParams['test'] == '1')
-		{
-			EditorUi.debug('Sync.fileSaved', [this],
-				'from', etag, 'to', current, data.length,
-				'bytes', 'diff', diff, 'checksum', checksum);
 		}
-		
-		this.file.stats.bytesSent += data.length;
-		this.file.stats.msgSent++;
 	}
-	else
+	
+	this.file.shadowPages = pages;
+	
+	if (success != null)
 	{
-		this.file.shadowPages = pages;
-		
-		if (this.channelId == null)
-		{
-			// Checks channel ID and starts sync
-			this.start();
-		}
-
-		if (success != null)
-		{
-			success();
-		}
+		success();
 	}
 };
 
@@ -1140,49 +1113,47 @@ DrawioFileSync.prototype.fileConflict = function(desc, success, error)
  */
 DrawioFileSync.prototype.stop = function()
 {
-	EditorUi.debug('Sync.stop', [this]);
-
-	if (this.changeListener != null && this.channel != null)
-	{
-		this.channel.unbind('changed', this.changeListener);
-		this.changeListener = null;
-	}
-
-	if (this.connectionListener != null)
+	if (this.pusher != null)
 	{
-		if (this.pusher != null && this.pusher.connection != null)
+		EditorUi.debug('Sync.stop', [this]);
+	
+		if (this.changeListener != null && this.channel != null)
 		{
-			this.pusher.connection.unbind('state_change', this.connectionListener);
+			this.channel.unbind('changed', this.changeListener);
+			this.changeListener = null;
 		}
-		
-		this.connectionListener = null;
-	}
-
-	if (this.pusherErrorListener != null)
-	{
-		if (this.pusher != null && this.pusher.connection != null)
+	
+		if (this.connectionListener != null)
+		{
+			if (this.pusher.connection != null)
+			{
+				this.pusher.connection.unbind('state_change', this.connectionListener);
+			}
+			
+			this.connectionListener = null;
+		}
+	
+		if (this.pusherErrorListener != null)
 		{
-			this.pusher.connection.unbind('error', this.pusherErrorListener);
+			if (this.pusher.connection != null)
+			{
+				this.pusher.connection.unbind('error', this.pusherErrorListener);
+			}
+			
+			this.pusherErrorListener = null;
 		}
-		
-		this.pusherErrorListener = null;
-	}
-
-	if (this.pusher != null && this.channel != null && this.channelId != null) 
-	{
-		// See https://github.com/pusher/pusher-js/issues/75
-		//this.pusher.unsubscribe(this.channelId);
-		this.channel = null;
-	}
 	
-	if (this.pusher != null)
-	{
+		if (this.channel != null) 
+		{
+			// See https://github.com/pusher/pusher-js/issues/75
+			// this.pusher.unsubscribe(this.channelId);
+			this.channel = null;
+		}
+		
 		this.pusher.disconnect();
+		this.pusher = null;
 	}
 	
-	this.channelId = null;
-	this.pusher = null;
-	this.paused = true;
 	this.updateOnlineState();
 	this.updateStatus();
 };
@@ -1192,7 +1163,7 @@ DrawioFileSync.prototype.stop = function()
  */
 DrawioFileSync.prototype.destroy = function()
 {
-	if (this.isConnected())
+	if (this.channelId != null)
 	{
 		var user = this.file.getCurrentUser();
 		var leave = {a: 'leave'};

+ 20 - 7
src/main/webapp/js/diagramly/EditorUi.js

@@ -1022,7 +1022,7 @@
 							{
 								if (cellDiffs[cellId].value != null)
 								{
-									cellDiffs[cellId].value =  '[' +
+									cellDiffs[cellId].value = '[' +
 										cellDiffs[cellId].value.length + ']';
 								}
 								
@@ -1034,8 +1034,8 @@
 								
 								if (cellDiffs[cellId].geometry != null)
 								{
-									cellDiffs[cellId].geometry = this.anonymizeString(
-										cellDiffs[cellId].geometry);
+									cellDiffs[cellId].geometry = '[' +
+										cellDiffs[cellId].geometry.length + ']';
 								}
 								
 								if (Object.keys(cellDiffs[cellId]).length == 0)
@@ -2408,7 +2408,20 @@
 				details.cellCount += model.getDescendants(model.root).length;
 			}
 			
-			hash = ((hash << 5) - hash + this.hashValue(diagram, null, details)) << 0;
+			hash = ((hash << 5) - hash + this.hashValue(diagram, function(obj, key, value, isXml)
+			{
+				// Ignores JS machine rounding errors in known numeric attributes
+				// eg. 412.33333333333326 (Webkit/FF) == 412.33333333333325 (Edge/IE11)
+				if (isXml && (obj.nodeName == 'mxGeometry' || obj.nodeName == 'mxPoint') &&
+					(key == 'x' || key == 'y' || key == 'width' || key == 'height'))
+				{
+					return Math.round(value);
+				}
+				else
+				{
+					return value;
+				}
+			}, details)) << 0;
 		}
 		
 		return hash;
@@ -2428,7 +2441,7 @@
 		{
 			if (obj.nodeName != null)
 			{
-				hash = hash ^ this.hashValue(obj.nodeName, replacer);
+				hash = hash ^ this.hashValue(obj.nodeName, replacer, details);
 			}
 			
 			if (obj.attributes != null)
@@ -2441,7 +2454,7 @@
 				for (var i = 0; i < obj.attributes.length; i++)
 				{
 					var key = obj.attributes[i].name;
-					var value = (replacer != null) ? replacer(obj, key, true) : obj.attributes[i].value;
+					var value = (replacer != null) ? replacer(obj, key, obj.attributes[i].value, true) : obj.attributes[i].value;
 	
 					if (value != null)
 					{
@@ -2472,7 +2485,7 @@
 			
 			for (var i = 0; i < str.length; i++)
 			{
-		    	temp  = ((temp << 5) - temp + str.charCodeAt(i)) << 0;
+		    	temp = ((temp << 5) - temp + str.charCodeAt(i)) << 0;
 			}
 		    
 			hash = hash ^ temp;

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


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
+ 296 - 295
src/main/webapp/js/viewer.min.js