소스 검색

12.1.4 release

David Benson [draw.io] 5 년 전
부모
커밋
d42ae42d6c

+ 8 - 0
ChangeLog

@@ -1,3 +1,11 @@
+23-OCT-2019: 12.1.4
+
+- Fixes scrollbars in Confluence Diagram Viewer
+- Fixes embed dialog for Google Shared Drives
+- Fixes snap to grid while panning
+- Adds paging for GitLab picker
+- Uses mxGraph 4.0.6 beta 1
+
 17-OCT-2019: 12.1.3
 
 - Fixes Google Drive auth in Office plugin

+ 1 - 1
VERSION

@@ -1 +1 @@
-12.1.3
+12.1.4

+ 25 - 48
etc/docker/README.md

@@ -1,58 +1,38 @@
-This guide is based on fjudith/draw.io docker image [docker-draw.io repository](https://github.com/jgraph/docker-draw.io)
+Our collection of docker images and docker-compose files are in [jgraph/docker-drawio repository](https://github.com/jgraph/docker-drawio)
 
-#HTTPS SSL Certificate via Let's Encrypt
+In that repository:
 
-###Prerequisites:
+* draw.io docker image that is always up-to-date with draw.io releases
+* draw.io export server image which allow exporting draw.io diagrams to pdf and images
+* docker-compose to run draw.io with the export server
+* docker-compose to run draw.io integrated within nextcloud
+* docker-compose to run draw.io with PlantUML support
+* docker-compose to run draw.io self-contained without any dependency on draw.io website (with the export server, plantUml, Google Drive support, OneDrive support, and EMF conversion support (for VSDX export)
 
-1. A Linux machine connected to the Internet with ports 443 and 80 open
-1. A domain/subdomain name pointing to this machine's IP address. (e.g., drawio.example.com)
-
-###Method:
-
-1. Using fjudith/draw.io docker image, run the following command
-`docker run -it -m1g -e LETS_ENCRYPT_ENABLED=true -e PUBLIC_DNS=drawio.example.com --rm --name="draw" -p 80:80 -p 443:8443 fjudith/draw.io`
-Notice that mapping port 80 to container's port 80 allows certbot to work in stand-alone mode. Mapping port 443 to container's port 8443 allows the container tomcat to serve https requests directly.
-
-#Updating draw.io in a running container
-
-##Method 1 (Using the host machine to build draw.io):
-
-###Prerequisites:
 
-1. Host machine must have wget, unzip and ant installed
-1. Assumptions: docker container is named 'draw' and $CATALINA_HOME in the container is '/usr/local/tomcat/'
+# HTTPS SSL Certificate via Let's Encrypt
 
-###Method:
+### Prerequisites:
 
-1. `wget https://github.com/jgraph/draw.io/archive/v{$version}.zip` For example, `wget https://github.com/jgraph/draw.io/archive/v11.2.9.zip`
-1. `unzip v11.2.9.zip`
-1. `cd drawio-11.2.9/etc/build/`
-1. `ant war`
-1. `cd ../../build/`
-1. `unzip draw.war -d draw`
-1. `docker cp draw draw:/usr/local/tomcat/webapps/`
-
-##Method 2 (build a new docker image with the new version)
+1. A Linux machine connected to the Internet with ports 443 and 80 open
+1. A domain/subdomain name pointing to this machine's IP address. (e.g., drawio.example.com)
 
-1. Download the VERSION file to get the latest draw.io version `wget https://raw.githubusercontent.com/jgraph/drawio/master/VERSION`
-1. Build the new docker image `docker build --build-arg VERSION=`cat VERSION` -t fjudith/draw.io .`
-1. Run the new image instead of the old one
+### Method:
 
-#Changing draw.io configuration
+1. Using jgraph/drawio docker image, run the following command
+`docker run -it -m1g -e LETS_ENCRYPT_ENABLED=true -e PUBLIC_DNS=drawio.example.com --rm --name="draw" -p 80:80 -p 443:8443 jgraph/drawio`
+Notice that mapping port 80 to container's port 80 allows certbot to work in stand-alone mode. Mapping port 443 to container's port 8443 allows the container tomcat to serve https requests directly.
 
-##Method 1 (Build you custom image with setting pre-loaded)
+# Changing draw.io configuration
 
-1. Create you PreConfig.js & PostConfig.js files using templates found in ⁨src⁩/⁨main⁩/⁨webapp⁩/⁨js⁩/PreConfig.js & PostConfig.js
-1. Add your configuration files (PreConfig.js & PostConfig.js) next to Dockerfile
-1. Add the following line after line 7 in Dockerfile
+## Method 1 (Build you custom image with setting pre-loaded)
 
-```
-COPY PreConfig.js PostConfig.js $CATALINA_HOME/webapps/draw/js/
-```
+1. Edit PreConfig.js & PostConfig.js files (next to Dockerfile in debian or alpine folders)
+1. Build the docker image
 
-##Method 2 (Using existing running docker container)
+## Method 2 (Using existing running docker container)
 
-1. Create you PreConfig.js & PostConfig.js files using templates found in ⁨src⁩/⁨main⁩/⁨webapp⁩/⁨js⁩/PreConfig.js & PostConfig.js
+1. Edit PreConfig.js & PostConfig.js files (next to Dockerfile in debian or alpine folders)
 1. Copy these files to docker container 
 
 ```
@@ -60,17 +40,14 @@ docker cp PreConfig.js draw:/usr/local/tomcat/webapps/draw/js/
 docker cp PostConfig.js draw:/usr/local/tomcat/webapps/draw/js/
 ```
 
-##Method 3 (Bind configuration files into the container when started)
+## Method 3 (Bind configuration files into the container when started)
 
 1. This method allows changing the configuration files directly on the host without invoking any other docker commands. It can be used for testing
-1. Create you PreConfig.js & PostConfig.js files using templates found in ⁨src⁩/⁨main⁩/⁨webapp⁩/⁨js⁩/PreConfig.js & PostConfig.js
+1. Edit PreConfig.js & PostConfig.js files (next to Dockerfile in debian or alpine folders)
 1. From within the directory that contained the configuration files, run the following command to start docker container
+1. Note: self-contained docker-compose file already mount the configuration files into the container
 
 ```
 docker run -it  --rm --name="draw" --mount type=bind,source="$(pwd)"/PreConfig.js,target=/usr/local/tomcat/webapps/draw/js/PreConfig.js --mount type=bind,source="$(pwd)"/PostConfig.js,target=/usr/local/tomcat/webapps/draw/js/PostConfig.js -p 8080:8080 -p 8443:8443 fjudith/draw.io
 ```
 
-#draw.io with local export server
-
-Use the docker image from `drawio-export` folder. This image is much larger since it requires Node.js in addition to Puppeteer (which requires many dependencies similar to Chromium)
-

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 8 - 8
etc/mxgraph/mxClient.js


+ 25 - 0
src/main/java/com/mxgraph/io/gliffy/importer/GliffyDiagramConverter.java

@@ -17,6 +17,8 @@ import java.util.logging.Logger;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.commons.lang3.tuple.Pair;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
@@ -75,6 +77,10 @@ public class GliffyDiagramConverter
 
 	private Pattern rotationPattern = Pattern.compile("rotation=(\\-?\\w+)");
 	
+	private static Pattern lightboxPageIdGliffyMigration =  Pattern.compile("pageId=(.*?)(?=&)");
+	
+	private static Pattern lightboxNameGliffyMigration =  Pattern.compile("name=(.*?)(?=&)");
+	
 	private StringBuilder report;
 
 	/**
@@ -1003,6 +1009,11 @@ public class GliffyDiagramConverter
 		{
 			Document doc = mxDomUtils.createDocument();
 			Element uo = doc.createElement("UserObject");
+
+			Pair<Long, String> lightBox = extractLightboxDataFromGliffyUrl(link);
+			if (lightBox != null) {
+				link = "/plugins/drawio/lightbox.action?ceoId=" + lightBox.getKey() + "&diagramName=" + lightBox.getValue() + ".drawio";
+			}
 			uo.setAttribute("link", link);
 			drawioDiagram.getModel().setValue(cell, uo);
 
@@ -1058,6 +1069,20 @@ public class GliffyDiagramConverter
 		style.replace(start, end, correctValue);
 	}
 
+	public Pair<Long, String> extractLightboxDataFromGliffyUrl(String link) {
+		Matcher pagem = lightboxPageIdGliffyMigration.matcher(link);
+		Matcher namem = lightboxNameGliffyMigration.matcher(link);
+		if (pagem.find())
+		{
+			 Long oldPageId = Long.parseLong(pagem.group(1));
+			 if (namem.find()) {
+				 String oldDiagramName = namem.group(1);
+				 return new ImmutablePair<Long, String>(oldPageId, oldDiagramName);
+			 }
+		}
+		return null;
+	}
+	
 	public StringBuilder getReport()
 	{
 		return report;

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

@@ -1,7 +1,7 @@
 CACHE MANIFEST
 
 # THIS FILE WAS GENERATED. DO NOT MODIFY!
-# 10/17/2019 10:47 AM
+# 10/23/2019 01:46 PM
 
 app.html
 index.html?offline=1

+ 18 - 8
src/main/webapp/export3.html

@@ -79,7 +79,7 @@
 			// Parses XML
 			var doc = mxUtils.parseXml(data.xml);
 			var node = Editor.extractGraphModel(doc.documentElement, true);
-			
+
 			if (node == null)
 			{
 				//Electron pdf export
@@ -331,7 +331,7 @@
 				}
 				
 				//handle layers
-				if (extras != null && extras.layers != null)
+				if (extras != null && (extras.layers != null || extras.layerIds != null))
 				{
 					var childCount = model.getChildCount(model.root);
 					
@@ -340,14 +340,24 @@
 					{
 						model.setVisible(model.getChildAt(model.root, i), false);
 					}
-					
-					for (var i = 0; i < extras.layers.length; i++)
+
+					if (extras.layerIds != null)
 					{
-						var layer = model.getChildAt(model.root, extras.layers[i]);
-						
-						if (layer != null)
+						for (var i = 0; i < extras.layerIds.length; i++)
 						{
-							model.setVisible(layer, true);
+							model.setVisible(model.getCell(extras.layerIds[i]), true);
+						}
+					}
+					else
+					{
+						for (var i = 0; i < extras.layers.length; i++)
+						{
+							var layer = model.getChildAt(model.root, extras.layers[i]);
+							
+							if (layer != null)
+							{
+								model.setVisible(layer, true);
+							}
 						}
 					}
 				}

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1307 - 1295
src/main/webapp/js/app.min.js


+ 214 - 0
src/main/webapp/js/diagramly/Dialogs.js

@@ -9881,3 +9881,217 @@ var BtnDialog = function(editorUi, peer, btnLbl, fn)
 	
 	this.container = div;
 };
+
+/**
+ * Aspect Dialog
+ * @module drawio/aspect-dialog
+ */
+function AspectDialog(editorUi, pageId, layerIds, okFn, cancelFn)
+{
+	this.aspect = {pageId : pageId || editorUi.pages[0].getId(), layerIds : layerIds || []};
+	var div = document.createElement('div');
+	
+	var title = document.createElement('h5');
+	title.style.margin = '0 0 10px';
+	mxUtils.write(title, mxResources.get('pages'));
+	div.appendChild(title);
+
+	var pagesContainer = document.createElement('div');
+	pagesContainer.className = 'geAspectDlgList';
+	div.appendChild(pagesContainer);
+
+	title = document.createElement('h5');
+	title.style.margin = '0 0 10px';
+	mxUtils.write(title, mxResources.get('layers'));
+	div.appendChild(title);
+
+	var layersContainer = document.createElement('div');
+	layersContainer.className = 'geAspectDlgList';
+	div.appendChild(layersContainer);
+	
+	this.pagesContainer = pagesContainer;
+	this.layersContainer = layersContainer;
+	this.ui = editorUi;
+	
+	var btns = document.createElement('div');
+	btns.style.marginTop = '16px';
+	btns.style.textAlign = 'center';
+	
+	var cancelBtn = mxUtils.button(mxResources.get('cancel'), function()
+	{
+		editorUi.hideDialog();
+		
+		if (cancelFn != null)
+		{
+			cancelFn();
+		}
+	});
+	
+	cancelBtn.className = 'geBtn';
+	
+	if (editorUi.editor.cancelFirst)
+	{
+		btns.appendChild(cancelBtn);
+	}
+
+	var okBtn = mxUtils.button(mxResources.get('ok'), mxUtils.bind(this, function()
+	{
+		editorUi.hideDialog();
+		okFn({pageId: this.selectedPage, layerIds: Object.keys(this.selectedLayers)});
+	}));
+
+	btns.appendChild(okBtn);
+	okBtn.className = 'geBtn gePrimaryBtn';
+	
+	if (!editorUi.editor.cancelFirst)
+	{
+		btns.appendChild(cancelBtn);
+	}
+
+	okBtn.setAttribute('disabled', 'disabled');
+	this.okBtn = okBtn;
+	div.appendChild(btns);
+	this.container = div;
+};
+
+//Drawing the graph with dialog not visible doesn't get dimensions right. It has to be visible!
+AspectDialog.prototype.init = function()
+{
+	for (var i = 0; i < this.ui.pages.length; i++)
+	{
+		var page = this.ui.updatePageRoot(this.ui.pages[i]);
+
+		this.createPageItem(page.getId(), page.getName(), page.node, page.root);
+	}
+};
+
+AspectDialog.prototype.createViewer = function(container, pageNode, layerId)
+{
+	mxEvent.disableContextMenu(container);
+	container.style.userSelect = 'none';
+
+	var graph = new Graph(container);
+	graph.setTooltips(false);
+	graph.setEnabled(false);
+	graph.setPanning(false);
+	graph.minFitScale = null;
+	graph.maxFitScale = null;
+	graph.centerZoom = true;
+	
+	var node = pageNode.firstElementChild;
+	
+	if (node != null)
+	{
+		var bg = node.getAttribute('background');
+		
+		if (bg == null || bg == '' || bg == mxConstants.NONE)
+		{
+			bg = '#ffffff';
+		}
+		
+		container.style.backgroundColor = bg;
+		
+		var codec = new mxCodec(node.ownerDocument);
+		var model = graph.getModel();
+		codec.decode(node, model);
+		
+		var childCount = model.getChildCount(model.root);
+		
+		var showAll = layerId == null;
+		
+		// handle layers visibility
+		for (var i = 0; i < childCount; i++)
+		{
+			var child = model.getChildAt(model.root, i);
+			model.setVisible(child, showAll || layerId == child.id);
+		}
+
+		graph.maxFitScale = 1;
+		graph.fit(0);
+		graph.center();
+	}
+	
+	return graph;
+};
+
+AspectDialog.prototype.createPageItem = function(pageId, pageName, pageNode, pageRoot)
+{
+	var $listItem = document.createElement('div');
+	$listItem.className = 'geAspectDlgListItem';
+	$listItem.setAttribute('data-page-id', pageId)
+	$listItem.innerHTML = '<div style="max-width: 100%; max-height: 100%;"></div><div class="geAspectDlgListItemText">' + pageName + '</div>';
+	
+	this.pagesContainer.appendChild($listItem);
+	
+	var graph = this.createViewer($listItem.childNodes[0], pageNode);
+	
+	var onClick = mxUtils.bind(this, function()
+	{
+		if (this.selectedItem != null)
+		{
+			this.selectedItem.className = 'geAspectDlgListItem';
+		}
+		
+		this.selectedItem = $listItem;
+		this.selectedPage = pageId;
+		$listItem.className += ' geAspectDlgListItemSelected';
+		this.layersContainer.innerHTML = '';
+		this.selectedLayers = {};
+		this.okBtn.setAttribute('disabled', 'disabled');
+		
+		var graphModel = graph.model;
+		var layers = graphModel.getChildCells(graphModel.getRoot());
+		
+		for (var i = 0; i < layers.length; i++) 
+		{
+			this.createLayerItem(layers[i], pageId, graph, pageNode);
+		}
+	});
+	
+	mxEvent.addListener($listItem, 'click', onClick);
+	
+	if(this.aspect.pageId == pageId) 
+	{
+		onClick();
+	}
+};
+
+AspectDialog.prototype.createLayerItem = function(layer, pageId, graph, pageNode)
+{
+	var graphModel = graph.model;
+	var layerName = graph.convertValueToString(layer) || (mxResources.get('background') || 'Background');
+	var $listItem = document.createElement('div');
+	$listItem.setAttribute('data-layer-id', layer.id);
+	$listItem.className = 'geAspectDlgListItem';
+	$listItem.innerHTML = '<div style="max-width: 100%; max-height: 100%;"></div><div class="geAspectDlgListItemText">' + layerName + '</div>';
+	this.layersContainer.appendChild($listItem);
+	
+	this.createViewer($listItem.childNodes[0], pageNode, layer.id);
+
+	var onClick = mxUtils.bind(this, function()
+	{
+		if ($listItem.className.indexOf('geAspectDlgListItemSelected') >= 0) //Selected
+		{
+			$listItem.className = 'geAspectDlgListItem';
+			delete this.selectedLayers[layer.id];
+			
+			if (Object.keys(this.selectedLayers).length == 0)
+			{
+				this.okBtn.setAttribute('disabled', 'disabled');
+			}
+		}
+		else
+		{
+			$listItem.className += ' geAspectDlgListItemSelected';
+			this.selectedLayers[layer.id] = true;
+			this.okBtn.removeAttribute('disabled');
+		}
+	});
+	
+	mxEvent.addListener($listItem, 'click', onClick);
+	
+	if(this.aspect.layerIds.indexOf(layer.id) != -1) 
+	{
+		onClick();
+	}
+};

+ 4 - 1
src/main/webapp/js/diagramly/DriveFile.js

@@ -78,7 +78,7 @@ DriveFile.prototype.getMode = function()
 DriveFile.prototype.getPublicUrl = function(fn)
 {
 	this.ui.drive.executeRequest({
-		url: '/files/' + this.desc.id + '/permissions'
+		url: '/files/' + this.desc.id + '/permissions?supportsTeamDrives=true'
 	}, 
 	mxUtils.bind(this, function(resp)
 	{
@@ -97,6 +97,9 @@ DriveFile.prototype.getPublicUrl = function(fn)
 		}
 		
 		fn(null);
+	}), mxUtils.bind(this, function()
+	{
+		fn(null)
 	}));
 };
 

+ 36 - 5
src/main/webapp/js/diagramly/EditorUi.js

@@ -8838,7 +8838,7 @@
 		    					{
 		    						graph.getModel().endUpdate();
 		    					}
-		    				}, function(err)
+		    				}, function(e)
 		    				{
 		    					ui.handleError(e);
 		    				});
