Browse Source

15.0.5 release

David Benson [draw.io] 4 years ago
parent
commit
d1ba3ba35a

+ 7 - 0
ChangeLog

@@ -1,3 +1,10 @@
+01-SEP-2021: 15.0.5
+
+- Adds hover handler for blue arrows
+- Fixes PDF export with background pages
+- Adds page range for PDF export
+- Fixes possible charAt is not a function
+
 31-AUG-2021: 15.0.4
 
 - Fixes offset for background pages

+ 1 - 1
VERSION

@@ -1 +1 @@
-15.0.4
+15.0.5

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


+ 11 - 14
src/main/webapp/js/diagramly/App.js

@@ -2092,29 +2092,26 @@ App.prototype.showRatingBanner = function()
 };
 
 /**
- * 
+ * Checks license in the case of Google Drive storage.
+ * IMPORTANT: Do not change this function without consulting 
+ * the privacy lead. No personal information must be sent.
  */
 App.prototype.checkLicense = function()
 {
 	var driveUser = this.drive.getUser();
-	var email = ((urlParams['dev'] == '1') ? urlParams['lic'] : null) ||
-		((driveUser != null) ? driveUser.email : null);
+	var email = (driveUser != null) ? driveUser.email : null;
 	
-	if (!this.isOffline() && !this.editor.chromeless && email != null)
+	if (!this.isOffline() && !this.editor.chromeless && email != null && driveUser.id != null)
 	{
-		// Anonymises the local part of the email address
+		// Only the domain and hashed user ID are transmitted. This code was reviewed and deemed
+		// compliant by dbenson 2021-09-01.
 		var at = email.lastIndexOf('@');
-		var domain = email;
+		var domain = (at >= 0) ? email.substring(at + 1) : '';
+		var userId = Editor.crc32(driveUser.id);
 		
-		if (at >= 0)
-		{
-			domain = email.substring(at + 1);
-			email = Editor.crc32(email.substring(0, at)) + '@' + domain;
-		}
-
 		// Timestamp is workaround for cached response in certain environments
-		mxUtils.post('/license', 'domain=' + encodeURIComponent(domain) + '&email=' + encodeURIComponent(email) + 
-				'&lc=' + encodeURIComponent(driveUser.locale) + '&ts=' + new Date().getTime(),
+		mxUtils.post('/license', 'domain=' + encodeURIComponent(domain) + '&id=' + encodeURIComponent(userId) + 
+				'&ts=' + new Date().getTime(),
 			mxUtils.bind(this, function(req)
 			{
 				try

+ 34 - 23
src/main/webapp/js/diagramly/EditorUi.js

@@ -1270,28 +1270,32 @@
 							var temp = enc.encode(new mxGraphModel(page.root));
 							this.editor.graph.saveViewState(page.viewState,
 								temp, null, resolveReferences);
-							EditorUi.removeChildNodes(page.node);
-							mxUtils.setTextContent(page.node, Graph.compressNode(temp));
+							EditorUi.removeChildNodes(currNode);
+							mxUtils.setTextContent(currNode, Graph.compressNode(temp));
 
 							// Marks the page as up-to-date
 							delete page.needsUpdate;
 						}
 						else if (resolveReferences)
 						{
-							// Checks for unresolved background page references
-							if (page.viewState == null)
-							{
-								var modelNode = this.editor.extractGraphModel(page.node);
-								page.viewState = this.editor.graph.createViewState(modelNode)
-							}
+							this.updatePageRoot(page);
 
-							if (page.viewState.backgroundImage != null &&
-								Graph.isPageLink(page.viewState.backgroundImage.originalSrc) &&
-								page.viewState.backgroundImage.src == null)
+							// Forces update of background page image in offscreen page
+							if (page.viewState.backgroundImage != null)
 							{
-								page.viewState.backgroundImage = this.createImageForPageLink(page.viewState.backgroundImage.originalSrc);
+								if (page.viewState.backgroundImage.originalSrc != null)
+								{
+									page.viewState.backgroundImage = this.createImageForPageLink(
+										page.viewState.backgroundImage.originalSrc, page);
+								}
+								else if (Graph.isPageLink(page.viewState.backgroundImage.src))
+								{
+									page.viewState.backgroundImage = this.createImageForPageLink(
+										page.viewState.backgroundImage.src, page);
+								}
 							}
 
+							// Updates the page node
 							if (page.viewState.backgroundImage != null &&
 								page.viewState.backgroundImage.originalSrc != null)
 							{
@@ -1300,7 +1304,7 @@
 								this.editor.graph.saveViewState(page.viewState,
 									temp, null, resolveReferences);
 								currNode = currNode.cloneNode(false);
-								mxUtils.setTextContent(node, Graph.compressNode(temp));
+								mxUtils.setTextContent(currNode, Graph.compressNode(temp));
 							}
 						}
 					}
@@ -1585,9 +1589,9 @@
 		// Forces compression of embedded XML
 		if (forceSvg || (!forceXml && file != null && /(\.svg)$/i.test(file.getTitle())))
 		{
-			uncompressed = false;
 			var darkTheme = graph.themes != null && graph.defaultThemeName == 'darkTheme';
-			
+			uncompressed = false;
+
 			// Exports SVG for first page while other page is visible by creating a graph
 			// LATER: Add caching for the graph or SVG while not on first page
 			// Dark mode requires a refresh that would destroy all handlers
@@ -1916,7 +1920,7 @@
 	 * @param {number} dy Y-coordinate of the translation.
 	 */
 	EditorUi.prototype.downloadFile = function(format, uncompressed, addShadow, ignoreSelection, currentPage,
-		pageVisible, transparent, scale, border, grid, includeXml)
+		pageVisible, transparent, scale, border, grid, includeXml, pageRange)
 	{
 		try
 		{
@@ -2019,7 +2023,7 @@
 						}
 						
 						var req = this.createDownloadRequest(newTitle, format, ignoreSelection, base64,
-							transparent, currentPage, scale, border, grid, includeXml);
+							transparent, currentPage, scale, border, grid, includeXml, pageRange);
 						this.editor.graph.pageVisible = prev;
 						
 						return req;
@@ -2044,7 +2048,7 @@
 	 * @param {number} dy Y-coordinate of the translation.
 	 */
 	EditorUi.prototype.createDownloadRequest = function(filename, format, ignoreSelection,
-		base64, transparent, currentPage, scale, border, grid, includeXml)
+		base64, transparent, currentPage, scale, border, grid, includeXml, pageRange)
 	{
 		var graph = this.editor.graph;
 		var bounds = graph.getGraphBounds();
@@ -2054,10 +2058,10 @@
 		// the from/to URL parameters to specify the page to be exported.
 		var data = this.getFileData(true, null, null, null, ignoreSelection,
 			currentPage == false ? false : format != 'xmlpng', null, null,
-			null, null, format == 'pdf');
+			null, false, format == 'pdf');
 		var range = '';
 		var allPages = '';
-		
+
 		if (bounds.width * bounds.height > MAX_AREA || data.length > MAX_REQUEST_SIZE)
 		{
 			throw {message: mxResources.get('drawingTooLarge')};
@@ -2065,9 +2069,16 @@
 		
 		var embed = (includeXml) ? '1' : '0';
        	
-		if (format == 'pdf' && currentPage == false)
+		if (format == 'pdf')
 		{
-			allPages = '&allPages=1';
+			if (pageRange != null)
+			{
+				allPages = '&from=' + pageRange.from + '&to=' + pageRange.to;
+			}
+			else if (currentPage == false)
+			{
+				allPages = '&allPages=1';
+			}
 		}
 		
        	if (format == 'xmlpng')
@@ -8997,7 +9008,7 @@
 		{
 			if (img != null && img.originalSrc != null)
 			{
-				img = ui.createImageForPageLink(img.originalSrc);
+				img = ui.createImageForPageLink(img.originalSrc, ui.currentPage);
 			}
 
 			graphSetBackgroundImage.apply(this, arguments);

+ 74 - 6
src/main/webapp/js/diagramly/Menus.js

@@ -407,19 +407,79 @@
 				};
 				
 				var dlgH = 180;
+				var pageCount = 1;
+				var currentPage = null;
 				
 				if (editorUi.pdfPageExport && !noPages)
 				{
 					var allPages = editorUi.addRadiobox(div, 'pages', mxResources.get('allPages'), true);
-					var currentPage = editorUi.addRadiobox(div, 'pages', mxResources.get('currentPage'), false);
+					var pagesRadio = editorUi.addRadiobox(div, 'pages', mxResources.get('pages') + ':', false, null, true);
+
+					var pagesFromInput = document.createElement('input');
+					pagesFromInput.style.cssText = 'margin:0 8px 0 8px;'
+					pagesFromInput.setAttribute('value', '1');
+					pagesFromInput.setAttribute('type', 'number');
+					pagesFromInput.setAttribute('min', '1');
+					pagesFromInput.style.width = '50px';
+					div.appendChild(pagesFromInput);
+					
+					var span = document.createElement('span');
+					mxUtils.write(span, mxResources.get('to'));
+					div.appendChild(span);
+					
+					var pagesToInput = pagesFromInput.cloneNode(true);
+					div.appendChild(pagesToInput);
+
+					mxEvent.addListener(pagesFromInput, 'focus', function()
+					{
+						pagesRadio.checked = true;
+					});
+					
+					mxEvent.addListener(pagesToInput, 'focus', function()
+					{
+						pagesRadio.checked = true;
+					});					
+
+					function validatePageRange()
+					{
+						pagesToInput.value = Math.max(1, Math.min(pageCount, Math.max(parseInt(pagesToInput.value), parseInt(pagesFromInput.value))));
+						pagesFromInput.value = Math.max(1, Math.min(pageCount, Math.min(parseInt(pagesToInput.value), parseInt(pagesFromInput.value))));
+					};
+					
+					mxEvent.addListener(pagesFromInput, 'change', validatePageRange);
+					mxEvent.addListener(pagesToInput, 'change', validatePageRange);
+					
+					if (editorUi.pages != null)
+					{
+						pageCount = editorUi.pages.length;
+			
+						if (editorUi.currentPage != null)
+						{
+							for (var i = 0; i < editorUi.pages.length; i++)
+							{
+								if (editorUi.currentPage == editorUi.pages[i])
+								{
+									currentPage = i + 1;
+									pagesFromInput.value = currentPage;
+									pagesToInput.value = currentPage;
+									break;
+								}
+							}
+						}
+					}
+					
+					pagesFromInput.setAttribute('max', pageCount);
+					pagesToInput.setAttribute('max', pageCount);
+					mxUtils.br(div);
+
 					var selection = editorUi.addRadiobox(div, 'pages', mxResources.get('selectionOnly'), false, graph.isSelectionEmpty());
 					var crop = editorUi.addCheckbox(div, mxResources.get('crop'), false, true);
 					var grid = editorUi.addCheckbox(div, mxResources.get('grid'), false, false);
 					
 					mxEvent.addListener(allPages, 'change', cropEnableFn);
-					mxEvent.addListener(currentPage, 'change', cropEnableFn);
+					mxEvent.addListener(pagesRadio, 'change', cropEnableFn);
 					mxEvent.addListener(selection, 'change', cropEnableFn);
-					dlgH += 60;
+					dlgH += 64;
 				}
 				else
 				{
@@ -456,12 +516,20 @@
 					
 					dlgH += 30;
 				}
-				
+
 				var dlg = new CustomDialog(editorUi, div, mxUtils.bind(this, function()
 				{
+					var from = parseInt(pagesFromInput.value);
+					var to = parseInt(pagesToInput.value);
+					var pageRange = (!allPages.checked &&
+						(from != currentPage || to != currentPage)) ?
+						{from: Math.max(0, Math.min(pageCount - 1, from - 1)),
+						to: Math.max(0, Math.min(pageCount - 1, to - 1))} : null;
+					
 					editorUi.downloadFile('pdf', null, null, !selection.checked,
-						noPages? true : !allPages.checked, !crop.checked, transparentBkg != null && transparentBkg.checked, null,
-						null, grid.checked, include != null && include.checked);
+						noPages? true : !allPages.checked && pageRange == null,
+						!crop.checked, transparentBkg != null && transparentBkg.checked, null,
+						null, grid.checked, include != null && include.checked, pageRange);
 				}), null, mxResources.get('export'));
 				editorUi.showDialog(dlg.container, 300, dlgH, true, true);
 			}

+ 2 - 2
src/main/webapp/js/diagramly/Pages.js

@@ -317,7 +317,7 @@ EditorUi.prototype.getPageById = function(id)
 /**
  * Returns the background image for the given page link.
  */
-EditorUi.prototype.createImageForPageLink = function(src)
+EditorUi.prototype.createImageForPageLink = function(src, sourcePage)
 {
 	var comma = src.indexOf(',');
 	var result = null;
@@ -326,7 +326,7 @@ EditorUi.prototype.createImageForPageLink = function(src)
 	{
 		var page = this.getPageById(src.substring(comma + 1));
 
-		if (page != null && page != this.currentPage)
+		if (page != null && page != sourcePage)
 		{
 			result = this.getImageForPage(page);
 			result.originalSrc = src;

+ 1 - 1
src/main/webapp/js/grapheditor/Editor.js

@@ -1625,7 +1625,7 @@ var PageSetupDialog = function(editorUi)
 
 		if (img != null && Graph.isPageLink(img.src))
 		{
-			img = editorUi.createImageForPageLink(img.src);
+			img = editorUi.createImageForPageLink(img.src, null);
 		}
 		
 		if (img != null && img.src != null)

+ 116 - 31
src/main/webapp/js/grapheditor/EditorUi.js

@@ -1178,22 +1178,19 @@ EditorUi.prototype.installShapePicker = function()
 			ui.hideShapePicker();
 		}
 	}));
-	
-	graph.addListener(mxEvent.ESCAPE, mxUtils.bind(this, function()
-	{
-		ui.hideShapePicker(true);
-	}));
-	
-	graph.getSelectionModel().addListener(mxEvent.CHANGE, mxUtils.bind(this, function()
-	{
-		ui.hideShapePicker(true);
-	}));
-	
-	graph.getModel().addListener(mxEvent.CHANGE, mxUtils.bind(this, function()
+
+	var hidePicker = mxUtils.bind(this, function()
 	{
 		ui.hideShapePicker(true);
-	}));
+	});
 	
