Browse Source

9.0.9 release

David Benson [draw.io] 7 years ago
parent
commit
3d9619a3f9

+ 6 - 0
ChangeLog

@@ -1,3 +1,9 @@
+27-AUG-2018: 9.0.9
+
+- Uses mxGraph 3.9.9 beta 8
+- Adds new color palette
+- GCP stencil updates
+
 23-AUG-2018: 9.0.8
 
 - Fixes XSS vulnerability in Trello Power-Up

+ 1 - 1
VERSION

@@ -1 +1 @@
-9.0.8
+9.0.9

File diff suppressed because it is too large
+ 3 - 2
etc/mxgraph/mxClient.js


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

@@ -1,7 +1,7 @@
 CACHE MANIFEST
 
 # THIS FILE WAS GENERATED. DO NOT MODIFY!
-# 08/23/2018 04:42 PM
+# 08/27/2018 04:58 PM
 
 app.html
 index.html?offline=1

+ 20 - 20
src/main/webapp/electron.js

@@ -113,27 +113,27 @@ function createWindow (opt = {})
 	mainWindow.webContents.on('did-fail-load', function(err)
     {
         let ourl = url.format(
+		{
+			pathname: `${__dirname}/index.html`,
+			protocol: 'file:',
+			query:
 			{
-				pathname: `${__dirname}/index.html`,
-				protocol: 'file:',
-				query:
-				{
-					'dev': __DEV__ ? 1 : 0,
-					'drawdev': __DEV__ ? 1 : 0,
-					'test': __DEV__ ? 1 : 0,
-					'db': 0,
-					'gapi': 0,
-					'od': 0,
-					'gh': 0,
-					'tr': 0,
-					'analytics': 0,
-					'picker': 0,
-					'mode': 'device',
-					'browser': 0,
-					'export': 'https://exp.draw.io/ImageExport4/export'
-				},
-				slashes: true,
-			})
+				'dev': __DEV__ ? 1 : 0,
+				'drawdev': __DEV__ ? 1 : 0,
+				'test': __DEV__ ? 1 : 0,
+				'db': 0,
+				'gapi': 0,
+				'od': 0,
+				'gh': 0,
+				'tr': 0,
+				'analytics': 0,
+				'picker': 0,
+				'mode': 'device',
+				'browser': 0,
+				'export': 'https://exp.draw.io/ImageExport4/export'
+			},
+			slashes: true,
+		})
 		
 		mainWindow.loadURL(ourl)
     })

BIN
src/main/webapp/images/sidebar-gcp2.png


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


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


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


+ 1 - 1
src/main/webapp/js/diagramly/Dialogs.js

@@ -3868,7 +3868,7 @@ var PopupDialog = function(editorUi, url, pre, fallback, hideDialog)
 			pre();
 		}
 		
-		editorUi.openLink(url);
+		editorUi.openLink(url, null, true);
 	});
 	wndBtn.className = 'geBtn gePrimaryBtn';
 	wndBtn.style.width = replaceBtn.style.width;

+ 255 - 0
src/main/webapp/js/diagramly/Editor.js

@@ -1000,6 +1000,10 @@
 			{fill: '#dae8fc', stroke: '#6c8ebf'}, {fill: '#d5e8d4', stroke: '#82b366'},
 			{fill: '#ffe6cc', stroke: '#d79b00'}, {fill: '#fff2cc', stroke: '#d6b656'},
 			{fill: '#f8cecc', stroke: '#b85450'}, {fill: '#e1d5e7', stroke: '#9673a6'}],
+			[null, {fill: mxConstants.NONE, stroke: '#36393d'},
+			{fill: '#fad7ac', stroke: '#b46504'}, {fill: '#fad9d5', stroke: '#ae4132'},
+			{fill: '#b0e3e6', stroke: '#0e8088'}, {fill: '#b1ddf0', stroke: '#10739e'},
+			{fill: '#d0cee2', stroke: '#56517e'}, {fill: '#bac8d3', stroke: '#23445d'}],
 		    [null,
 			{fill: '#f5f5f5', stroke: '#666666', gradient: '#b3b3b3'},
 			{fill: '#dae8fc', stroke: '#6c8ebf', gradient: '#7ea6e0'},
@@ -1029,6 +1033,9 @@
 			}
 			
 			styleFormatPanelInit.apply(this, arguments);