@@ -11131,16 +11131,27 @@
 						   	    postDataBack(uri);
 							});
 					
+							var pageId = data.pageId || (this.pages != null? this.pages[0].getId() : null);
+							
 							// LATER: Uses external export if current page (not first page) has mathEnabled
 							if (this.isExportToCanvas())
 							{
-								// Exports PNG for first page while other page is visible by creating a graph
+								// Exports PNG for first/specific page while other page is visible by creating a graph
 								// LATER: Add caching for the graph or SVG while not on first page
-								if (this.pages != null && this.currentPage != this.pages[0])
+								if (this.pages != null && this.currentPage.getId() != pageId)
 								{
 									var graphGetGlobalVariable = graph.getGlobalVariable;
 									graph = this.createTemporaryGraph(graph.getStylesheet());
-									var page = this.pages[0];
+									var page;
+									
+									for (var i = 0; i < this.pages.length; i++)
+									{
+										if (this.pages[i].getId() == pageId)
+										{
+											page = this.updatePageRoot(this.pages[i]);
+											break;
+										}
+									}
 							
 									graph.getGlobalVariable = function(name)
 									{
@@ -11160,6 +11171,24 @@
 									graph.model.setRoot(page.root);
 								}
 
+								//Set visible layers based on message setting
+								if (data.layerIds != null)
+								{
+									var graphModel = graph.model;
+									var layers = graphModel.getChildCells(graphModel.getRoot());
+									var layerIdsMap = {};
+									
+									for (var i = 0; i < data.layerIds.length; i++)
+									{
+										layerIdsMap[data.layerIds[i]] = true;
+									}
+
+									for (var i = 0; i < layers.length; i++)
+									{
+										graphModel.setVisible(layers[i], layerIdsMap[layers[i].id] || false);
+									}
+								}
+								
 								this.exportToCanvas(mxUtils.bind(this, function(canvas)
 							   	{
 									processUri(canvas.toDataURL('image/png'));
@@ -11174,8 +11203,10 @@
 								// Double encoding for XML arg is needed for UTF8 encoding
 						       	var req = new mxXmlRequest(EXPORT_URL, 'format=png&embedXml=' +
 						       		((data.format == 'xmlpng') ? '1' : '0') + 
+						       		(pageId != null? '&pageId=' + pageId : '') +
+						       		(data.layerIds != null? '&extras=' + encodeURIComponent(JSON.stringify({layerIds: data.layerIds})) : '') +
 						       		(data.scale != null? '&scale=' + data.scale : '') +'&base64=1&xml=' +
-						       		encodeURIComponent(encodeURIComponent(xml)));
+						       		encodeURIComponent(xml));
 
 								req.send(mxUtils.bind(this, function(req)
 								{

+ 87 - 25
src/main/webapp/js/diagramly/GitHubClient.js

@@ -888,34 +888,74 @@ GitHubClient.prototype.showGitHubDialog = function(showFiles, fn)
 		}));
 	});
 	
-	var selectFile = mxUtils.bind(this, function()
+	// Adds paging for repos, branches and files (files limited to 1000 by API)
+	var nextPageDiv = null;
+	var scrollFn = null;
+	var pageSize = 100;
+
+	var selectFile = mxUtils.bind(this, function(page)
 	{
+		if (page == null)
+		{
+			div.innerHTML = '';
+			page = 1;
+		}
+		
 		var req = new mxXmlRequest(this.baseUrl + '/repos/' + org + '/' + repo +
-				'/contents/' + path + '?ref=' + encodeURIComponent(ref), null, 'GET');
-		dlg.okButton.removeAttribute('disabled');
-		div.innerHTML = '';
+				'/contents/' + path + '?ref=' + encodeURIComponent(ref) +
+				'&per_page=' + pageSize + '&page=' + page, null, 'GET');
 		this.ui.spinner.spin(div, mxResources.get('loading'));
+		dlg.okButton.removeAttribute('disabled');
+		
+		if (scrollFn != null)
+		{
+			mxEvent.removeListener(div, 'scroll', scrollFn);
+			scrollFn = null;
+		}
+		
+		if (nextPageDiv != null && nextPageDiv.parentNode != null)
+		{
+			nextPageDiv.parentNode.removeChild(nextPageDiv);
+		}
+		
+		nextPageDiv = document.createElement('a');
+		nextPageDiv.style.display = 'block';
+		nextPageDiv.setAttribute('href', 'javascript:void(0);');
+		mxUtils.write(nextPageDiv, mxResources.get('more') + '...');
+		
+		var nextPage = mxUtils.bind(this, function()
+		{
+			selectFile(page + 1);
+		});
+		
+		mxEvent.addListener(nextPageDiv, 'click', nextPage);
 		
 		this.executeRequest(req, mxUtils.bind(this, function(req)
 		{
-			updatePathInfo();
 			this.ui.spinner.stop();
-			var files = JSON.parse(req.getText());
-			div.appendChild(createLink('../ [Up]', mxUtils.bind(this, function()
+			
+			if (page == 1)
 			{
-				if (path == '')
-				{
-					path = null;
-					selectRepo();
-				}
-				else
+				updatePathInfo();
+				
+				div.appendChild(createLink('../ [Up]', mxUtils.bind(this, function()
 				{
-					var tokens = path.split('/');
-					path = tokens.slice(0, tokens.length - 1).join('/');
-					selectFile();
-				}
-			}), '4px'));
+					if (path == '')
+					{
+						path = null;
+						selectRepo();
+					}
+					else
+					{
+						var tokens = path.split('/');
+						path = tokens.slice(0, tokens.length - 1).join('/');
+						selectFile();
+					}
+				}), '4px'));
+			}
 
+			var files = JSON.parse(req.getText());
+			
 			if (files == null || files.length == 0)
 			{
 				mxUtils.write(div, mxResources.get('noFiles'));
@@ -923,6 +963,7 @@ GitHubClient.prototype.showGitHubDialog = function(showFiles, fn)
 			else
 			{
 				var gray = true;
+				var count = 0;
 				
 				var listFiles = mxUtils.bind(this, function(showFolders)
 				{
@@ -959,6 +1000,7 @@ GitHubClient.prototype.showGitHubDialog = function(showFiles, fn)
 								})));
 								
 								div.appendChild(temp);
+								count++;
 							}
 						}))(files[i], i);
 					}