+	graph.addListener('wheel', hidePicker);
+	graph.addListener(mxEvent.ESCAPE, hidePicker);
+	graph.view.addListener(mxEvent.SCALE, hidePicker);
+	graph.view.addListener(mxEvent.SCALE_AND_TRANSLATE, hidePicker);
+	graph.getSelectionModel().addListener(mxEvent.CHANGE, hidePicker);
+	graph.getModel().addListener(mxEvent.CHANGE, hidePicker);
+
 	// Counts as popup menu
 	var popupMenuHandlerIsMenuShowing = graph.popupMenuHandler.isMenuShowing;
 	 
@@ -1212,9 +1209,9 @@ EditorUi.prototype.installShapePicker = function()
 			if (cell == null && ui.sidebar != null && !mxEvent.isShiftDown(evt) &&
 				!graph.isCellLocked(graph.getDefaultParent()))
 			{
-				mxEvent.consume(evt);
 				var pt = mxUtils.convertPoint(this.container, mxEvent.getClientX(evt), mxEvent.getClientY(evt));
-				
+				mxEvent.consume(evt);
+
 				// Asynchronous to avoid direct insert after double tap
 				window.setTimeout(mxUtils.bind(this, function()
 				{
@@ -1227,9 +1224,10 @@ EditorUi.prototype.installShapePicker = function()
 			}
 		}
 	};
-	
+
 	if (this.hoverIcons != null)
 	{
+		this.hoverIcons.addListener('reset', hidePicker);
 		var hoverIconsDrag = this.hoverIcons.drag;
 		
 		this.hoverIcons.drag = function()
@@ -1282,22 +1280,105 @@ EditorUi.prototype.installShapePicker = function()
 				hoverIconsExecute.apply(this, arguments);
 			}
 		};