+
+			if (urlParams['properties'] == '1')
+				this.container.appendChild(this.addProperties(this.createPanel(), sstate));
 		};
 
 		/**
@@ -1065,6 +1072,250 @@
 			return styleFormatPanelAddStyleOps.apply(this, arguments);
 		};
 
+		/**
+		 * Create Properties Panel
+		 */
+		StyleFormatPanel.prototype.addProperties = function(div, state)
+		{
+			var that = this;
+			var graph = this.editorUi.editor.graph;
+
+			function applyStyleVal(pName, newVal)
+			{
+				graph.getModel().beginUpdate();
+				try
+				{
+					graph.setCellStyles(pName, newVal, graph.getSelectionCells());
+					that.editorUi.fireEvent(new mxEventObject('styleChanged', 'keys', [pName],
+							'values', [newVal], 'cells', graph.getSelectionCells()));
+
+				}
+				finally
+				{
+					graph.getModel().endUpdate();
+				}
+			}
+			
+			function setElementPos(td, elem, adjustHeight)
+			{
+				var pos = mxUtils.getOffset(td, true);
+				elem.style.position = 'absolute';
+				elem.style.left = pos.x + 'px';
+				elem.style.top = pos.y + 'px';
+				elem.style.width = td.offsetWidth + 'px';
+				elem.style.height = (td.offsetHeight - (adjustHeight? 4 : 0)) + 'px';
+				elem.style.zIndex = 5;
+			};
+			
+			function createColorBtn(pName, pValue)
+			{
+				var clrDiv = document.createElement("div");
+				clrDiv.style.width = '32px';
+				clrDiv.style.height = '4px';
+				clrDiv.style.margin = "2px";
+				clrDiv.style.border = "1px solid black";
+				clrDiv.style.backgroundColor = pValue;
+
+				btn = mxUtils.button('', mxUtils.bind(that, function(evt)
+				{
+					this.editorUi.pickColor(pValue, function(color)
+					{
+						clrDiv.style.backgroundColor = color;
+						applyStyleVal(pName, color);
+					});
+					mxEvent.consume(evt);
+				}));
+				
+				btn.style.height = '12px';
+				btn.style.width = '40px';
+				btn.className = 'geColorBtn';
+				
+				btn.appendChild(clrDiv);
+				return btn;
+			};
+			
+			function createCheckbox(pName, pValue)
+			{
+				var input = document.createElement('input');
+				input.type = "checkbox";
+				input.checked = pValue == '1';
+				
+				mxEvent.addListener(input, 'change', function() 
+				{
+					applyStyleVal(pName, input.checked? '1' : '0');
+				});
+				return input;
+			};
+			
+			function createPropertyRow(pName, pDiplayName, pValue, pType, isOdd)
+			{
+				var row = document.createElement('tr');
+				row.className = "propRow" + (isOdd? "Alt" : "");
+				var td = document.createElement('td');
+				td.className = "propRowCell";
+				td.innerHTML = mxUtils.htmlEntities(pDiplayName);
+				row.appendChild(td);
+				td = document.createElement('td');
+				td.className = "propRowCell";
+				
+				if (pType == "color")
+				{
+					td.appendChild(createColorBtn(pName, pValue));
+				}
+				else if (pType == "bool")
+				{
+					td.appendChild(createCheckbox(pName, pValue));
+				}
+				else if (pType.type == "enum")
+				{
+					for (var i = 0; i < pType.options.length; i++)
+					{
+						var op = pType.options[i];
+						
+						if (op.val == pValue)
+						{
+							td.innerHTML = mxUtils.htmlEntities(op.dispName);
+							break;
+						}
+					}
+					
+					mxEvent.addListener(td, 'click', mxUtils.bind(that, function()
+					{
+						var select = document.createElement('select');
+						setElementPos(td, select);
+
+						for (var i = 0; i < pType.options.length; i++)
+						{
+							var op = pType.options[i];
+							var opElem = document.createElement('option');
+							opElem.value = mxUtils.htmlEntities(op.val);
+							opElem.innerHTML = mxUtils.htmlEntities(op.dispName);
+							select.appendChild(opElem);
+						}
+						
+						select.value = mxUtils.htmlEntities(pValue);
+						
+						document.body.appendChild(select);
+						
+						mxEvent.addListener(select, 'blur', function()
+						{
+							document.body.removeChild(select);
+						});
+						
+						mxEvent.addListener(select, 'change', function()
+						{
+							applyStyleVal(pName, select.value);
+							td.innerHTML = mxUtils.htmlEntities(select.value);
+						});
+						select.focus();
+					}));
+				}
+				else
+				{
+					td.innerHTML = mxUtils.htmlEntities(pValue);
+					mxEvent.addListener(td, 'click', mxUtils.bind(that, function()
+					{
+						var input = document.createElement('input');
+						setElementPos(td, input, true);
+						input.value = pValue;
+						input.className = "propEditor";
+						
+						if (pType == "int" || pType == "float")
+						{
+							input.type = "number";
+							input.step = pType == "int"? "1" : "any";
+						}
+						
+						document.body.appendChild(input);
+						mxEvent.addListener(input, 'blur', function(){
+							document.body.removeChild(input);
+						});
+						
+						function setInputVal()
+						{
+							var newVal = pType == "int"? parseInt(input.value) + '' : input.value;
+							applyStyleVal(pName, newVal);
+							td.innerHTML = mxUtils.htmlEntities(newVal);
+						}
+						
+						mxEvent.addListener(input, 'change', setInputVal);
+						mxEvent.addListener(input, 'keypress', function(e)
+						{
+							if (e.keyCode == 13) 
+							{
+								setInputVal();
+								
+								try
+								{
+									document.body.removeChild(input);
+								}
+								catch(e){}
+							}
+						});
+						
+						input.focus();
+					}));
+				}
+				row.appendChild(td);
+				return row;
+			};
+			
+			div.style.position = 'relative';
+			div.style.padding = '0';
+			var grid = document.createElement('table');
+			grid.style.whiteSpace = 'nowrap';
+			grid.style.width = '100%';
+			//create header row
+			var hrow = document.createElement('tr');
+			hrow.className = "propHeader";
+			var th = document.createElement('th');
+			th.className = "propHeaderCell";
+			th.innerHTML = mxResources.get('property', null, 'Property');
+			hrow.appendChild(th);
+			th = document.createElement('th');
+			th.className = "propHeaderCell";
+			th.innerHTML = mxResources.get('value', null, 'Value');
+			hrow.appendChild(th);
+			grid.appendChild(hrow);
+			
+			var isOdd = false;
+			for (var key in state.style)
+			{
+				var pValue = state.style[key];
+				
+				//skip non-common properties (which are	set to empty string)
+				if (pValue == "") continue;
+				
+				var type = "string";
+
+				if (typeof pValue == "string" && pValue.indexOf("#") == 0 && pValue.length == 7)
+				{
+					type = "color";
+				}
+				else if (pValue == '1')
+				{
+					type = 'bool';
+				}
+				else if (parseInt(pValue) == parseFloat(pValue))
+				{
+					type = "int";
+				}
+				else if (isFinite(parseFloat(pValue)))
+				{
+					type = "float";
+				}
+				else if (pValue == 'middle')
+				{
+					type = {type: 'enum', options: [{val: 'middle', dispName: 'Middle'}, {val: 'bottom', dispName: 'Bottom'}, {val: 'top', dispName: 'Top'}]};
+				}
+				grid.appendChild(createPropertyRow(key, key.charAt(0).toUpperCase() + key.substr(1), pValue, type, isOdd));
+				isOdd = !isOdd;
+			}
+			
+			div.appendChild(grid);
+			
+			return div;
+		}		
 		/**
 		 * Creates the buttons for the predefined styles.
 		 */