@@ -970,14 +1012,25 @@ GitHubClient.prototype.showGitHubDialog = function(showFiles, fn)
 				{
 					listFiles(false);
 				}
+				
+				// LATER: Paging not supported for contents in GitHub
+//				if (count == pageSize)
+//				{
+//					div.appendChild(nextPageDiv);
+//					
+//					scrollFn = function()
+//					{
+//						if (div.scrollTop >= div.scrollHeight - div.offsetHeight)
+//						{
+//							nextPage();
+//						}
+//					};
+//					
+//					mxEvent.addListener(div, 'scroll', scrollFn);
+//				}
 			}
 		}), error, true);
 	});
-	
-	// Adds paging for repos and branches (files limited to 1000 by API)
-	var pageSize = 100;
-	var nextPageDiv = null;
-	var scrollFn = null;
 
 	var selectRef = mxUtils.bind(this, function(page)
 	{
@@ -992,6 +1045,12 @@ GitHubClient.prototype.showGitHubDialog = function(showFiles, fn)
 		dlg.okButton.setAttribute('disabled', 'disabled');
 		this.ui.spinner.spin(div, mxResources.get('loading'));
 		
+		if (scrollFn != null)
+		{
+			mxEvent.removeListener(div, 'scroll', scrollFn);
+			scrollFn = null;
+		}
+		
 		if (nextPageDiv != null && nextPageDiv.parentNode != null)
 		{
 			nextPageDiv.parentNode.removeChild(nextPageDiv);
@@ -1004,7 +1063,6 @@ GitHubClient.prototype.showGitHubDialog = function(showFiles, fn)
 		
 		var nextPage = mxUtils.bind(this, function()
 		{
-			mxEvent.removeListener(div, 'scroll', scrollFn);
 			selectRef(page + 1);
 		});
 		
@@ -1082,6 +1140,11 @@ GitHubClient.prototype.showGitHubDialog = function(showFiles, fn)
 		dlg.okButton.setAttribute('disabled', 'disabled');
 		this.ui.spinner.spin(div, mxResources.get('loading'));
 		
+		if (scrollFn != null)
+		{
+			mxEvent.removeListener(div, 'scroll', scrollFn);
+		}
+		
 		if (nextPageDiv != null && nextPageDiv.parentNode != null)
 		{
 			nextPageDiv.parentNode.removeChild(nextPageDiv);
@@ -1094,7 +1157,6 @@ GitHubClient.prototype.showGitHubDialog = function(showFiles, fn)
 		
 		var nextPage = mxUtils.bind(this, function()
 		{
-			mxEvent.removeListener(div, 'scroll', scrollFn);
 			selectRepo(page + 1);
 		});
 		

+ 86 - 26
src/main/webapp/js/diagramly/GitLabClient.js

@@ -768,34 +768,72 @@ GitLabClient.prototype.showGitLabDialog = function(showFiles, fn)
 		}));
 	});
 	