+
+		this.hoverIcons.addListener('focus', mxUtils.bind(this, function(sender, evt)
+		{
+			var arrow = evt.getProperty('arrow');
+			var dir = evt.getProperty('direction');
+			var mouseEvent = evt.getProperty('event');
+
+			var rect = arrow.getBoundingClientRect();
+			var offset = mxUtils.getOffset(graph.container);
+			var x = graph.container.scrollLeft + rect.x - offset.x;
+			var y = graph.container.scrollTop + rect.y - offset.y;
+
+			var temp = graph.getCompositeParent((this.hoverIcons.currentState != null) ?
+				this.hoverIcons.currentState.cell : null);
+			var div = ui.showShapePicker(x, y, temp, mxUtils.bind(this, function(cell)
+			{
+				if (cell != null)
+				{
+					graph.connectVertex(temp, dir, graph.defaultEdgeLength, mouseEvent, true, true, function(x, y, execute)
+					{
+						execute(cell);
+							
+						if (ui.hoverIcons != null)
+						{
+							ui.hoverIcons.update(graph.view.getState(cell));
+						}
+					}, function(cells)
+					{
+						graph.selectCellsForConnectVertex(cells);
+					}, mouseEvent, this.hoverIcons);
+				}
+			}), dir, true);
+
+			this.centerShapePicker(div, rect, x, y, dir);
+			mxUtils.setOpacity(div, 30);
+
+			mxEvent.addListener(div, 'mouseenter', function()
+			{
+				mxUtils.setOpacity(div, 100);
+			});
+
+			mxEvent.addListener(div, 'mouseleave', function()
+			{
+				ui.hideShapePicker();
+			});
+		}));
 	}
 };
 
 /**
  * Creates a temporary graph instance for rendering off-screen content.
  */