@@ -1147,6 +1398,10 @@
 									colorset['gradient'] + ' 100%)';
 							}
 						}
+						else if (colorset['fill'] == mxConstants.NONE)
+						{
+							btn.style.background = 'url(\'' + Dialog.prototype.noColorImage + '\')';
+						}
 						else
 						{					
 							btn.style.backgroundColor = colorset['fill'];

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

@@ -202,10 +202,10 @@
 	/**
 	 * Hook for subclassers.
 	 */
-	EditorUi.prototype.openLink = function(url, target)
+	EditorUi.prototype.openLink = function(url, target, allowOpener)
 	{
 		// LATER: Replace this with direct calls to graph
-		return this.editor.graph.openLink(url, target);
+		return this.editor.graph.openLink(url, target, allowOpener);
 	};
 
 	/**

+ 4 - 8
src/main/webapp/js/diagramly/ElectronApp.js

@@ -171,10 +171,10 @@ FeedbackDialog.feedbackUrl = 'https://log.draw.io/email';
 			           
 		        if (paths !== undefined && paths[0] != null)
 		        {
-			        	var path = paths[0];
-			        	var asImage = /\.png$/i.test(path) || /\.gif$/i.test(path) || /\.jpe?g$/i.test(path);
-			        	var encoding = (asImage || /\.vsdx$/i.test(path) || /\.vssx$/i.test(path)) ?
-			        		'base64' : 'utf-8';
+		        	var path = paths[0];
+		        	var asImage = /\.png$/i.test(path) || /\.gif$/i.test(path) || /\.jpe?g$/i.test(path);
+		        	var encoding = (asImage || /\.vsdx$/i.test(path) || /\.vssx$/i.test(path)) ?
+		        		'base64' : 'utf-8';
 
 					if (editorUi.spinner.spin(document.body, mxResources.get('loading')))
 					{
@@ -635,10 +635,6 @@ FeedbackDialog.feedbackUrl = 'https://log.draw.io/email';
 				this.fileObject.type = 'utf-8';
 				fn();
 			}
-	        else if (error != null)
-			{
-				error();
-			}
 		}
 		else
 		{

+ 6 - 3
src/main/webapp/js/diagramly/Minimal.js

@@ -6,6 +6,8 @@ EditorUi.initMinimalTheme = function()
 	// Disabled in lightbox and chromeless mode
 	if (urlParams['lightbox'] == '1' || urlParams['chrome'] == '0' || typeof window.Format === 'undefined' || typeof window.Menus === 'undefined')
 	{
+		window.uiTheme = null;
+		
 		return;
 	}
 	
@@ -816,15 +818,16 @@ EditorUi.initMinimalTheme = function()
         this.put('exportAs', new Menu(mxUtils.bind(this, function(menu, parent)
         {
         	exportAsMenu.funct(menu, parent);
-            menu.addSeparator(parent);
-            ui.menus.addSubmenu('embed', menu, parent);
-            
+
     		if (!mxClient.IS_CHROMEAPP && !EditorUi.isElectronApp)
     		{
 	            // Publish menu contains only one element by default...
 	            //ui.menus.addSubmenu('publish', menu, parent); 
 	            ui.menus.addMenuItems(menu, ['publishLink'], parent);
     		}
+    		
+            menu.addSeparator(parent);
+            ui.menus.addSubmenu('embed', menu, parent);
         })));
 
         var langMenu = this.get('language');

+ 158 - 1
src/main/webapp/js/diagramly/sidebar/Sidebar-GCP2.js

@@ -5,6 +5,7 @@
 		this.addGCP2PathsPalette();
 		this.addGCP2ZonesPalette();
 		this.addGCP2ServiceCardsPalette();
+		this.addGCP2UserDeviceCardsPalette();
 		this.addGCP2ComputePalette();
 		this.addGCP2APIPlatformEcosystemsPalette();
 		this.addGCP2IdentitySecurityPalette();
@@ -160,7 +161,101 @@
 			    bg.insert(zone12Cell);
 
 			   	return sb.createVertexTemplateFromCells([bg], bg.geometry.width, bg.geometry.height, 'Project Zone / Cloud Service Provider');
-			})
+			}),
+			
+		    this.createVertexTemplateEntry('fillColor=#4DA1F5;strokeColor=none;shadow=1;gradientColor=none;fontSize=14;align=left;spacingLeft=50;fontColor=#ffffff;', 
+		    		1000, 40, 'Architecture: App Engine and Cloud Endpoints', 'Title bar', null, null, this.getTagsForStencil(gn, '', dt + 'title bar').join(' ')),
+		    this.createVertexTemplateEntry('fillColor=#ffffff;strokeColor=none;shadow=0;gradientColor=none;fontSize=11;align=left;spacing=10;fontColor=#;9E9E9E;verticalAlign=top;spacingTop=100;',
+		    		300, 350, 'Use this note to call out\nor clarify parts of a diagram', 'Note', null, null, this.getTagsForStencil(gn, '', dt + 'note').join(' ')),
+		    		
+			this.addEntry(dt + 'project', function()
+		   	{
+			    var bg = new mxCell('<b>Google </b>Cloud Platform', new mxGeometry(0, 0, 650, 350), 
+			    		'fillColor=#F6F6F6;strokeColor=none;shadow=0;gradientColor=none;fontSize=14;align=left;spacing=10;fontColor=#717171;9E9E9E;verticalAlign=top;spacingTop=-4;fontStyle=0;spacingLeft=40;html=1;');
+		    	bg.vertex = true;
+		    	
+			    var zone1Cell = new mxCell('', 
+			    		new mxGeometry(0, 0, 23, 20), 
+			    		'shape=mxgraph.gcp2.google_cloud_platform;fillColor=#F6F6F6;strokeColor=none;shadow=0;gradientColor=none;');
+			    zone1Cell.geometry.relative = true;
+			    zone1Cell.geometry.offset = new mxPoint(20, 10);
+			    zone1Cell.vertex = true;
+		    	bg.insert(zone1Cell);
+
+			   	return sb.createVertexTemplateFromCells([bg], bg.geometry.width, bg.geometry.height, 'Project Zone');
+			}),
+
+			this.addEntry(dt + 'markers', function()
+		   	{
+				s = 'shape=ellipse;fillColor=#ffffff;strokeColor=#BDBDBD;strokeWidth=2;shadow=0;gradientColor=none;fontColor=#757575;align=center;html=1;fontStyle=1;spacingTop=-1;';
+				
+			    var icon1 = new mxCell('1', new mxGeometry(0, 0, 20, 20), s);
+			    icon1.vertex = true;
+			    var icon2 = new mxCell('2', new mxGeometry(40, 0, 20, 20), s);
+			    icon2.vertex = true;
+			    var icon3 = new mxCell('3', new mxGeometry(80, 0, 20, 20), s);
+			    icon3.vertex = true;
+			    var icon4 = new mxCell('4', new mxGeometry(120, 0, 20, 20), s);
+			    icon4.vertex = true;
+			    var icon5 = new mxCell('5', new mxGeometry(160, 0, 20, 20), s);
+			    icon5.vertex = true;
+			    var icon6 = new mxCell('6', new mxGeometry(200, 0, 20, 20), s);
+			    icon6.vertex = true;
+			    var icon7 = new mxCell('7', new mxGeometry(240, 0, 20, 20), s);
+			    icon7.vertex = true;
+			    var label1 = new mxCell('Markers to be used with the legend', new mxGeometry(0, 20, 260, 30), 
+			    		'strokeColor=none;fillColor=none;fontColor=#757575;align=left;html=1;fontStyle=0;fontSize=11;');
+			    label1.vertex = true;
+
+			   	return sb.createVertexTemplateFromCells([icon1, icon2, icon3, icon4, icon5, icon6, icon7, label1], 260, 50, 'Markers');
+			}),
+
+			this.addEntry(dt + 'markers', function()
+		   	{
+				var s = 'strokeColor=none;fillColor=none;fontColor=#757575;align=left;html=1;fontStyle=0;spacingLeft=5;fontSize=11;verticalAlign=top;whiteSpace=wrap;spacingRight=5;';
+				
+			    var bg = new mxCell('', new mxGeometry(0, 0, 600, 70), 
+	    			'fillColor=#ffffff;strokeColor=#BDBDBD;strokeWidth=1;shadow=0;gradientColor=none;');
+			    bg.vertex = true;
+				
+			    var label1 = new mxCell('1 Commit code', new mxGeometry(0, 0, 200, 30), s);
+			    label1.geometry.relative = true;
+			    label1.vertex = true;
+			    bg.insert(label1);
+			    
+			    var label2 = new mxCell('2 Detect code change', new mxGeometry(0, 0, 200, 30), s);
+			    label2.geometry.relative = true;
+			    label2.geometry.offset = new mxPoint(0, 30);
+			    label2.vertex = true;
+			    bg.insert(label2);
+			    
+			    var label3 = new mxCell('3 Build immutable image', new mxGeometry(0, 0, 200, 30), s);
+			    label3.geometry.relative = true;
+			    label3.geometry.offset = new mxPoint(200, 0);
+			    label3.vertex = true;
+			    bg.insert(label3);
+			    
+			    var label4 = new mxCell('4 Launch test instance from image', new mxGeometry(0, 0, 200, 30), s);
+			    label4.geometry.relative = true;
+			    label4.geometry.offset = new mxPoint(200, 30);
+			    label4.vertex = true;
+			    bg.insert(label4);
+			    
+			    var label5 = new mxCell('5 Run tests', new mxGeometry(0, 0, 200, 30), s);
+			    label5.geometry.relative = true;
+			    label5.geometry.offset = new mxPoint(400, 0);
+			    label5.vertex = true;
+			    bg.insert(label5);
+			    
+			    var label6 = new mxCell('6 Perform rolling update of image to autoscaler', new mxGeometry(0, 0, 200, 30), s);
+			    label6.geometry.relative = true;
+			    label6.geometry.offset = new mxPoint(400, 30);
+			    label6.vertex = true;
+			    bg.insert(label6);
+			    
+			   	return sb.createVertexTemplateFromCells([bg], bg.geometry.width, bg.geometry.height, 'Markers');
+			}),
+
 	 	];
 		
 		this.addPalette('gcp2Zones', 'GCP / Zones', false, mxUtils.bind(this, function(content)
@@ -518,6 +613,45 @@
 		}));
 	};
 	
+	Sidebar.prototype.addGCP2UserDeviceCardsPalette = function()
+	{
+		var dt = 'gcp google cloud platform user and device cards';
+		var fns = [];
+
+		this.addGCP2UserDeviceCard('Application', 'application', 1, 0.8, 0, dt + 'application', fns);
+		this.addGCP2UserDeviceCard('Beacon', 'beacon', 0.73, 1, 0, dt + 'beacon', fns);
+		this.addGCP2UserDeviceCard('Circuit-Board', 'circuit_board', 1, 0.9, 15, dt + 'circuit board', fns);
+		this.addGCP2UserDeviceCard('Database', 'database', 1, 0.9, 0, dt + 'database db', fns);
+		this.addGCP2UserDeviceCard('Desktop', 'desktop', 1, 0.9, 0, dt + 'desktop', fns);
+		this.addGCP2UserDeviceCard('Desktop and Mobile', 'desktop_and_mobile', 1, 0.66, 15, dt + 'desktop and mobile', fns);
+		this.addGCP2UserDeviceCard('Game', 'game', 1, 0.54, 0, dt + 'game', fns);
+		this.addGCP2UserDeviceCard('Gateway', 'gateway_icon', 1, 0.44, 0, dt + 'gateway icon', fns);
+		this.addGCP2UserDeviceCard('Laptop', 'laptop', 1, 0.66, 0, dt + 'laptop', fns);
+		this.addGCP2UserDeviceCard('Lightbulb', 'lightbulb', 0.7, 1, 0, dt + 'lighbulb', fns);
+		this.addGCP2UserDeviceCard('List', 'list', 0.89, 1, 0, dt + 'list', fns);
+		this.addGCP2UserDeviceCard('Live', 'live', 0.74, 1, 0, dt + 'live', fns);
+		this.addGCP2UserDeviceCard('Local-Compute', 'compute_engine_icon', 1, 0.89, 15, dt + 'local compute', fns);
+		this.addGCP2UserDeviceCard('Mobile Devices', 'mobile_devices', 1, 0.73, 15, dt + 'mobile devices', fns);
+		this.addGCP2UserDeviceCard('Payment', 'payment', 1, 0.8, 0, dt + 'payment', fns);
+		this.addGCP2UserDeviceCard('Phone', 'phone', 0.64, 1, 0, dt + 'phone', fns);
+		this.addGCP2UserDeviceCard('Record', 'record', 1, 0.66, 0, dt + 'record', fns);
+		this.addGCP2UserDeviceCard('Report', 'report', 1, 1, 0, dt + 'report', fns);
+		this.addGCP2UserDeviceCard('Retail', 'retail', 1, 0.89, 0, dt + 'retail', fns);
+		this.addGCP2UserDeviceCard('Speaker', 'speaker', 0.7, 1, 0, dt + 'speaker', fns);
+		this.addGCP2UserDeviceCard('Storage', 'storage', 1, 0.8, 0, dt + 'storage', fns);
+		this.addGCP2UserDeviceCard('Stream', 'stream', 1, 0.82, 0, dt + 'stream', fns);
+		this.addGCP2UserDeviceCard('Users', 'users', 1, 0.63, 0, dt + 'users', fns);
+		this.addGCP2UserDeviceCard('Webcam', 'webcam', 0.5, 1, 0, dt + 'webcam', fns);
+		
+		this.addPalette('gcp2User Device Cards', 'GCP / User and Device Cards', false, mxUtils.bind(this, function(content)
+		{
+			for (var i = 0; i < fns.length; i++)
+			{
+				content.appendChild(fns[i](content));
+			}
+		}));
+	};
+	
 	Sidebar.prototype.addGCP2CardSet = function(label, icon, w1, w2, dt, fns)
 	{
 		var sb = this;
@@ -772,4 +906,27 @@
 			);
 	};
 
+	Sidebar.prototype.addGCP2UserDeviceCard = function(label, icon, scaleX, scaleY, h1, dt, fns)
+	{
+		var sb = this;
+		var s = 'dashed=0;html=1;fillColor=#757575;strokeColor=none;' + mxConstants.STYLE_SHAPE + '=mxgraph.gcp2.';
+		var label1 = label.replace('\n', ' ');
+		var label1 = label1.replace('- ', '-');
+
+		fns.push(
+			this.addEntry(dt, function()
+		   	{
+			    var bg = new mxCell(label, new mxGeometry(0, 0, 70, 85  + h1), 
+			    		'strokeColor=#dddddd;fillColor=#ffffff;shadow=1;strokeWidth=1;rounded=1;absoluteArcSize=1;arcSize=2;labelPosition=center;verticalLabelPosition=middle;align=center;verticalAlign=bottom;spacingLeft=0;fontColor=#999999;fontSize=12;whiteSpace=wrap;spacingBottom=2;');
+		    	bg.vertex = true;
+			    var icon1 = new mxCell('', new mxGeometry(0.5, 0, 50 * scaleX, 50 * scaleY), s + icon + ';part=1;');
+			    icon1.geometry.relative = true;
+			    icon1.geometry.offset = new mxPoint(- scaleX * 25, 10 + (1 - scaleY) * 25);
+		    	icon1.vertex = true;
+		    	bg.insert(icon1);
+		    	
+			   	return sb.createVertexTemplateFromCells([bg], bg.geometry.width, bg.geometry.height, label1);
+			})
+		);
+	};
 })();

+ 1 - 1
src/main/webapp/js/diagramly/sidebar/Sidebar.js

@@ -51,7 +51,7 @@
 	/**
 	 * 
 	 */
-	Sidebar.prototype.gcp2 = ['Paths', 'Zones', 'Service Cards', 'Compute', 'API Platform and Ecosystems', 'Identity and Security', 'Big Data', 'Data Transfer', 'Cloud AI', 'Internet of Things', 'Storage and Databases', 'Management Tools', 'Networking', 'Developer Tools', 'Expanded Product Cards'];
+	Sidebar.prototype.gcp2 = ['Paths', 'Zones', 'Service Cards', 'Compute', 'API Platform and Ecosystems', 'Identity and Security', 'Big Data', 'Data Transfer', 'Cloud AI', 'Internet of Things', 'Storage and Databases', 'Management Tools', 'Networking', 'Developer Tools', 'Expanded Product Cards', 'User Device Cards'];
 	
 	/**
 	 *

+ 8 - 0
src/main/webapp/js/diagramly/vsdx/importer.js

@@ -823,6 +823,14 @@ var com;
                         }
                         ;
                     }
+                    
+                    /* put */ (function (m, k, v) { if (m.entries == null)
+                    m.entries = []; for (var i = 0; i < m.entries.length; i++)
+                    if (m.entries[i].key.equals != null && m.entries[i].key.equals(k) || m.entries[i].key === k) {
+                        m.entries[i].value = v;
+                        return;
+                    } m.entries.push({ key: k, value: v, getKey: function () { return this.key; }, getValue: function () { return this.value; } }); })(this.vertexMap, new com.mxgraph.io.vsdx.ShapePageId(pageId, shape.getId()), group);
+                    
                     return group;
                 };
                 mxVsdxCodec.rotatedEdgePoint = function (pt, rotation, cx, cy) {

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


File diff suppressed because it is too large
+ 159 - 158
src/main/webapp/js/extensions.min.js


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

@@ -337,7 +337,7 @@ Editor.prototype.editAsNew = function(xml, title)
 			}
 
 			this.editorWindow = this.graph.openLink(this.getEditBlankUrl(p +
-				((p.length > 0) ? '&' : '?') + 'client=1'));
+				((p.length > 0) ? '&' : '?') + 'client=1'), null, true);
 		}
 		else
 		{

+ 8 - 1
src/main/webapp/js/mxgraph/Graph.js

@@ -1450,7 +1450,7 @@ Graph.prototype.labelLinkClicked = function(state, elt, evt)
 /**
  * Returns the size of the page format scaled with the page size.
  */
-Graph.prototype.openLink = function(href, target)
+Graph.prototype.openLink = function(href, target, allowOpener)
 {
 	var result = window;
 	
@@ -1479,6 +1479,11 @@ Graph.prototype.openLink = function(href, target)
 		else
 		{
 			result = window.open(href, target);
+			
+			if (result != null && !allowOpener)
+			{
+				result.opener = null;
+			}
 		}
 	}
 	
@@ -5626,6 +5631,7 @@ if (typeof mxVertexHandler != 'undefined')
 						
 						if (href != null)
 						{
+							links[i].setAttribute('rel', 'nofollow noopener noreferrer');
 							links[i].setAttribute('href', href);
 							
 							if (beforeClick != null)
@@ -6624,6 +6630,7 @@ if (typeof mxVertexHandler != 'undefined')
 			};
 			
 			var a = document.createElement('a');
+			a.setAttribute('rel', 'nofollow noopener noreferrer');
 			a.setAttribute('href', this.getAbsoluteUrl(link));
 			a.setAttribute('title', short((this.isCustomLink(link)) ?
 				this.getLinkTitle(link) : link, 80));

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


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


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


+ 4 - 1
src/main/webapp/plugins/tickets.js

@@ -102,7 +102,10 @@ Draw.loadPlugin(function(ui)
 
 	function isDeskLink(link)
 	{
-		return config != null && link.substring(0, deskDomain.length) == deskDomain;
+		var dl = deskDomain.length;
+		
+		return config != null && link.substring(0, dl) == deskDomain &&
+			link.substring(dl, dl + 18) == '/helpdesk/tickets/';
 	};
 	
 	function getIdForDeskLink(link)

File diff suppressed because it is too large
+ 933 - 0
src/main/webapp/stencils/gcp2.xml


+ 3 - 0
src/main/webapp/styles/dark.css

@@ -88,6 +88,9 @@ html body .geDialog, html body div.mxWindow {
 	box-shadow:none;
 	color:#cccccc;
 }
+html body .geActivePage {
+	border-bottom: 3px solid #DF6C0C !important;
+}
 .geHint {
 	-webkit-box-shadow: 1px 1px 1px 0px #ccc;
 	-moz-box-shadow: 1px 1px 1px 0px #ccc;

+ 42 - 1
src/main/webapp/styles/grapheditor.css

@@ -715,4 +715,45 @@ html td.mxWindowTitle {
  	font-size:12px;
  	color:rgb(112, 112, 112);
  	padding:4px;
-}
+}
+
+.propHeader, .propRow {
+	border: 1px solid #e9e9e9;	
+}
+
+.propHeader>.propHeaderCell {
+    border-top: 0;
+    border-bottom: 0;
+    text-align: left;
+	width: 50%;
+}
+
+.propHeader>.propHeaderCell:first-child {
+    border-left: none;
+}
+
+.propHeader>.propHeaderCell:last-child {
+    border-right: none;
+}
+
+.propHeader {
+    background: #e5e5e5;
+    color: black;
+}
+
+.propRowCell {
+    border-left: 1px solid #f3f3f3;
+    width: 50%;
+}
+
+.propRow>.propRowCell {
+    background: #fff;
+}
+
+.propRowAtl>.propRowCell {
+    background: #fcfcfc;
+}
+
+.propEditor input:invalid {
+  border: 1px solid red;
+}

File diff suppressed because it is too large
+ 1 - 1
src/main/webapp/templates/network/active_directory.xml