-	var selectFile = mxUtils.bind(this, function()
+	// Adds paging for repos, branches and files
+	var nextPageDiv = null;
+	var scrollFn = null;
+	var pageSize = 100;
+	
+	var selectFile = mxUtils.bind(this, function(page)
 	{
+		if (page == null)
+		{
+			div.innerHTML = '';
+			page = 1;
+		}
+		
 		var req = new mxXmlRequest(this.baseUrl + '/projects/' + encodeURIComponent(org + '/' + repo) +
-			'/repository/tree?path=' + path + '&ref=' + ref, null, 'GET');
-		dlg.okButton.removeAttribute('disabled');
-		div.innerHTML = '';
+			'/repository/tree?path=' + path + '&ref=' + ref + '&per_page=' + pageSize + '&page=' + page, null, 'GET');
 		this.ui.spinner.spin(div, mxResources.get('loading'));
+		dlg.okButton.removeAttribute('disabled');
+		
+		if (scrollFn != null)
+		{
+			mxEvent.removeListener(div, 'scroll', scrollFn);
+			scrollFn = null;
+		}
+		
+		if (nextPageDiv != null && nextPageDiv.parentNode != null)
+		{
+			nextPageDiv.parentNode.removeChild(nextPageDiv);
+		}
+		
+		nextPageDiv = document.createElement('a');
+		nextPageDiv.style.display = 'block';
+		nextPageDiv.setAttribute('href', 'javascript:void(0);');
+		mxUtils.write(nextPageDiv, mxResources.get('more') + '...');
+		
+		var nextPage = mxUtils.bind(this, function()
+		{
+			selectFile(page + 1);
+		});
+		
+		mxEvent.addListener(nextPageDiv, 'click', nextPage);
 		
 		this.executeRequest(req, mxUtils.bind(this, function(req)
 		{
-			updatePathInfo(!ref);
 			this.ui.spinner.stop();
-			var files = JSON.parse(req.getText());
 			
-			div.appendChild(createLink('../ [Up]', mxUtils.bind(this, function()
+			if (page == 1)
 			{
-				if (path == '')
-				{
-					path = null;
-					selectRepo();
-				}
-				else
+				updatePathInfo(!ref);
+				
+				div.appendChild(createLink('../ [Up]', mxUtils.bind(this, function()
 				{
-					var tokens = path.split('/');
-					path = tokens.slice(0, tokens.length - 1).join('/');
-					selectFile();
-				}
-			}), '4px'));
+					if (path == '')
+					{
+						path = null;
+						selectRepo();
+					}
+					else
+					{
+						var tokens = path.split('/');
+						path = tokens.slice(0, tokens.length - 1).join('/');
+						selectFile();
+					}
+				}), '4px'));
+			}
+
+			var files = JSON.parse(req.getText());
 
 			if (files == null || files.length == 0)
 			{
@@ -804,6 +842,7 @@ GitLabClient.prototype.showGitLabDialog = function(showFiles, fn)
 			else
 			{
 				var gray = true;
+				var count = 0;
 				
 				var listFiles = mxUtils.bind(this, function(showFolders)
 				{
@@ -840,6 +879,7 @@ GitLabClient.prototype.showGitLabDialog = function(showFiles, fn)
 								})));
 								
 								div.appendChild(temp);
+								count++;
 							}
 						}))(files[i]);
 					}