-EditorUi.prototype.showShapePicker = function(x, y, source, callback, direction)
+EditorUi.prototype.centerShapePicker = function(div, rect, x, y, dir)
+{
+	if (dir == mxConstants.DIRECTION_EAST || dir == mxConstants.DIRECTION_WEST)
+	{
+		div.style.width = '40px';
+	}
+
+	var r2 = div.getBoundingClientRect();
+
+	if (dir == mxConstants.DIRECTION_NORTH)
+	{
+		x -= r2.width / 2 - 10;
+		y -= r2.height + 6;
+	}
+	else if (dir == mxConstants.DIRECTION_SOUTH)
+	{
+		x -= r2.width / 2 - 10;
+		y += rect.height + 6;
+	}
+	else if (dir == mxConstants.DIRECTION_WEST)
+	{
+		x -= r2.width + 6;
+		y -= r2.height / 2 - 10;
+	}
+	else if (dir == mxConstants.DIRECTION_EAST)
+	{
+		x += rect.width + 6;
+		y -= r2.height / 2 - 10;
+	}
+
+	div.style.left = x + 'px';
+	div.style.top = y + 'px';
+};
+
+/**
+ * Creates a temporary graph instance for rendering off-screen content.
+ */
+EditorUi.prototype.showShapePicker = function(x, y, source, callback, direction, hovering)
 {
 	var div = this.createShapePicker(x, y, source, callback, direction, mxUtils.bind(this, function()
 	{	
 		this.hideShapePicker();
-	}), this.getCellsForShapePicker(source));
+	}), this.getCellsForShapePicker(source, hovering), hovering);
 	
 	if (div != null)
 	{
-		if (this.hoverIcons != null)
+		if (this.hoverIcons != null && !hovering)
 		{
 			this.hoverIcons.reset();
 		}
@@ -1311,12 +1392,14 @@ EditorUi.prototype.showShapePicker = function(x, y, source, callback, direction)
 		this.shapePickerCallback = callback;
 		this.shapePicker = div;
 	}