@@ -851,14 +891,24 @@ GitLabClient.prototype.showGitLabDialog = function(showFiles, fn)
 				{
 					listFiles(false);
 				}
+				
+				if (count == pageSize)
+				{
+					div.appendChild(nextPageDiv);
+					
+					scrollFn = function()
+					{
+						if (div.scrollTop >= div.scrollHeight - div.offsetHeight)
+						{
+							nextPage();
+						}
+					};
+					
+					mxEvent.addListener(div, 'scroll', scrollFn);
+				}
 			}
 		}), error, true);
 	});
-	
-	// Adds paging for repos and branches (files limited to 1000 by API)
-	var pageSize = 100;
-	var nextPageDiv = null;
-	var scrollFn = null;
 
 	var selectRef = mxUtils.bind(this, function(page)
 	{
@@ -873,6 +923,12 @@ GitLabClient.prototype.showGitLabDialog = function(showFiles, fn)
 		dlg.okButton.setAttribute('disabled', 'disabled');
 		this.ui.spinner.spin(div, mxResources.get('loading'));
 		
+		if (scrollFn != null)
+		{
+			mxEvent.removeListener(div, 'scroll', scrollFn);
+			scrollFn = null;
+		}
+		
 		if (nextPageDiv != null && nextPageDiv.parentNode != null)
 		{
 			nextPageDiv.parentNode.removeChild(nextPageDiv);
@@ -885,7 +941,6 @@ GitLabClient.prototype.showGitLabDialog = function(showFiles, fn)
 		
 		var nextPage = mxUtils.bind(this, function()
 		{
-			mxEvent.removeListener(div, 'scroll', scrollFn);
 			selectRef(page + 1);
 		});
 		
@@ -962,7 +1017,13 @@ GitLabClient.prototype.showGitLabDialog = function(showFiles, fn)
 			div.innerHTML = '';
 			page = 1;
 		}
-
+		
+		if (scrollFn != null)
+		{
+			mxEvent.removeListener(div, 'scroll', scrollFn);
+			scrollFn = null;
+		}
+		
 		if (nextPageDiv != null && nextPageDiv.parentNode != null)
 		{
 			nextPageDiv.parentNode.removeChild(nextPageDiv);
@@ -975,7 +1036,6 @@ GitLabClient.prototype.showGitLabDialog = function(showFiles, fn)
 		
 		var nextPage = mxUtils.bind(this, function()
 		{
-			mxEvent.removeListener(div, 'scroll', scrollFn);
 			selectRepo(page + 1);
 		});
 		

+ 36 - 29
src/main/webapp/js/diagramly/GraphViewer.js

@@ -292,6 +292,15 @@ GraphViewer.prototype.init = function(container, xmlNode, graphConfig)
 				this.graph.panningHandler.ignoreCell = true;
 				this.graph.setPanning(false);
 		
+				if (this.graphConfig.toolbar != null)
+				{
+					this.addToolbar();
+				}
+				else if (this.graphConfig.title != null && this.showTitleAsTooltip)
+				{
+					container.setAttribute('title', this.graphConfig.title);
+				}
+				
 				this.addSizeHandler();
 				this.showLayers(this.graph);
 				this.addClickHandler(this.graph);
@@ -325,15 +334,6 @@ GraphViewer.prototype.init = function(container, xmlNode, graphConfig)
 					return done;
 				};
 				
-				if (this.graphConfig.toolbar != null)
-				{
-					this.addToolbar();
-				}
-				else if (this.graphConfig.title != null && this.showTitleAsTooltip)
-				{
-					container.setAttribute('title', this.graphConfig.title);
-				}
-				
 				this.fireEvent(new mxEventObject('render'));
 			});
 
@@ -749,30 +749,37 @@ GraphViewer.prototype.updateContainerHeight = function(container, height)
 GraphViewer.prototype.showLayers = function(graph, sourceGraph)
 {
 	var layers = this.graphConfig.layers;
+	var idx = (layers != null) ? layers.split(' ') : [];
+	var layerIds = this.graphConfig.layerIds;
 	
-	if (layers != null || sourceGraph != null)
+	if (idx.length > 0 || layerIds != null || sourceGraph != null)
 	{
-		var idx = (layers != null) ? layers.split(' ') : null;
+		var source = (sourceGraph != null) ? sourceGraph.getModel() : null;
+		var model = graph.getModel();
+		model.beginUpdate();
 		
-		if (sourceGraph != null || idx.length > 0)
+		try
 		{
-			var source = (sourceGraph != null) ? sourceGraph.getModel() : null;
-			var model = graph.getModel();
-			model.beginUpdate();
+			var childCount = model.getChildCount(model.root);
 			
-			try
+			// Hides all layers
+			for (var i = 0; i < childCount; i++)
 			{
-				var childCount = model.getChildCount(model.root);
-				
-				// Hides all layers
-				for (var i = 0; i < childCount; i++)
+				model.setVisible(model.getChildAt(model.root, i),
+					(sourceGraph != null) ? source.isVisible(source.getChildAt(source.root, i)) : false);
+			}
+			
+			// Shows specified layers (eg. 0 1 3)
+			if (source == null)
+			{
+				if (layerIds != null)
 				{
-					model.setVisible(model.getChildAt(model.root, i),
-						(sourceGraph != null) ? source.isVisible(source.getChildAt(source.root, i)) : false);
+					for (var i = 0; i < layerIds.length; i++)
+					{
+						model.setVisible(model.getCell(layerIds[i]), true);
+					}
 				}
-				
-				// Shows specified layers (eg. 0 1 3)
-				if (source == null)
+				else
 				{
 					for (var i = 0; i < idx.length; i++)
 					{
@@ -780,10 +787,10 @@ GraphViewer.prototype.showLayers = function(graph, sourceGraph)
 					}
 				}
 			}
-			finally
-			{
-				model.endUpdate();
-			}
+		}
+		finally
+		{
+			model.endUpdate();
 		}
 	}
 };

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1216 - 1214
src/main/webapp/js/viewer.min.js