+
+	return div;
 };
 
 /**
  * Creates a temporary graph instance for rendering off-screen content.
  */
-EditorUi.prototype.createShapePicker = function(x, y, source, callback, direction, afterClick, cells)
+EditorUi.prototype.createShapePicker = function(x, y, source, callback, direction, afterClick, cells, hovering)
 {
 	var div = null;
 	
@@ -1332,12 +1415,16 @@ EditorUi.prototype.createShapePicker = function(x, y, source, callback, directio
 		
 		// Do not place entry under pointer for touch devices
 		var w = (cells.length < 6) ? cells.length * 35 : 140;
-		div.className = 'geToolbarContainer geSidebarContainer geSidebar';
+		div.className = 'geToolbarContainer geSidebarContainer';
 		div.style.cssText = 'position:absolute;left:' + x + 'px;top:' + y +
 			'px;width:' + w + 'px;border-radius:10px;padding:4px;text-align:center;' +
 			'box-shadow:0px 0px 3px 1px #d1d1d1;padding: 6px 0 8px 0;' +
 			'z-index: ' + mxPopupMenu.prototype.zIndex + 1 + ';';
-		mxUtils.setPrefixedStyle(div.style, 'transform', 'translate(-22px,-22px)');
+
+		if (!hovering)
+		{
+			mxUtils.setPrefixedStyle(div.style, 'transform', 'translate(-22px,-22px)');
+		}
 		
 		if (graph.background != null && graph.background != mxConstants.NONE)
 		{
@@ -1408,7 +1495,7 @@ EditorUi.prototype.createShapePicker = function(x, y, source, callback, directio
 			});
 		});
 		