+ 89 - 0
src/main/webapp/plugins/cConf-1-4-8.js

@@ -253,6 +253,95 @@ Draw.loadPlugin(function(ui)
 		
 		div.appendChild(zoomOpt);
 		
+		//Page and layers settings
+		div.appendChild(this.createTitle(mxResources.get('pageLayers', null, 'Page and Layers')));
+		
+		var hasAspect = false;
+		var pageId = null, layerIds = null;
+		
+		var customizeBtn = mxUtils.button(mxResources.get('customize', null, 'Customize'), function()
+		{
+			
+			var dlg = new AspectDialog(ui, pageId, layerIds, function(info)
+			{
+				pageId = info.pageId;
+				layerIds = info.layerIds;
+				macroData.aspect = pageId + ' ' + layerIds.join(' ');
+				ui.remoteInvoke('setAspect', [macroData.aspect], null, function(){}, function(){}); //Notify plugin of the change, ignoring both success and error callbacks
+			});
+			
+			ui.showDialog(dlg.container, 700, 465, true, true);
+			dlg.init();
+		});
+		
+		customizeBtn.className = 'geColorBtn';
+		customizeBtn.style.marginLeft = '10px';
+		customizeBtn.style.padding = '2px';
+		customizeBtn.setAttribute('disabled', 'disabled');
+		
+		if (macroData.aspect != null)
+		{
+			var aspectArray = macroData.aspect.split(' ');
+			
+			if (aspectArray.length > 1)
+			{
+				pageId = aspectArray[0];
+				layerIds = aspectArray.slice(1);
+				hasAspect = true;
+				customizeBtn.removeAttribute('disabled');
+			}
+		}
+		
+		var firstPageRadio = ui.addRadiobox(div, 'pageLayers', mxResources.get('firstPage', null, 'First Page (All Layers)'), !hasAspect);
+		firstPageRadio.style.marginTop = '4px';
+		
+		mxEvent.addListener(firstPageRadio, 'change', function()
+		{
+			if (this.checked)
+			{
+				macroData.aspect = null;
+				ui.remoteInvoke('setAspect', [macroData.aspect], null, function(){}, function(){}); //Notify plugin of the change, ignoring both success and error callbacks
+				customizeBtn.setAttribute('disabled', 'disabled');
+			}
+		});
+		
+		var currentStateRadio = ui.addRadiobox(div, 'pageLayers', mxResources.get('curEditorState', null, 'Current Editor State'), false);
+		currentStateRadio.style.marginTop = '8px';
+		
+		mxEvent.addListener(currentStateRadio, 'change', function()
+		{
+			if (this.checked)
+			{
+				var curPage = ui.updatePageRoot(ui.currentPage);
+				var layerIds = [], layers = curPage.root.children;
+				
+				for (var i = 0; i < layers.length; i++)
+				{
+					if (layers[i].visible != false)
+					{
+						layerIds.push(layers[i].id);
+					}
+				}
+
+				macroData.aspect = curPage.getId() + ' ' + layerIds.join(' ');
+				ui.remoteInvoke('setAspect', [macroData.aspect], null, function(){}, function(){}); //Notify plugin of the change, ignoring both success and error callbacks
+				customizeBtn.setAttribute('disabled', 'disabled');
+			}
+		});
+
+		var customStateRadio = ui.addRadiobox(div, 'pageLayers', mxResources.get('custom', null, 'Custom'), hasAspect, false, true);
+		customStateRadio.style.marginTop = '8px';
+		
+		mxEvent.addListener(customStateRadio, 'change', function()
+		{
+			if (this.checked)
+			{
+				customizeBtn.removeAttribute('disabled');
+			}
+		});
+
+		div.appendChild(customizeBtn);
+
 		return div;
 	};
 	