-		for (var i = 0; i < cells.length; i++)
+		for (var i = 0; i < (hovering ? Math.min(cells.length, 4) : cells.length); i++)
 		{
 			addCell(cells[i]);
 		}
@@ -1436,7 +1523,7 @@ EditorUi.prototype.createShapePicker = function(x, y, source, callback, directio
 /**
  * Creates a temporary graph instance for rendering off-screen content.
  */
-EditorUi.prototype.getCellsForShapePicker = function(cell)
+EditorUi.prototype.getCellsForShapePicker = function(cell, hovering)
 {
 	var createVertex = mxUtils.bind(this, function(style, w, h, value)
 	{
@@ -1444,11 +1531,11 @@ EditorUi.prototype.getCellsForShapePicker = function(cell)
 	});
 	
 	return [(cell != null) ? this.editor.graph.cloneCell(cell) :
-			createVertex('text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;', 40, 20, 'Text'),
+			createVertex('text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;', 40, 20, 'Text'),
 		createVertex('whiteSpace=wrap;html=1;'),
-		createVertex('rounded=1;whiteSpace=wrap;html=1;'),
 		createVertex('ellipse;whiteSpace=wrap;html=1;'),
 		createVertex('rhombus;whiteSpace=wrap;html=1;', 80, 80),
+		createVertex('rounded=1;whiteSpace=wrap;html=1;'),
 		createVertex('shape=parallelogram;perimeter=parallelogramPerimeter;whiteSpace=wrap;html=1;fixedSize=1;'),
 		createVertex('shape=trapezoid;perimeter=trapezoidPerimeter;whiteSpace=wrap;html=1;fixedSize=1;', 120, 60),
 		createVertex('shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;', 120, 80),
@@ -1458,11 +1545,7 @@ EditorUi.prototype.getCellsForShapePicker = function(cell)
 		createVertex('shape=document;whiteSpace=wrap;html=1;boundedLbl=1;', 120, 80),
 		createVertex('shape=tape;whiteSpace=wrap;html=1;', 120, 100),
 		createVertex('ellipse;shape=cloud;whiteSpace=wrap;html=1;', 120, 80),
-		createVertex('shape=cylinder;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;', 60, 80),
-		createVertex('shape=callout;rounded=1;whiteSpace=wrap;html=1;perimeter=calloutPerimeter;', 120, 80),
-		createVertex('shape=doubleArrow;whiteSpace=wrap;html=1;arrowWidth=0.4;arrowSize=0.3;'),
 		createVertex('shape=singleArrow;whiteSpace=wrap;html=1;arrowWidth=0.4;arrowSize=0.4;', 80, 60),
-		createVertex('shape=singleArrow;whiteSpace=wrap;html=1;arrowWidth=0.4;arrowSize=0.4;flipH=1;', 80, 60),
 		createVertex('shape=waypoint;sketch=0;size=6;pointerEvents=1;points=[];fillColor=none;resizable=0;rotatable=0;perimeter=centerPerimeter;snapToPoint=1;', 40, 40)];
 };
 
@@ -2774,6 +2857,8 @@ EditorUi.prototype.initCanvas = function()
 	
 	mxEvent.addMouseWheelListener(mxUtils.bind(this, function(evt, up, force, cx, cy)
 	{
+		graph.fireEvent(new mxEventObject('wheel'));
+
 		if (this.dialogs == null || this.dialogs.length == 0)
 		{
 			// Scrolls with scrollbars turned off
@@ -3427,7 +3512,7 @@ ChangePageSetup.prototype.execute = function()
 
 		if (img != null && img.src != null && img.src.substring(0, 13) == 'data:page/id,')
 		{
-			img = this.ui.createImageForPageLink(img.src);
+			img = this.ui.createImageForPageLink(img.src, this.ui.currentPage);
 		}
 
 		this.ui.setBackgroundImage(img);

+ 1 - 1
src/main/webapp/js/grapheditor/Format.js

@@ -1179,7 +1179,7 @@ BaseFormatPanel.prototype.createColorOption = function(label, getColorFn, setCol
 	btn.style.display = (cb.checked || hideCheckbox) ? '' : 'none';
 	div.appendChild(btn);
 
-	var clr = (value != null && value.charAt(0) == '#') ? value.substring(1).toUpperCase() : value;
+	var clr = (value != null && typeof value === 'string' && value.charAt(0) == '#') ? value.substring(1).toUpperCase() : value;
 	var name = ColorDialog.prototype.colorNames[clr];
 	btn.setAttribute('title', (name != null) ? name + ' (' + title + ')' : title);
 

+ 50 - 27
src/main/webapp/js/grapheditor/Graph.js

@@ -4726,10 +4726,14 @@ Graph.prototype.zapGremlins = function(text)
  */
 HoverIcons = function(graph)
 {
+	mxEventSource.call(this);
 	this.graph = graph;
 	this.init();
 };
 
+// Extends mxEventSource
+mxUtils.extend(HoverIcons, mxEventSource);
+
 /**
  * Up arrow.
  */
@@ -4827,10 +4831,10 @@ HoverIcons.prototype.tolerance = (mxClient.IS_TOUCH) ? 6 : 0;
  */
 HoverIcons.prototype.init = function()
 {
-	this.arrowUp = this.createArrow(this.triangleUp, mxResources.get('plusTooltip'));
-	this.arrowRight = this.createArrow(this.triangleRight, mxResources.get('plusTooltip'));
-	this.arrowDown = this.createArrow(this.triangleDown, mxResources.get('plusTooltip'));
-	this.arrowLeft = this.createArrow(this.triangleLeft, mxResources.get('plusTooltip'));
+	this.arrowUp = this.createArrow(this.triangleUp, mxResources.get('plusTooltip'), mxConstants.DIRECTION_NORTH);
+	this.arrowRight = this.createArrow(this.triangleRight, mxResources.get('plusTooltip'), mxConstants.DIRECTION_EAST);
+	this.arrowDown = this.createArrow(this.triangleDown, mxResources.get('plusTooltip'), mxConstants.DIRECTION_SOUTH);
+	this.arrowLeft = this.createArrow(this.triangleLeft, mxResources.get('plusTooltip'), mxConstants.DIRECTION_WEST);
 
 	this.elts = [this.arrowUp, this.arrowRight, this.arrowDown, this.arrowLeft];
 
@@ -4997,7 +5001,7 @@ HoverIcons.prototype.isResetEvent = function(evt, allowShift)
 /**
  * 
  */
-HoverIcons.prototype.createArrow = function(img, tooltip)
+HoverIcons.prototype.createArrow = function(img, tooltip, direction)
 {
 	var arrow = null;
 	arrow = mxUtils.createImage(img.src);
@@ -5042,11 +5046,20 @@ HoverIcons.prototype.createArrow = function(img, tooltip)
 			this.graph.connectionHandler.constraintHandler.reset();
 			mxUtils.setOpacity(arrow, 100);
 			this.activeArrow = arrow;
+
+			this.fireEvent(new mxEventObject('focus', 'arrow', arrow,
+				'direction', direction, 'event', evt));
 		}
 	}));
 	
 	mxEvent.addListener(arrow, 'mouseleave', mxUtils.bind(this, function(evt)
 	{
+		if (mxEvent.isMouseEvent(evt))
+		{
+			this.fireEvent(new mxEventObject('blur', 'arrow', arrow,
+				'direction', direction, 'event', evt));
+		}
+
 		// Workaround for IE11 firing this event on touch
 		if (!this.graph.isMouseDown)
 		{
@@ -5237,6 +5250,8 @@ HoverIcons.prototype.reset = function(clearTimeout)
 	this.activeArrow = null;
 	this.removeNodes();
 	this.bbox = null;
+
+	this.fireEvent(new mxEventObject('reset'));
 };
 
 /**
@@ -9034,33 +9049,41 @@ if (typeof mxVertexHandler != 'undefined')
 				for (var i = 0; i < cells.length; i++)
 				{
 					var parent = model.getParent(cells[i]);
-					var child = this.moveCells([clones[i]], s, s, false)[0];
-					select.push(child);
-					
-					if (append)
-					{
-						model.add(parent, clones[i]);
-					}
-					else
-					{
-						// Maintains child index by inserting after clone in parent
-						var index = parent.getIndex(cells[i]);
-						model.add(parent, clones[i], index + 1);
-					}
-					
-					// Extends tables	
-					if (this.isTable(parent))
+
+					if (parent != null)
 					{
-						var row = this.getCellGeometry(clones[i]);
-						var table = this.getCellGeometry(parent);
+						var child = this.moveCells([clones[i]], s, s, false)[0];
+						select.push(child);
+						
+						if (append)
+						{
+							model.add(parent, clones[i]);
+						}
+						else
+						{
+							// Maintains child index by inserting after clone in parent
+							var index = parent.getIndex(cells[i]);
+							model.add(parent, clones[i], index + 1);
+						}
 						
-						if (row != null && table != null)
+						// Extends tables	
+						if (this.isTable(parent))
 						{
-							table = table.clone();
-							table.height += row.height;
-							model.setGeometry(parent, table);
+							var row = this.getCellGeometry(clones[i]);
+							var table = this.getCellGeometry(parent);
+							
+							if (row != null && table != null)
+							{
+								table = table.clone();
+								table.height += row.height;
+								model.setGeometry(parent, table);
+							}
 						}
 					}
+					else
+					{
+						select.push(clones[i]);
+					}
 				}
 
 				// Updates custom links after inserting into the model for cells to have new IDs

File diff suppressed because it is too large
+ 376 - 371
src/main/webapp/js/viewer-static.min.js


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


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


+ 9 - 9
src/main/webapp/resources/dia_zh.txt

@@ -111,7 +111,7 @@ diagramPngDesc=可编辑位图文件
 diagramSvgDesc=可编辑矢量图文件
 didYouMeanToExportToPdf=是否要导出到 PDF?
 draftFound={1} 的草稿已经找到。将其加载到编辑器或将其丢弃以继续。
-draftRevisionMismatch=There is a different version of this diagram on a shared draft of this page. Please edit the diagram from the draft to ensure you are working with the latest version.
+draftRevisionMismatch=此页面上的共享草稿有一个不同的版本。请从草稿编辑以确保您正在使用最新版本。
 selectDraft=选择一个草稿继续编辑:
 dragAndDropNotSupported=暂不支持图片拖放功能。是否要使用导入?
 dropboxCharsNotAllowed=系统不允许使用下列字符:\ / : ? * " |
@@ -766,7 +766,7 @@ updatingSelection=选择更新中。请稍候...
 upload=上传
 url=URL
 useOffline=离线使用
-useRootFolder=Use root folder?
+useRootFolder=要使用根目录吗?
 userManual=用户手册
 vertical=垂直
 verticalFlow=垂直流
@@ -861,7 +861,7 @@ markedAsResolved=标记为已解决
 noCommentsFound=找不到评论
 comments=评论
 timeAgo={1} 之前
-confluenceCloud=Confluence Cloud
+confluenceCloud=Confluence 
 libraries=库
 confAnchor=Confluence 页面锚点
 confTimeout=连接超时
@@ -876,9 +876,9 @@ confExtEditNotPossible=绘图无法在外部编辑. 请尝试在编辑页面时
 confEditedExt=绘图/页面 已在外部编辑
 diagNotFound=绘图找不到
 confEditedExtRefresh=绘图/页面 已在外部编辑. 请刷新页面.
-confCannotEditDraftDelOrExt=Cannot edit diagrams in a draft page, diagram is deleted from the page, or diagram is edited externally. Please check the page.
+confCannotEditDraftDelOrExt=无法在草稿页面中进行编辑, 该绘图已经在其他页面删除或外部编辑. 请检查页面.
 retBack=Return back
-confDiagNotPublished=The diagram does not belong to a published page
+confDiagNotPublished=改绘图不属于已发布的页面
 createdByDraw=使用 draw.io 创建
 filenameShort=文件名太短
 invalidChars=无效的字符
@@ -887,13 +887,13 @@ draftReadErr=草稿读取错误
 diagCantLoad=无法加载绘图
 draftWriteErr=草稿写入错误
 draftCantCreate=无法创建草稿
-confDuplName=Duplicate diagram name detected. Please pick another name.
-confSessionExpired=Looks like your session expired. Log in again to keep working.
+confDuplName=检测到重复的绘图名. 请重新选择.
+confSessionExpired=似乎登陆已经过期. 重新登录以避免中断.
 login=登录
 drawPrev=draw.io 预览
 drawDiag=draw.io 绘图
-invalidCallFnNotFound=Invalid Call: {1} not found
-invalidCallErrOccured=Invalid Call: An error occurred, {1}
+invalidCallFnNotFound=无效调用: 无法找到 {1}
+invalidCallErrOccured=无效调用: 检测到错误, {1}
 anonymous=匿名
 confGotoPage=Go to containing page
 showComments=显示评论

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


File diff suppressed because it is too large
+ 1 - 1
src/main/webapp/service-worker.js.map