+ 7 - 7
src/main/webapp/resources/dia_ro.txt

@@ -685,7 +685,7 @@ support=Suport
 sysml=SysML
 tags=Tag-uri
 table=Tabel
-tables=Tables
+tables=Tabele
 takeOver=Take Over
 targetSpacing=Spațierea țintei
 template=Template
@@ -699,7 +699,7 @@ title=Titlu
 to=către
 toBack=Înapoi
 toFront=Înainte
-toolbar=Toolbar
+toolbar=Unelte
 tooltips=Sfaturi pentru folosirea instrumentelor
 top=Sus
 topAlign=Aliniere sus
@@ -768,13 +768,13 @@ venndiagrams=Diagrame Venn
 webEmailOrOther=Web, e-mail sau orice alta adresa online
 webLink=Web Link
 wireframes=Wireframe-uri
-property=Property
-value=Value
+property=Proprietate
+value=Valoare
 showMore=Show More
 showLess=Show Less
-myDiagrams=My Diagrams
-allDiagrams=All Diagrams
-recentlyUsed=Recently used
+myDiagrams=Diagramele mele
+allDiagrams=Toate diagramele
+recentlyUsed=Folosite recent
 listView=List view
 gridView=Grid view
 resultsFor=Results for '{1}'

+ 44 - 0
src/main/webapp/styles/grapheditor.css

@@ -1440,3 +1440,47 @@ html td.mxWindowTitle {
 	top: 5px;
 	right: 5px;
 }
+
+.geAspectDlgListItem 
+{
+	width : 120px;
+	height : 120px;
+	display : inline-block;
+	border: 3px solid #F0F0F0;
+	border-radius: 5px;
+	padding: 5px;
+	margin : 2px 2px 20px 2px;
+}
+
+.geAspectDlgListItem:hover
+{
+	border: 3px solid #c5c5c5;
+}
+
+.geAspectDlgListItemSelected 
+{
+	border: 3px solid #3b73af;
+}
+
+.geAspectDlgListItemSelected:hover
+{
+	border: 3px solid #405a86;
+}
+
+.geAspectDlgListItemText
+{
+	text-overflow: ellipsis;
+	max-width: 100%;
+	min-height : 2em;
+	overflow : hidden;
+	text-align : center;
+	margin-top : 10px;
+}
+
+.geAspectDlgList
+{
+	min-height: 184px;
+	white-space: nowrap;
+	overflow-x : auto;
+	overflow-y : hidden;
+}