1/**
2 * Code for the minimal UI theme.
3 */
4EditorUi.initMinimalTheme = function()
5{
6	// Disabled in lightbox and chromeless mode
7	if (urlParams['lightbox'] == '1' || urlParams['chrome'] == '0' || typeof window.Format === 'undefined' || typeof window.Menus === 'undefined')
8	{
9		window.uiTheme = null;
10
11		return;
12	}
13
14	var iw = 0;
15
16	try
17	{
18		iw = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
19	}
20	catch (e)
21	{
22		// ignore
23	}
24
25	/**
26	 *
27	 */
28	var WrapperWindow = function(editorUi, title, x, y, w, h, fn)
29	{
30	    var graph = editorUi.editor.graph;
31
32	    var div = document.createElement('div');
33	    div.className = 'geSidebarContainer';
34	    div.style.position = 'absolute';
35	    div.style.width = '100%';
36	    div.style.height = '100%';
37	    div.style.border = '1px solid whiteSmoke';
38	    div.style.overflowX = 'hidden';
39	    div.style.overflowY = 'auto';
40
41	    fn(div);
42
43	    this.window = new mxWindow(title, div, x, y, w, h, true, true);
44	    this.window.destroyOnClose = false;
45	    this.window.setMaximizable(false);
46	    this.window.setResizable(true);
47	    this.window.setClosable(true);
48	    this.window.setVisible(true);
49
50	    this.window.setLocation = function(x, y)
51	    {
52	    	var iiw = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
53	        var ih = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
54
55	        x = Math.max(0, Math.min(x, iiw - this.table.clientWidth));
56	        y = Math.max(0, Math.min(y, ih - this.table.clientHeight -
57				((urlParams['sketch'] == '1') ? 3 : 48)));
58
59	        if (this.getX() != x || this.getY() != y)
60	        {
61	            mxWindow.prototype.setLocation.apply(this, arguments);
62	        }
63	    };
64
65	    // Workaround for text selection starting in Safari
66	    // when dragging shapes outside of window
67	    if (mxClient.IS_SF)
68	    {
69		    this.window.div.onselectstart = mxUtils.bind(this, function(evt)
70		    {
71				if (evt == null)
72				{
73					evt = window.event;
74				}
75
76				return (evt != null && editorUi.isSelectionAllowed(evt));
77			});
78	    }
79	};
80
81	function toggleFormat(ui, visible)
82	{
83		var graph = ui.editor.graph;
84	    graph.popupMenuHandler.hideMenu();
85
86	    if (ui.formatWindow == null)
87	    {
88			ui.formatWindow = new WrapperWindow(ui, mxResources.get('format'),
89				(urlParams['sketch'] == '1') ? Math.max(10, ui.diagramContainer.clientWidth - 241) :
90				Math.max(10, ui.diagramContainer.clientWidth - 248), 60,
91				240, Math.min(566, graph.container.clientHeight - 10), function(container)
92			{
93				var format = ui.createFormat(container);
94				format.init();
95
96				ui.addListener('darkModeChanged', mxUtils.bind(this, function()
97				{
98					format.refresh();
99				}));
100
101				return format;
102			});
103
104			ui.formatWindow.window.minimumSize = new mxRectangle(0, 0, 240, 80);
105			ui.formatWindow.window.setVisible(true);
106	    }
107	    else
108	    {
109	        ui.formatWindow.window.setVisible((visible != null) ?
110	        	visible : !ui.formatWindow.window.isVisible());
111	    }
112
113        if (ui.formatWindow.window.isVisible() && urlParams['sketch'] != '1')
114        {
115            ui.formatWindow.window.fit();
116        }
117	};
118
119	function toggleShapes(ui, visible)
120	{
121		var graph = ui.editor.graph;
122	    graph.popupMenuHandler.hideMenu();
123	    var rect = new mxRectangle();
124
125	    if (ui.sidebarWindow == null)
126	    {
127			var w = Math.min(graph.container.clientWidth - 10, 218);
128
129			ui.sidebarWindow = new WrapperWindow(ui, mxResources.get('shapes'), 10,
130				(urlParams['sketch'] == '1' && urlParams['embedInline'] != '1') ? 15 : 56,
131				w - 6, Math.min(650, graph.container.clientHeight - 30),
132				function(container)
133			{
134				var div = document.createElement('div');
135				div.style.cssText = 'position:absolute;left:0;right:0;border-top:1px solid lightgray;' +
136					'height:24px;bottom:31px;text-align:center;cursor:pointer;padding:6px 0 0 0;';
137				div.className = 'geTitle';
138				div.innerHTML = '<span style="font-size:18px;margin-right:5px;">+</span>';
139				mxUtils.write(div, mxResources.get('moreShapes'));
140				container.appendChild(div);
141
142				mxEvent.addListener(div, 'click', function()
143				{
144					ui.actions.get('shapes').funct();
145				});
146
147				var menuObj = new Menubar(ui, container);
148
149				function addMenu(id, label)
150				{
151					var menu = ui.menus.get(id);
152
153					var elt = menuObj.addMenu(label, mxUtils.bind(this, function()
154					{
155						// Allows extensions of menu.functid
156						menu.funct.apply(this, arguments);
157					}));
158
159					elt.style.cssText = 'position:absolute;border-top:1px solid lightgray;width:50%;' +
160						'height:24px;bottom:0px;text-align:center;cursor:pointer;padding:6px 0 0 0;cusor:pointer;';
161					elt.className = 'geTitle';
162					container.appendChild(elt);
163
164					return elt;
165				}
166
167				if (Editor.enableCustomLibraries && (urlParams['embed'] != '1' || urlParams['libraries'] == '1'))
168				{
169					// Defined in native apps together with openLibrary
170					if (ui.actions.get('newLibrary') != null)
171					{
172						var div = document.createElement('div');
173						div.style.cssText = 'position:absolute;left:0px;width:50%;border-top:1px solid lightgray;' +
174							'height:30px;bottom:0px;text-align:center;cursor:pointer;padding:0px;';
175						div.className = 'geTitle';
176						var span = document.createElement('span');
177						span.style.cssText = 'position:relative;top:6px;';
178						mxUtils.write(span, mxResources.get('newLibrary'));
179						div.appendChild(span);
180						container.appendChild(div);
181
182						mxEvent.addListener(div, 'click', ui.actions.get('newLibrary').funct);
183
184						var div = document.createElement('div');
185						div.style.cssText = 'position:absolute;left:50%;width:50%;border-top:1px solid lightgray;' +
186							'height:30px;bottom:0px;text-align:center;cursor:pointer;padding:0px;border-left: 1px solid lightgray;';
187						div.className = 'geTitle';
188						var span = document.createElement('span');
189						span.style.cssText = 'position:relative;top:6px;';
190						mxUtils.write(span, mxResources.get('openLibrary'));
191						div.appendChild(span);
192						container.appendChild(div);
193
194						mxEvent.addListener(div, 'click', ui.actions.get('openLibrary').funct);
195					}
196					else
197					{
198						var elt = addMenu('newLibrary', mxResources.get('newLibrary'));
199						elt.style.boxSizing = 'border-box';
200						elt.style.paddingRight = '6px';
201						elt.style.paddingLeft = '6px';
202						elt.style.height = '32px';
203						elt.style.left = '0';
204
205						var elt = addMenu('openLibraryFrom', mxResources.get('openLibraryFrom'));
206						elt.style.borderLeft = '1px solid lightgray';
207						elt.style.boxSizing = 'border-box';
208						elt.style.paddingRight = '6px';
209						elt.style.paddingLeft = '6px';
210						elt.style.height = '32px';
211						elt.style.left = '50%';
212					}
213				}
214				else
215				{
216					div.style.bottom = '0';
217				}
218
219				container.appendChild(ui.sidebar.container);
220				container.style.overflow = 'hidden';
221
222				return container;
223			});
224
225			ui.sidebarWindow.window.minimumSize = new mxRectangle(0, 0, 90, 90);
226			ui.sidebarWindow.window.setVisible(true);
227
228			ui.getLocalData('sidebar', function(value)
229			{
230				ui.sidebar.showEntries(value, null, true);
231			});
232
233			ui.restoreLibraries();
234		}
235		else
236		{
237    		ui.sidebarWindow.window.setVisible((visible != null) ?
238    			visible : !ui.sidebarWindow.window.isVisible());
239		}
240
241		if (ui.sidebarWindow.window.isVisible())
242		{
243			ui.sidebarWindow.window.fit();
244		}
245	};
246
247    // Changes colors for some UI elements
248	var fill = '#29b6f2';
249	var stroke = '#ffffff';
250
251	Editor.checkmarkImage = Graph.createSvgImage(22, 18, '<path transform="translate(4 0)" d="M7.181,15.007a1,1,0,0,1-.793-0.391L3.222,10.5A1,1,0,1,1,4.808,9.274L7.132,12.3l6.044-8.86A1,1,0,1,1,14.83,4.569l-6.823,10a1,1,0,0,1-.8.437H7.181Z" fill="' + fill + '"/>').src;
252	mxWindow.prototype.closeImage = Graph.createSvgImage(18, 10, '<path d="M 5 1 L 13 9 M 13 1 L 5 9" stroke="#C0C0C0" stroke-width="2"/>').src;
253	mxWindow.prototype.minimizeImage = Graph.createSvgImage(14, 10, '<path d="M 3 7 L 7 3 L 11 7" stroke="#C0C0C0" stroke-width="2" fill="none"/>').src;
254	mxWindow.prototype.normalizeImage = Graph.createSvgImage(14, 10, '<path d="M 3 3 L 7 7 L 11 3" stroke="#C0C0C0" stroke-width="2" fill="none"/>').src;
255	mxConstraintHandler.prototype.pointImage = Graph.createSvgImage(5, 5,
256		'<path d="m 0 0 L 5 5 M 0 5 L 5 0" stroke-width="2" style="stroke-opacity:0.4" stroke="#ffffff"/>' +
257		'<path d="m 0 0 L 5 5 M 0 5 L 5 0" stroke="' + fill + '"/>');
258	mxOutline.prototype.sizerImage = null;
259
260	mxConstants.VERTEX_SELECTION_COLOR = '#C0C0C0';
261	mxConstants.EDGE_SELECTION_COLOR = '#C0C0C0';
262	mxConstants.CONNECT_HANDLE_FILLCOLOR = '#cee7ff';
263
264	mxConstants.DEFAULT_VALID_COLOR = fill;
265	mxConstants.GUIDE_COLOR = '#C0C0C0';
266
267	mxConstants.HIGHLIGHT_STROKEWIDTH = 5;
268	mxConstants.HIGHLIGHT_OPACITY = 35;
269	mxConstants.OUTLINE_COLOR = '#29b6f2';
270	mxConstants.OUTLINE_HANDLE_FILLCOLOR = '#29b6f2';
271	mxConstants.OUTLINE_HANDLE_STROKECOLOR = '#fff';
272
273	Graph.prototype.svgShadowColor = '#3D4574';
274	Graph.prototype.svgShadowOpacity = '0.4';
275	Graph.prototype.svgShadowSize = '0.6';
276	Graph.prototype.svgShadowBlur = '1.2';
277
278	Format.inactiveTabBackgroundColor = '#f0f0f0';
279	mxGraphHandler.prototype.previewColor = '#C0C0C0';
280	mxRubberband.prototype.defaultOpacity = 50;
281	HoverIcons.prototype.inactiveOpacity = 25;
282	Format.prototype.showCloseButton = false;
283	EditorUi.prototype.closableScratchpad = false;
284	EditorUi.prototype.toolbarHeight = (urlParams['sketch'] == '1') ? 1 : 46;
285	EditorUi.prototype.footerHeight = 0;
286	Graph.prototype.editAfterInsert = urlParams['sketch'] != '1' &&
287		!mxClient.IS_IOS && !mxClient.IS_ANDROID;
288
289	/**
290	 * Dynamic change of dark mode.
291	 */
292	EditorUi.prototype.setDarkMode = function(value)
293	{
294		if (this.spinner.spin(document.body, mxResources.get('working') + '...'))
295		{
296			window.setTimeout(mxUtils.bind(this, function()
297			{
298				this.spinner.stop();
299				this.doSetDarkMode(value);
300
301				// Persist setting
302				if (urlParams['dark'] == null)
303				{
304					mxSettings.settings.darkMode = value;
305					mxSettings.save();
306				}
307
308				this.fireEvent(new mxEventObject('darkModeChanged'));
309			}), 0);
310		}
311	};
312
313	/**
314	 * Links to dark.css
315	 */
316	var darkStyle = document.createElement('link');
317	darkStyle.setAttribute('rel', 'stylesheet');
318	darkStyle.setAttribute('href', STYLE_PATH + '/dark.css');
319	darkStyle.setAttribute('charset', 'UTF-8');
320	darkStyle.setAttribute('type', 'text/css');
321
322	/**
323	 * Dynamic change of dark mode.
324	 */
325	EditorUi.prototype.doSetDarkMode = function(value)
326	{
327		if (Editor.darkMode != value)
328		{
329			var graph = this.editor.graph;
330			Editor.darkMode = value;
331
332			// Sets instance vars and graph stylesheet
333			this.spinner.opts.color = Editor.isDarkMode() ? '#c0c0c0' : '#000';
334			this.setGridColor(Editor.isDarkMode() ? graph.view.defaultDarkGridColor : graph.view.defaultGridColor);
335			graph.defaultPageBackgroundColor = (urlParams['embedInline'] == '1') ? 'transparent' :
336				Editor.isDarkMode() ? Editor.darkColor : '#ffffff';
337			graph.defaultPageBorderColor = Editor.isDarkMode() ? '#505759' : '#ffffff';
338			graph.shapeBackgroundColor = Editor.isDarkMode() ? Editor.darkColor : '#ffffff';
339			graph.shapeForegroundColor = Editor.isDarkMode() ? Editor.lightColor : '#000000';
340			graph.defaultThemeName = Editor.isDarkMode() ? 'darkTheme' : 'default-style2';
341			graph.graphHandler.previewColor = Editor.isDarkMode() ? '#cccccc' : 'black';
342			document.body.style.backgroundColor = (urlParams['embedInline'] == '1') ? 'transparent' :
343				(Editor.isDarkMode() ? Editor.darkColor : '#ffffff');
344			graph.loadStylesheet();
345
346			// Destroys windows with code for dark mode
347		    if (this.actions.layersWindow != null)
348		    {
349				var wasVisible = this.actions.layersWindow.window.isVisible();
350
351		    	this.actions.layersWindow.window.setVisible(false);
352		    	this.actions.layersWindow.destroy();
353		    	this.actions.layersWindow = null;
354
355				if (wasVisible)
356				{
357					window.setTimeout(this.actions.get('layers').funct, 0);
358				}
359		    }
360
361			if (this.menus.commentsWindow != null)
362			{
363		    	this.menus.commentsWindow.window.setVisible(false);
364				this.menus.commentsWindow.destroy();
365				this.menus.commentsWindow = null;
366			}
367
368			if (this.ruler != null)
369			{
370				this.ruler.updateStyle();
371			}
372
373			// Sets global vars
374			Graph.prototype.defaultPageBackgroundColor = graph.defaultPageBackgroundColor;
375			Graph.prototype.defaultPageBorderColor = graph.defaultPageBorderColor;
376			Graph.prototype.shapeBackgroundColor = graph.shapeBackgroundColor;
377			Graph.prototype.shapeForegroundColor = graph.shapeForegroundColor;
378			Graph.prototype.defaultThemeName = graph.defaultThemeName;
379			StyleFormatPanel.prototype.defaultStrokeColor = Editor.isDarkMode() ? '#cccccc' : 'black';
380			BaseFormatPanel.prototype.buttonBackgroundColor = Editor.isDarkMode() ? Editor.darkColor : 'white';
381			Format.inactiveTabBackgroundColor = Editor.isDarkMode() ? 'black' : '#f0f0f0';
382			Dialog.backdropColor = Editor.isDarkMode() ? Editor.darkColor : 'white';
383			mxConstants.DROP_TARGET_COLOR = Editor.isDarkMode() ? '#00ff00' : '#0000FF';
384			Editor.helpImage = (Editor.isDarkMode() && mxClient.IS_SVG) ?
385				Editor.darkHelpImage : Editor.lightHelpImage;
386			Editor.checkmarkImage = (Editor.isDarkMode() && mxClient.IS_SVG) ?
387				Editor.darkCheckmarkImage : Editor.lightCheckmarkImage;
388
389			// Updates CSS
390			styleElt.innerHTML = Editor.createMinimalCss();
391
392			// Adds or removes link to CSS
393			if (Editor.darkMode)
394			{
395				if (darkStyle.parentNode == null)
396				{
397					var head = document.getElementsByTagName('head')[0];
398					head.appendChild(darkStyle);
399				}
400			}
401			else if (darkStyle.parentNode != null)
402			{
403				darkStyle.parentNode.removeChild(darkStyle);
404			}
405		}
406	};
407
408	/**
409	 * Dynamic change of dark mode.
410	 */
411	Editor.createMinimalCss = function()
412	{
413		return '* { -webkit-font-smoothing: antialiased; }' +
414			'html body td.mxWindowTitle > div > img { padding: 8px 4px; }' +
415			// Dark mode styles
416			(Editor.isDarkMode() ?
417			'html body td.mxWindowTitle > div > img { margin: -4px; }' +
418			'html body .geToolbarContainer .geMenuItem, html body .geToolbarContainer .geToolbarButton, ' +
419			'html body .geMenubarContainer .geMenuItem .geMenuItem, html body .geMenubarContainer a.geMenuItem,' +
420			'html body .geMenubarContainer .geToolbarButton { filter: invert(1); }' +
421			'html body div.geToolbarContainer a.geInverted { filter: none; }' +
422			'html body .geMenubarContainer .geMenuItem .geMenuItem, html body .geMenubarContainer a.geMenuItem { color: #353535; }' +
423			'html > body > div > .geToolbarContainer { border: 1px solid #c0c0c0 !important; box-shadow: none !important; }' +
424			'html > body.geEditor > div > a.geItem { background-color: #2a2a2a; color: #cccccc; border-color: #505759; }' +
425			'html body .geTabContainer, html body .geTabContainer div, html body .geMenubarContainer { border-color: #505759 !important; }' +
426			'html body .mxCellEditor { color: #f0f0f0; }'
427			:
428			// Non-dark mode styles
429			'html body div.geToolbarContainer a.geInverted { filter: invert(1); }' +
430			'html body.geEditor .geTabContainer div { border-color: #e5e5e5 !important; }'
431			) +
432			// End of custom styles
433			'html > body > div > a.geItem { background-color: #ffffff; color: #707070; border-top: 1px solid lightgray; border-left: 1px solid lightgray; }' +
434			'html body .geMenubarContainer { border-bottom:1px solid lightgray;background-color:#ffffff; }' +
435			'html body .mxWindow button.geBtn { font-size:12px !important; margin-left: 0; }' +
436			'html body table.mxWindow td.mxWindowPane div.mxWindowPane *:not(svg *) { font-size:9pt; }' +
437			'table.mxWindow * :not(svg *) { font-size:13px; }' +
438			'html body .mxWindow { z-index: 3; }' +
439			'html body div.diagramContainer button, html body button.geBtn { font-size:14px; font-weight:700; border-radius: 5px; }' +
440			'html body button.geBtn:active { opacity: 0.6; }' +
441			'html body a.geMenuItem { opacity: 0.75; cursor: pointer; user-select: none; }' +
442			'html body a.geMenuItem[disabled] { opacity: 0.2; }' +
443			'html body a.geMenuItem[disabled]:active { opacity: 0.2; }' +
444			'html body div.geActivePage { opacity: 0.7; }' +
445			'html body a.geMenuItem:active { opacity: 0.2; }' +
446			'html body .geToolbarButton { opacity: 0.3; }' +
447			'html body .geToolbarButton:active { opacity: 0.15; }' +
448			'html body .geStatus:active { opacity: 0.5; }' +
449			'html body .geStatus { padding-top:3px !important; }' +
450			'html body .geMenubarContainer .geStatus { margin-top: 0px !important; }' +
451			'html table.mxPopupMenu tr.mxPopupMenuItemHover:active { opacity: 0.7; }' +
452			'html body .geDialog input, html body .geToolbarContainer input, html body .mxWindow input {padding: 2px; display: inline-block; }' +
453			'html body .mxWindow input[type="checkbox"] {padding: 0px; }' +
454			'div.geDialog { border-radius: 5px; }' +
455			'html body div.geDialog button.geBigButton { color: ' + (Editor.isDarkMode() ? Editor.darkColor : '#fff') + ' !important; border: none !important; }' +
456			'.mxWindow button, .geDialog select, .mxWindow select { display:inline-block; }' +
457			'html body .mxWindow .geColorBtn, html body .geDialog .geColorBtn { background: none; }' +
458			'html body div.diagramContainer button, html body .mxWindow button, html body .geDialog button { min-width: 0px; border-radius: 5px; color: ' + (Editor.isDarkMode() ? '#cccccc' : '#353535') + ' !important; border-style: solid; border-width: 1px; border-color: rgb(216, 216, 216); }' +
459			'html body div.diagramContainer button:hover, html body .mxWindow button:hover, html body .geDialog button:hover { border-color: rgb(177, 177, 177); }' +
460			'html body div.diagramContainer button:active, html body .mxWindow button:active, html body .geDialog button:active { opacity: 0.6; }' +
461			'div.diagramContainer button.geBtn, .mxWindow button.geBtn, .geDialog button.geBtn { min-width:72px; font-weight: 600; background: none; }' +
462			'div.diagramContainer button.gePrimaryBtn, .mxWindow button.gePrimaryBtn, .geDialog button.gePrimaryBtn, html body .gePrimaryBtn { background: #29b6f2; color: #fff !important; border: none; box-shadow: none; }' +
463			'html body .gePrimaryBtn:hover { background: #29b6f2; border: none; box-shadow: inherit; }' +
464			'html body button.gePrimaryBtn:hover { background: #29b6f2; border: none; }' +
465			'.geBtn button { min-width:72px !important; }' +
466			'div.geToolbarContainer a.geButton { margin:0px; padding: 0 2px 4px 2px; } ' +
467			'html body div.geToolbarContainer a.geColorBtn { margin: 2px; } ' +
468			'html body .mxWindow td.mxWindowPane input, html body .mxWindow td.mxWindowPane select, html body .mxWindow td.mxWindowPane textarea, html body .mxWindow td.mxWindowPane radio { padding: 0px; box-sizing: border-box; }' +
469			'.geDialog, .mxWindow td.mxWindowPane *, div.geSprite, td.mxWindowTitle, .geDiagramContainer { box-sizing:content-box; }' +
470			'.mxWindow div button.geStyleButton { box-sizing: border-box; }' +
471			'table.mxWindow td.mxWindowPane button.geColorBtn { padding:0px; box-sizing: border-box; }' +
472			'td.mxWindowPane .geSidebarContainer button { padding:2px; box-sizing: border-box; }' +
473			'html body .geMenuItem { font-size:14px; text-decoration: none; font-weight: normal; padding: 6px 10px 6px 10px; border: none; border-radius: 5px; color: #353535; box-shadow: inset 0 0 0 1px rgba(0,0,0,.11), inset 0 -1px 0 0 rgba(0,0,0,.08), 0 1px 2px 0 rgba(0,0,0,.04); }' +
474			// Styling for Minimal
475			'.geTabContainer { border-bottom:1px solid lightgray; border-top:1px solid lightgray; background: ' + (Editor.isDarkMode() ? Editor.darkColor : '#fff') + ' !important; }' +
476			'.geToolbarContainer { background: ' + (Editor.isDarkMode() ? Editor.darkColor : '#fff') + '; }' +
477			'div.geSidebarContainer { background-color: ' + (Editor.isDarkMode() ? Editor.darkColor : '#fff') + '; }' +
478			'div.geSidebarContainer .geTitle { background-color: ' + (Editor.isDarkMode() ? Editor.darkColor : '#fdfdfd') + '; }' +
479			'div.mxWindow td.mxWindowPane button { background-image: none; float: none; }' +
480			'td.mxWindowTitle { height: 22px !important; background: none !important; font-size: 13px !important; text-align:center !important; border-bottom:1px solid lightgray; }' +
481			'div.mxWindow, div.mxWindowTitle { background-image: none !important; background-color:' + (Editor.isDarkMode() ? Editor.darkColor : '#fff') + ' !important; }' +
482			'div.mxWindow { border-radius:5px; box-shadow: 0px 0px 2px #C0C0C0 !important;}' +
483			'div.mxWindow *:not(svg *) { font-family: inherit !important; }' +
484			// Minimal Style UI
485			'html div.geVerticalHandle { position:absolute;bottom:0px;left:50%;cursor:row-resize;width:11px;height:11px;background:white;margin-bottom:-6px; margin-left:-6px; border: none; border-radius: 6px; box-shadow: inset 0 0 0 1px rgba(0,0,0,.11), inset 0 -1px 0 0 rgba(0,0,0,.08), 0 1px 2px 0 rgba(0,0,0,.04); }' +
486			'html div.geInactivePage { background: ' + (Editor.isDarkMode() ? Editor.darkColor : 'rgb(249, 249, 249)') + ' !important; color: #A0A0A0 !important; } ' +
487			'html div.geActivePage { background:  ' + (Editor.isDarkMode() ? Editor.darkColor : '#fff') + ' !important;  ' + (Editor.isDarkMode() ? '' : 'color: #353535 !important; } ') +
488			'html div.mxRubberband { border:1px solid; border-color: #29b6f2 !important; background:rgba(41,182,242,0.4) !important; } ' +
489			'html body div.mxPopupMenu { border-radius:5px; border:1px solid #c0c0c0; padding:5px 0 5px 0; box-shadow: 0px 4px 17px -4px rgba(96,96,96,1); } ' +
490			'html table.mxPopupMenu td.mxPopupMenuItem { color: ' + (Editor.isDarkMode() ? '#cccccc' : '#353535') + '; font-size: 14px; padding-top: 4px; padding-bottom: 4px; }' +
491			'html table.mxPopupMenu tr.mxPopupMenuItemHover { background-color: ' + (Editor.isDarkMode() ? '#000000' : '#29b6f2') + '; }' +
492			'html tr.mxPopupMenuItemHover td.mxPopupMenuItem, html tr.mxPopupMenuItemHover td.mxPopupMenuItem span { color: ' + (Editor.isDarkMode() ? '#cccccc' : '#ffffff') + ' !important; }' +
493			'html tr.mxPopupMenuItem, html td.mxPopupMenuItem { transition-property: none !important; }' +
494			'html table.mxPopupMenu hr { height: 2px; background-color: rgba(0,0,0,.07); margin: 5px 0; }' +
495			'html body td.mxWindowTitle { padding-right: 14px; }' +
496			'html td.mxWindowTitle div { top: 0px !important; }' +
497			// Fixes checkbox and radio size on iOS
498			((mxClient.IS_IOS) ? 'html input[type=checkbox], html input[type=radio] { height:12px; }' : '') +
499			((urlParams['sketch'] == '1') ? 'a.geStatus > div { overflow: hidden; text-overflow: ellipsis; max-width: 100%; }' : '');
500	};
501
502	var styleElt = document.createElement('style')
503	styleElt.type = 'text/css';
504	styleElt.innerHTML = Editor.createMinimalCss();
505	document.getElementsByTagName('head')[0].appendChild(styleElt);
506
507	/**
508     * Sets the XML node for the current diagram.
509     */
510    Editor.prototype.isChromelessView = function()
511    {
512    	return false;
513    };
514
515    /**
516     * Sets the XML node for the current diagram.
517     */
518    Graph.prototype.isLightboxView = function()
519    {
520    	return false;
521    };
522
523    // Overridden to ignore tabContainer height for diagramContainer
524    var editorUiUpdateTabContainer = EditorUi.prototype.updateTabContainer;
525
526    EditorUi.prototype.updateTabContainer = function()
527    {
528    	if (this.tabContainer != null)
529        {
530        	// Makes room for view zoom menu
531        	this.tabContainer.style.right = '70px';
532        	this.diagramContainer.style.bottom = (urlParams['sketch'] == '1') ?
533				'0px' : this.tabContainerHeight + 'px';
534        }
535
536    	editorUiUpdateTabContainer.apply(this, arguments);
537    };
538
539    // Overridden to update save menu state
540	/**
541	 * Updates action states depending on the selection.
542	 */
543	var editorUiUpdateActionStates = EditorUi.prototype.updateActionStates;
544
545	EditorUi.prototype.updateActionStates = function()
546	{
547		editorUiUpdateActionStates.apply(this, arguments);
548
549		this.menus.get('save').setEnabled(this.getCurrentFile() != null || urlParams['embed'] == '1');
550	};
551
552    // Hides keyboard shortcuts in menus
553    var menusAddShortcut = Menus.prototype.addShortcut;
554
555    Menus.prototype.addShortcut = function(item, action)
556    {
557        if (action.shortcut != null && iw < 900 && !mxClient.IS_IOS)
558        {
559            var td = item.firstChild.nextSibling;
560            td.setAttribute('title', action.shortcut);
561        }
562        else
563        {
564        	menusAddShortcut.apply(this, arguments);
565        }
566    };
567
568    var appUpdateUserElement = App.prototype.updateUserElement;
569
570    App.prototype.updateUserElement = function()
571    {
572    	appUpdateUserElement.apply(this, arguments);
573
574		if (this.userElement != null)
575		{
576			var elt = this.userElement;
577    		elt.style.cssText = 'position:relative;margin-right:4px;cursor:pointer;display:' + elt.style.display;
578    		elt.className = 'geToolbarButton';
579    		elt.innerHTML = '';
580			elt.style.backgroundImage = 'url(' + Editor.userImage + ')';
581        	elt.style.backgroundPosition = 'center center';
582        	elt.style.backgroundRepeat = 'no-repeat';
583        	elt.style.backgroundSize = '24px 24px';
584        	elt.style.height = '24px';
585        	elt.style.width = '24px';
586        	elt.style.cssFloat = 'right';
587        	elt.setAttribute('title', mxResources.get('changeUser'));
588
589        	if (elt.style.display != 'none')
590        	{
591        		elt.style.display = 'inline-block';
592        	}
593		}
594    };
595
596    var appUpdateButtonContainer = App.prototype.updateButtonContainer;
597
598    App.prototype.updateButtonContainer = function()
599    {
600    	appUpdateButtonContainer.apply(this, arguments);
601
602    	if (this.shareButton != null)
603		{
604    		var elt = this.shareButton;
605    		elt.style.cssText = 'display:inline-block;position:relative;box-sizing:border-box;margin-right:4px;cursor:pointer;';
606    		elt.className = 'geToolbarButton';
607    		elt.innerHTML = '';
608			elt.style.backgroundImage = 'url(' + Editor.shareImage + ')';
609        	elt.style.backgroundPosition = 'center center';
610        	elt.style.backgroundRepeat = 'no-repeat';
611        	elt.style.backgroundSize = '24px 24px';
612        	elt.style.height = '24px';
613        	elt.style.width = '24px';
614
615			// Share button hidden via CSS to enable notifications button
616			if (urlParams['sketch'] == '1')
617			{
618				this.shareButton.style.display = 'none';
619			}
620		}
621
622		if (this.buttonContainer != null)
623		{
624			this.buttonContainer.style.marginTop = '-2px';
625			this.buttonContainer.style.paddingTop = '4px';
626		}
627    };
628
629	EditorUi.prototype.addEmbedButtons = function()
630	{
631		if (this.buttonContainer != null && urlParams['embedInline'] != '1')
632		{
633			var div = document.createElement('div');
634			div.style.display = 'inline-block';
635			div.style.position = 'relative';
636			div.style.marginTop = '6px';
637			div.style.marginRight = '4px';
638
639			var button = document.createElement('a');
640			button.className = 'geMenuItem gePrimaryBtn';
641			button.style.marginLeft = '8px';
642			button.style.padding = '6px';
643
644			if (urlParams['noSaveBtn'] == '1')
645			{
646				if (urlParams['saveAndExit'] != '0')
647				{
648					var saveAndExitTitle = urlParams['publishClose'] == '1' ? mxResources.get('publish') : mxResources.get('saveAndExit');
649					mxUtils.write(button, saveAndExitTitle);
650					button.setAttribute('title', saveAndExitTitle);
651
652					mxEvent.addListener(button, 'click', mxUtils.bind(this, function()
653					{
654						this.actions.get('saveAndExit').funct();
655					}));
656
657					div.appendChild(button);
658				}
659			}
660			else
661			{
662				mxUtils.write(button, mxResources.get('save'));
663				button.setAttribute('title', mxResources.get('save') + ' (' + Editor.ctrlKey + '+S)');
664
665				mxEvent.addListener(button, 'click', mxUtils.bind(this, function()
666				{
667					this.actions.get('save').funct();
668				}));
669
670				div.appendChild(button);
671
672				if (urlParams['saveAndExit'] == '1')
673				{
674					button = document.createElement('a');
675					mxUtils.write(button, mxResources.get('saveAndExit'));
676					button.setAttribute('title', mxResources.get('saveAndExit'));
677					button.className = 'geMenuItem';
678					button.style.marginLeft = '6px';
679					button.style.padding = '6px';
680
681					mxEvent.addListener(button, 'click', mxUtils.bind(this, function()
682					{
683						this.actions.get('saveAndExit').funct();
684					}));
685
686					div.appendChild(button);
687				}
688			}
689
690			if (urlParams['noExitBtn'] != '1')
691			{
692				button = document.createElement('a');
693				var exitTitle = urlParams['publishClose'] == '1' ? mxResources.get('close') : mxResources.get('exit');
694				mxUtils.write(button, exitTitle);
695				button.setAttribute('title', exitTitle);
696				button.className = 'geMenuItem';
697				button.style.marginLeft = '6px';
698				button.style.padding = '6px';
699
700				mxEvent.addListener(button, 'click', mxUtils.bind(this, function()
701				{
702					this.actions.get('exit').funct();
703				}));
704
705				div.appendChild(button);
706			}
707
708			this.buttonContainer.appendChild(div);
709			this.buttonContainer.style.top = '6px';
710
711			this.editor.fireEvent(new mxEventObject('statusChanged'));
712		}
713	};
714
715	// Fixes sidebar tooltips (previews)
716	var sidebarGetTooltipOffset = Sidebar.prototype.getTooltipOffset;
717
718	Sidebar.prototype.getTooltipOffset = function(elt, bounds)
719	{
720		if (this.editorUi.sidebarWindow == null ||
721			mxUtils.isAncestorNode(this.editorUi.picker, elt))
722		{
723			var off = mxUtils.getOffset(this.editorUi.picker);
724
725			off.x += this.editorUi.picker.offsetWidth + 4;
726			off.y += elt.offsetTop - bounds.height / 2 + 16;
727
728			return off;
729		}
730		else
731		{
732			var result = sidebarGetTooltipOffset.apply(this, arguments);
733			var off = mxUtils.getOffset(this.editorUi.sidebarWindow.window.div);
734
735			result.x += off.x - 16;
736			result.y += off.y;
737
738			return result;
739		}
740	};
741
742    // Adds context menu items
743    var menuCreatePopupMenu = Menus.prototype.createPopupMenu;
744
745    Menus.prototype.createPopupMenu = function(menu, cell, evt)
746    {
747        var graph = this.editorUi.editor.graph;
748        menu.smartSeparators = true;
749        menuCreatePopupMenu.apply(this, arguments);
750
751		if (urlParams['sketch'] == '1')
752		{
753			if (graph.isEnabled())
754			{
755				menu.addSeparator();
756
757				if (graph.getSelectionCount() == 1)
758	        	{
759					this.addMenuItems(menu, ['-', 'lockUnlock'], null, evt);
760				}
761			}
762		}
763		else
764		{
765			if (graph.getSelectionCount() == 1)
766			{
767				if (graph.isCellFoldable(graph.getSelectionCell()))
768				{
769					this.addMenuItems(menu, (graph.isCellCollapsed(cell)) ? ['expand'] : ['collapse'], null, evt);
770				}
771
772				this.addMenuItems(menu, ['collapsible', '-', 'lockUnlock', 'enterGroup'], null, evt);
773				menu.addSeparator();
774				this.addSubmenu('layout', menu);
775	        }
776	        else if (graph.isSelectionEmpty() && graph.isEnabled())
777	        {
778				menu.addSeparator();
779				this.addMenuItems(menu, ['editData'], null, evt);
780				menu.addSeparator();
781				this.addSubmenu('layout', menu);
782				this.addSubmenu('insert', menu);
783				this.addMenuItems(menu, ['-', 'exitGroup'], null, evt);
784			}
785			else if (graph.isEnabled())
786			{
787				this.addMenuItems(menu, ['-', 'lockUnlock'], null, evt);
788			}
789		}
790
791		if (graph.isEnabled() && graph.isSelectionEmpty())
792		{
793			this.addMenuItems(menu, ['-', 'fullscreen']);
794
795			if (urlParams['embedInline'] != '1' && (Editor.isDarkMode() ||
796				(!mxClient.IS_IE && !mxClient.IS_IE11)))
797			{
798				this.addMenuItems(menu, ['toggleDarkMode']);
799			}
800		}
801	};
802
803	// Adds copy as image after paste for empty selection
804	var menuAddPopupMenuEditItems = Menus.prototype.addPopupMenuEditItems;
805
806	/**
807	 * Creates the keyboard event handler for the current graph and history.
808	 */
809	Menus.prototype.addPopupMenuEditItems = function(menu, cell, evt)
810	{
811		menuAddPopupMenuEditItems.apply(this, arguments);
812
813		if (this.editorUi.editor.graph.isSelectionEmpty())
814		{
815			this.addMenuItems(menu, ['copyAsImage'], null, evt);
816		}
817	};
818
819
820    // Overridden to toggle window instead
821    EditorUi.prototype.toggleFormatPanel = function(visible)
822    {
823        if (this.formatWindow != null)
824        {
825        	this.formatWindow.window.setVisible((visible != null) ?
826        		visible : !this.formatWindow.window.isVisible());
827        }
828        else
829        {
830        	toggleFormat(this);
831        }
832    };
833
834    DiagramFormatPanel.prototype.isMathOptionVisible = function()
835    {
836        return true;
837    };
838
839	// Initializes the user interface
840	var editorUiDestroy = EditorUi.prototype.destroy;
841	EditorUi.prototype.destroy = function()
842	{
843        if (this.sidebarWindow != null)
844        {
845            this.sidebarWindow.window.setVisible(false);
846            this.sidebarWindow.window.destroy();
847            this.sidebarWindow = null;
848        }
849
850        if (this.formatWindow != null)
851        {
852        	this.formatWindow.window.setVisible(false);
853        	this.formatWindow.window.destroy();
854        	this.formatWindow = null;
855        }
856
857        if (this.actions.outlineWindow != null)
858        {
859        	this.actions.outlineWindow.window.setVisible(false);
860        	this.actions.outlineWindow.window.destroy();
861        	this.actions.outlineWindow = null;
862        }
863
864        if (this.actions.layersWindow != null)
865        {
866        	this.actions.layersWindow.window.setVisible(false);
867        	this.actions.layersWindow.destroy();
868        	this.actions.layersWindow = null;
869        }
870
871        if (this.menus.tagsWindow != null)
872        {
873        	this.menus.tagsWindow.window.setVisible(false);
874        	this.menus.tagsWindow.window.destroy();
875        	this.menus.tagsWindow = null;
876        }
877
878        if (this.menus.findWindow != null)
879        {
880        	this.menus.findWindow.window.setVisible(false);
881        	this.menus.findWindow.window.destroy();
882        	this.menus.findWindow = null;
883        }
884
885        if (this.menus.findReplaceWindow != null)
886        {
887        	this.menus.findReplaceWindow.window.setVisible(false);
888        	this.menus.findReplaceWindow.window.destroy();
889        	this.menus.findReplaceWindow = null;
890        }
891
892		editorUiDestroy.apply(this, arguments);
893	};
894
895	// Hides windows when a file is closed
896	var editorUiSetGraphEnabled = EditorUi.prototype.setGraphEnabled;
897
898	EditorUi.prototype.setGraphEnabled = function(enabled)
899	{
900		editorUiSetGraphEnabled.apply(this, arguments);
901
902		if (!enabled)
903		{
904			if (this.sidebarWindow != null)
905            {
906            	this.sidebarWindow.window.setVisible(false);
907            }
908
909            if (this.formatWindow != null)
910            {
911            	this.formatWindow.window.setVisible(false);
912            }
913		}
914		else
915		{
916			var iw = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
917
918			if (iw >= 1000 && this.sidebarWindow != null && urlParams['sketch'] != '1')
919            {
920                this.sidebarWindow.window.setVisible(true);
921            }
922
923            if (this.formatWindow != null && (iw >= 1000 || urlParams['sketch'] == '1'))
924            {
925            	this.formatWindow.window.setVisible(true);
926            }
927		}
928	};
929
930    // Disables centering of graph after iframe resize
931	EditorUi.prototype.chromelessWindowResize = function() {};
932
933	// Adds actions and menus
934	var menusInit = Menus.prototype.init;
935	Menus.prototype.init = function()
936	{
937		menusInit.apply(this, arguments);
938
939        var ui = this.editorUi;
940        var graph = ui.editor.graph;
941
942        ui.actions.get('editDiagram').label = mxResources.get('formatXml') + '...';
943        ui.actions.get('createShape').label = mxResources.get('shape') + '...';
944        ui.actions.get('outline').label = mxResources.get('outline') + '...';
945        ui.actions.get('layers').label = mxResources.get('layers') + '...';
946        ui.actions.get('tags').label = mxResources.get('tags') + '...';
947		ui.actions.get('forkme').visible = urlParams['sketch'] != '1';
948		ui.actions.get('downloadDesktop').visible = urlParams['sketch'] != '1';
949
950        var toggleDarkModeAction = ui.actions.put('toggleDarkMode', new Action(mxResources.get('dark'), function(e)
951        {
952            ui.setDarkMode(!Editor.darkMode);
953        }));
954
955		toggleDarkModeAction.setToggleAction(true);
956		toggleDarkModeAction.setSelectedCallback(function() { return Editor.isDarkMode(); });
957
958        var toggleSketchModeAction = ui.actions.put('toggleSketchMode', new Action(mxResources.get('sketch'), function(e)
959        {
960            ui.setSketchMode(!Editor.sketchMode);
961        }));
962
963		toggleSketchModeAction.setToggleAction(true);
964		toggleSketchModeAction.setSelectedCallback(function() { return Editor.sketchMode; });
965
966        var togglePagesAction = ui.actions.put('togglePagesVisible', new Action(mxResources.get('pages'), function(e)
967        {
968            ui.setPagesVisible(!Editor.pagesVisible);
969        }));
970
971		togglePagesAction.setToggleAction(true);
972		togglePagesAction.setSelectedCallback(function() { return Editor.pagesVisible; });
973
974        ui.actions.put('importCsv', new Action(mxResources.get('csv') + '...', function()
975        {
976            graph.popupMenuHandler.hideMenu();
977            ui.showImportCsvDialog();
978        }));
979        ui.actions.put('importText', new Action(mxResources.get('text') + '...', function()
980        {
981            var dlg = new ParseDialog(ui, 'Insert from Text');
982            ui.showDialog(dlg.container, 620, 420, true, false);
983            dlg.init();
984        }));
985        ui.actions.put('formatSql', new Action(mxResources.get('formatSql') + '...', function()
986        {
987            var dlg = new ParseDialog(ui, 'Insert from Text', 'formatSql');
988            ui.showDialog(dlg.container, 620, 420, true, false);
989            dlg.init();
990        }));
991
992        ui.actions.put('toggleShapes', new Action(mxResources.get((urlParams['sketch'] == '1') ?
993			'moreShapes' : 'shapes') + '...', function()
994        {
995        	toggleShapes(ui);
996        }, null, null, Editor.ctrlKey + '+Shift+K'));
997
998        var action = ui.actions.put('toggleFormat', new Action(mxResources.get('format') + '...', function()
999        {
1000        	toggleFormat(ui);
1001        }));
1002		action.shortcut = ui.actions.get('formatPanel').shortcut;
1003
1004        if (EditorUi.enablePlantUml && !ui.isOffline())
1005        {
1006	        ui.actions.put('plantUml', new Action(mxResources.get('plantUml') + '...', function()
1007	        {
1008	            var dlg = new ParseDialog(ui, mxResources.get('plantUml') + '...', 'plantUml');
1009	            ui.showDialog(dlg.container, 620, 420, true, false);
1010	            dlg.init();
1011	        }));
1012        }
1013
1014    	ui.actions.put('mermaid', new Action(mxResources.get('mermaid') + '...', function()
1015        {
1016            var dlg = new ParseDialog(ui, mxResources.get('mermaid') + '...', 'mermaid');
1017            ui.showDialog(dlg.container, 620, 420, true, false);
1018            dlg.init();
1019        }));
1020
1021		// Adds submenu for edit items
1022		var addPopupMenuCellEditItems = this.addPopupMenuCellEditItems;
1023
1024		this.put('editCell', new Menu(mxUtils.bind(this, function(menu, parent)
1025		{
1026			var graph = this.editorUi.editor.graph;
1027			var cell = graph.getSelectionCell();
1028			addPopupMenuCellEditItems.call(this, menu, cell, null, parent);
1029
1030			this.addMenuItems(menu, ['editTooltip'], parent);
1031
1032			if (graph.model.isVertex(cell))
1033			{
1034				this.addMenuItems(menu, ['editGeometry'], parent);
1035			}
1036
1037			this.addMenuItems(menu, ['-', 'edit'], parent);
1038		})));
1039
1040		this.addPopupMenuCellEditItems = function(menu, cell, evt, parent)
1041		{
1042			// LATER: Pass-through for evt from context menu to submenu item
1043			menu.addSeparator();
1044			this.addSubmenu('editCell', menu, parent, mxResources.get('edit'));
1045		};
1046
1047        this.put('diagram', new Menu(mxUtils.bind(this, function(menu, parent)
1048        {
1049			var file = ui.getCurrentFile();
1050        	ui.menus.addSubmenu('extras', menu, parent, mxResources.get('preferences'));
1051			menu.addSeparator(parent);
1052
1053			if (mxClient.IS_CHROMEAPP || EditorUi.isElectronApp)
1054			{
1055				ui.menus.addMenuItems(menu, ['new', 'open', '-', 'synchronize',
1056					'-', 'save', 'saveAs', '-'], parent);
1057			}
1058			else if (urlParams['embed'] == '1')
1059			{
1060				if (urlParams['noSaveBtn'] != '1' &&
1061					urlParams['embedInline'] != '1')
1062				{
1063					ui.menus.addMenuItems(menu, ['-', 'save'], parent);
1064				}
1065
1066				if (urlParams['saveAndExit'] == '1' ||
1067					(urlParams['noSaveBtn'] == '1' &&
1068					urlParams['saveAndExit'] != '0'))
1069				{
1070					ui.menus.addMenuItems(menu, ['saveAndExit'], parent);
1071				}
1072
1073				menu.addSeparator(parent);
1074			}
1075			else if (ui.mode == App.MODE_ATLAS)
1076			{
1077				ui.menus.addMenuItems(menu, ['save', 'synchronize', '-'], parent);
1078			}
1079			else if (urlParams['noFileMenu'] != '1')
1080			{
1081	        	ui.menus.addMenuItems(menu, ['new'], parent);
1082				ui.menus.addSubmenu('openFrom', menu, parent);
1083
1084				if (isLocalStorage)
1085				{
1086					this.addSubmenu('openRecent', menu, parent);
1087				}
1088
1089				if (urlParams['sketch'] != '1')
1090				{
1091					menu.addSeparator(parent);
1092
1093					if (file != null && file.constructor == DriveFile)
1094					{
1095						ui.menus.addMenuItems(menu, ['share'], parent);
1096					}
1097
1098					if (!mxClient.IS_CHROMEAPP && !EditorUi.isElectronApp &&
1099						file != null && file.constructor != LocalFile)
1100					{
1101						ui.menus.addMenuItems(menu, ['synchronize'], parent);
1102					}
1103				}
1104
1105				menu.addSeparator(parent);
1106				ui.menus.addSubmenu('save', menu, parent);
1107			}
1108
1109			ui.menus.addSubmenu('exportAs', menu, parent);
1110
1111            if (mxClient.IS_CHROMEAPP || EditorUi.isElectronApp)
1112            {
1113            	ui.menus.addMenuItems(menu, ['import'], parent);
1114            }
1115            else if (urlParams['noFileMenu'] != '1')
1116            {
1117            	ui.menus.addSubmenu('importFrom', menu, parent);
1118            }
1119
1120			if (urlParams['sketch'] != '1')
1121			{
1122				ui.menus.addMenuItems(menu, ['-', 'outline'], parent);
1123
1124				if (ui.commentsSupported())
1125				{
1126					ui.menus.addMenuItems(menu, ['comments'], parent);
1127				}
1128			}
1129
1130			ui.menus.addMenuItems(menu, ['-', 'findReplace', 'layers', 'tags'], parent);
1131
1132			ui.menus.addMenuItems(menu, ['-', 'pageSetup', 'pageScale'], parent);
1133
1134			// Cannot use print in standalone mode on iOS as we cannot open new windows
1135			if (urlParams['noFileMenu'] != '1' && (!mxClient.IS_IOS || !navigator.standalone))
1136			{
1137				ui.menus.addMenuItems(menu, ['print'], parent);
1138			}
1139
1140			if (file != null && ui.fileNode != null && urlParams['embedInline'] != '1')
1141			{
1142				var filename = (file.getTitle() != null) ?
1143					file.getTitle() : ui.defaultFilename;
1144
1145				if (!/(\.html)$/i.test(filename) &&
1146					!/(\.svg)$/i.test(filename))
1147				{
1148					this.addMenuItems(menu, ['-', 'properties']);
1149				}
1150			}
1151
1152			menu.addSeparator(parent);
1153			ui.menus.addSubmenu('help', menu, parent);
1154
1155            if (urlParams['embed'] == '1')
1156			{
1157				if (urlParams['noExitBtn'] != '1')
1158				{
1159					ui.menus.addMenuItems(menu, ['-', 'exit'], parent);
1160				}
1161			}
1162			else if (urlParams['noFileMenu'] != '1')
1163			{
1164				ui.menus.addMenuItems(menu, ['-', 'close']);
1165			}
1166        })));
1167
1168		this.put('save', new Menu(mxUtils.bind(this, function(menu, parent)
1169        {
1170			var file = ui.getCurrentFile();
1171
1172			if (file != null && file.constructor == DriveFile)
1173			{
1174				ui.menus.addMenuItems(menu, ['save', 'makeCopy', '-', 'rename', 'moveToFolder'], parent);
1175			}
1176			else
1177			{
1178				ui.menus.addMenuItems(menu, ['save', 'saveAs', '-', 'rename'], parent);
1179
1180				if (ui.isOfflineApp())
1181				{
1182					if (navigator.onLine && urlParams['stealth'] != '1' && urlParams['lockdown'] != '1')
1183					{
1184						this.addMenuItems(menu, ['upload'], parent);
1185					}
1186				}
1187				else
1188				{
1189					ui.menus.addMenuItems(menu, ['makeCopy'], parent);
1190				}
1191			}
1192
1193			if (urlParams['sketch'] == '1' && !mxClient.IS_CHROMEAPP &&
1194				!EditorUi.isElectronApp && file != null &&
1195				file.constructor != LocalFile)
1196			{
1197				ui.menus.addMenuItems(menu, ['-', 'synchronize'], parent);
1198			}
1199
1200			ui.menus.addMenuItems(menu, ['-', 'autosave'], parent);
1201
1202			if (file != null && file.isRevisionHistorySupported())
1203			{
1204				ui.menus.addMenuItems(menu, ['-', 'revisionHistory'], parent);
1205			}
1206        })));
1207
1208        // Augments the existing export menu
1209        var exportAsMenu = this.get('exportAs');
1210
1211        this.put('exportAs', new Menu(mxUtils.bind(this, function(menu, parent)
1212        {
1213        	exportAsMenu.funct(menu, parent);
1214
1215    		if (!mxClient.IS_CHROMEAPP && !EditorUi.isElectronApp)
1216    		{
1217	            // Publish menu contains only one element by default...
1218	            //ui.menus.addSubmenu('publish', menu, parent);
1219	            ui.menus.addMenuItems(menu, ['publishLink'], parent);
1220    		}
1221
1222    		if (ui.mode != App.MODE_ATLAS && urlParams['extAuth'] != '1')
1223    		{
1224    			menu.addSeparator(parent);
1225    			ui.menus.addSubmenu('embed', menu, parent);
1226    		}
1227        })));
1228
1229        var langMenu = this.get('language');
1230
1231        this.put('table', new Menu(mxUtils.bind(this, function(menu, parent)
1232		{
1233			ui.menus.addInsertTableCellItem(menu, parent);
1234		})));
1235
1236		// Adds XML option to import menu
1237		var importMenu = this.get('importFrom');
1238
1239		this.put('importFrom', new Menu(mxUtils.bind(this, function(menu, parent)
1240        {
1241			importMenu.funct(menu, parent);
1242
1243			this.addMenuItems(menu, ['editDiagram'], parent);
1244
1245			if (urlParams['sketch'] == '1')
1246			{
1247				menu.addSeparator(parent);
1248
1249				menu.addItem(mxResources.get('csv') + '...', null, function()
1250				{
1251					ui.showImportCsvDialog();
1252				}, parent, null, mxUtils.bind(graph, graph.isEnabled));
1253
1254				ui.addInsertMenuItems(menu, parent, ['formatSql', '-',
1255					'fromText', 'plantUml', 'mermaid']);
1256			}
1257		})));
1258
1259        // Extras menu is labelled preferences but keeps ID for extensions
1260        this.put('extras', new Menu(mxUtils.bind(this, function(menu, parent)
1261        {
1262			if (urlParams['embed'] != '1' && urlParams['extAuth'] != '1')
1263			{
1264				ui.menus.addSubmenu('theme', menu, parent);
1265			}
1266
1267			if (langMenu != null)
1268			{
1269				ui.menus.addSubmenu('language', menu, parent);
1270			}
1271
1272			ui.menus.addSubmenu('units', menu, parent);
1273			menu.addSeparator(parent);
1274			ui.menus.addMenuItems(menu, ['scrollbars', 'tooltips', 'ruler', '-', 'copyConnect', 'collapseExpand', '-'], parent);
1275
1276			if (urlParams['sketch'] == '1')
1277			{
1278				this.addMenuItems(menu, ['toggleSketchMode'], parent);
1279			}
1280
1281			if (urlParams['embedInline'] != '1')
1282			{
1283				if (Editor.isDarkMode() || (!mxClient.IS_IE && !mxClient.IS_IE11))
1284				{
1285					this.addMenuItems(menu, ['toggleDarkMode'], parent);
1286				}
1287
1288				if (urlParams['embed'] != '1' && (isLocalStorage || mxClient.IS_CHROMEAPP))
1289				{
1290					ui.menus.addMenuItems(menu, ['-', 'showStartScreen', 'search', 'scratchpad'], parent);
1291				}
1292
1293				if (urlParams['sketch'] == '1' && urlParams['pages'] == null)
1294				{
1295					this.addMenuItems(menu, ['togglePagesVisible'], parent);
1296				}
1297			}
1298
1299			menu.addSeparator(parent);
1300        	ui.menus.addMenuItem(menu, 'configuration', parent);
1301
1302			if (!ui.isOfflineApp() && isLocalStorage)
1303			{
1304	        	ui.menus.addMenuItem(menu, 'plugins', parent);
1305			}
1306
1307			this.addMenuItems(menu, ['-', 'fullscreen'], parent);
1308
1309			// Adds trailing separator in case new plugin entries are added
1310			menu.addSeparator(parent);
1311        })));
1312
1313        this.put('insertAdvanced', new Menu(mxUtils.bind(this, function(menu, parent)
1314        {
1315            ui.menus.addMenuItems(menu, ['importText', 'plantUml', 'mermaid', '-', 'formatSql', 'importCsv', '-', 'createShape', 'editDiagram'], parent);
1316        })));
1317
1318        (mxUtils.bind(this, function()
1319        {
1320			var insertMenu = this.get('insert');
1321			var insertMenuFunct = insertMenu.funct;
1322
1323			insertMenu.funct = function(menu, parent)
1324			{
1325				if (urlParams['sketch'] == '1')
1326				{
1327					ui.menus.addMenuItems(menu, ['insertFreehand'], parent);
1328
1329					if (ui.insertTemplateEnabled && !ui.isOffline())
1330					{
1331						ui.menus.addMenuItems(menu, ['insertTemplate'], parent);
1332					}
1333				}
1334				else
1335				{
1336					insertMenuFunct.apply(this, arguments);
1337					ui.menus.addSubmenu('table', menu, parent);
1338					menu.addSeparator(parent);
1339				}
1340
1341				ui.menus.addMenuItems(menu, ['-', 'toggleShapes'], parent);
1342			};
1343        }))();
1344
1345        var methods = ['horizontalFlow', 'verticalFlow', '-', 'horizontalTree', 'verticalTree',
1346                       'radialTree', '-', 'organic', 'circle'];
1347
1348        var addInsertItem = function(menu, parent, title, method)
1349        {
1350            menu.addItem(title, null, mxUtils.bind(this, function()
1351            {
1352                var dlg = new CreateGraphDialog(ui, title, method);
1353                ui.showDialog(dlg.container, 620, 420, true, false);
1354                // Executed after dialog is added to dom
1355                dlg.init();
1356            }), parent);
1357        };
1358
1359        this.put('insertLayout', new Menu(mxUtils.bind(this, function(menu, parent)
1360        {
1361            for (var i = 0; i < methods.length; i++)
1362            {
1363                if (methods[i] == '-')
1364                {
1365                    menu.addSeparator(parent);
1366                }
1367                else
1368                {
1369                    addInsertItem(menu, parent, mxResources.get(methods[i]) + '...', methods[i]);
1370                }
1371            }
1372        })));
1373	};
1374
1375	// Installs the format toolbar
1376	EditorUi.prototype.installFormatToolbar = function(container)
1377	{
1378		var graph = this.editor.graph;
1379		var div = document.createElement('div');
1380
1381		div.style.cssText = 'position:absolute;top:10px;z-index:1;border-radius:4px;' +
1382			'box-shadow:0px 0px 3px 1px #d1d1d1;padding:6px;white-space:nowrap;background-color:#fff;' +
1383			'transform:translate(-50%, 0);left:50%;';
1384
1385		graph.getSelectionModel().addListener(mxEvent.CHANGE, mxUtils.bind(this, function(sender, evt)
1386		{
1387			if (graph.getSelectionCount() > 0)
1388			{
1389				container.appendChild(div);
1390				div.innerHTML = 'Selected: ' + graph.getSelectionCount();
1391			}
1392			else if (div.parentNode != null)
1393			{
1394				div.parentNode.removeChild(div);
1395			}
1396		}));
1397	};
1398
1399	var formatWindowInitialized = false;
1400
1401	EditorUi.prototype.initFormatWindow = function()
1402	{
1403		if (!formatWindowInitialized)
1404		{
1405			formatWindowInitialized = true;
1406			this.formatWindow.window.setClosable(false);
1407
1408			var toggleMinimized = this.formatWindow.window.toggleMinimized;
1409
1410			this.formatWindow.window.toggleMinimized = function()
1411			{
1412				toggleMinimized.apply(this, arguments);
1413
1414				if (this.minimized)
1415				{
1416					this.div.style.width = '90px';
1417					this.table.style.width = '90px';
1418					this.div.style.left = parseInt(this.div.style.left) + 150 + 'px';
1419				}
1420				else
1421				{
1422
1423					this.div.style.width = '240px';
1424					this.table.style.width = '240px';
1425					this.div.style.left = Math.max(0, parseInt(this.div.style.left) - 150) + 'px';
1426				}
1427
1428				this.fit();
1429			};
1430
1431			mxEvent.addListener(this.formatWindow.window.title, 'dblclick', mxUtils.bind(this, function(evt)
1432			{
1433				if (mxEvent.getSource(evt) == this.formatWindow.window.title)
1434				{
1435					this.formatWindow.window.toggleMinimized();
1436				}
1437			}));
1438		}
1439	};
1440
1441	// Initializes the user interface
1442	var editorUiInit = EditorUi.prototype.init;
1443
1444	EditorUi.prototype.init = function()
1445	{
1446		editorUiInit.apply(this, arguments);
1447
1448		if (urlParams['embedInline'] != '1')
1449		{
1450			this.doSetDarkMode((urlParams['dark'] != null) ?
1451				urlParams['dark'] == 1 && !mxClient.IS_IE &&
1452				!mxClient.IS_IE11 : ((mxSettings.settings.darkMode != null) ?
1453				mxSettings.settings.darkMode : (window.matchMedia &&
1454					window.matchMedia('(prefers-color-scheme: dark)').matches)));
1455		}
1456
1457		var div = document.createElement('div');
1458		div.style.cssText = 'position:absolute;left:0px;right:0px;top:0px;overflow-y:auto;overflow-x:hidden;';
1459		div.style.bottom = (urlParams['embed'] != '1' || urlParams['libraries'] == '1') ? '63px' : '32px';
1460		this.sidebar = this.createSidebar(div);
1461
1462		if (urlParams['sketch'] == '1')
1463		{
1464			this.toggleScratchpad();
1465		}
1466
1467		if ((urlParams['sketch'] != '1' && iw >= 1000) || urlParams['clibs'] != null ||
1468			urlParams['libs'] != null || urlParams['search-shapes'] != null)
1469		{
1470			toggleShapes(this, true);
1471
1472			if (this.sidebar != null && urlParams['search-shapes'] != null && this.sidebar.searchShapes != null)
1473			{
1474				this.sidebar.searchShapes(urlParams['search-shapes']);
1475				this.sidebar.showEntries('search');
1476			}
1477		}
1478
1479		// Overrides mxWindow.fit to allow for embedViewport
1480		var ui = this;
1481
1482		mxWindow.prototype.fit = function()
1483		{
1484			if (!Editor.inlineFullscreen && ui.embedViewport != null)
1485			{
1486				var left = parseInt(this.div.offsetLeft);
1487				var width = parseInt(this.div.offsetWidth);
1488				var right = ui.embedViewport.x + ui.embedViewport.width;
1489				this.div.style.left = Math.max(ui.embedViewport.x, Math.min(left, right - width)) + 'px';
1490
1491				var top = parseInt(this.div.offsetTop);
1492				var height = parseInt(this.div.offsetHeight);
1493				var bottom = ui.embedViewport.y + ui.embedViewport.height;
1494
1495				this.div.style.top = Math.max(ui.embedViewport.y, Math.min(top, bottom - height)) + 'px';
1496			}
1497			else
1498			{
1499				mxUtils.fit(this.div);
1500			}
1501		};
1502
1503		// Overrides insert ellipse shortcut
1504		this.keyHandler.bindAction(75, true, 'toggleShapes', true); // Ctrl+Shift+K
1505
1506		if (urlParams['sketch'] == '1' || iw >= 1000)
1507		{
1508			if (urlParams['embedInline'] != '1')
1509			{
1510				toggleFormat(this, true);
1511
1512				if (urlParams['sketch'] == '1')
1513				{
1514					this.initFormatWindow();
1515					this.formatWindow.window.toggleMinimized();
1516				}
1517			}
1518		}
1519
1520		// Needed for creating elements in Format panel
1521		var ui = this;
1522		var graph = ui.editor.graph;
1523		ui.toolbar = this.createToolbar(ui.createDiv('geToolbar'));
1524		ui.defaultLibraryName = mxResources.get('untitledLibrary');
1525
1526		var menubar = document.createElement('div');
1527		menubar.className = 'geMenubarContainer';
1528		var before = null;
1529		var menuObj = new Menubar(ui, menubar);
1530
1531		function addMenu(id, small, img)
1532		{
1533			var menu = ui.menus.get(id);
1534
1535			var elt = menuObj.addMenu(mxResources.get(id), mxUtils.bind(this, function()
1536			{
1537				// Allows extensions of menu.functid
1538				menu.funct.apply(this, arguments);
1539			}), before);
1540
1541			elt.className = (urlParams['sketch'] == '1') ? 'geToolbarButton' : 'geMenuItem';
1542			elt.style.display = 'inline-block';
1543			elt.style.boxSizing = 'border-box';
1544			elt.style.top = '6px';
1545			elt.style.marginRight = '6px';
1546			elt.style.height = '30px';
1547			elt.style.paddingTop = '6px';
1548			elt.style.paddingBottom = '6px';
1549			elt.style.cursor = 'pointer';
1550			elt.setAttribute('title', mxResources.get(id));
1551			ui.menus.menuCreated(menu, elt, 'geMenuItem');
1552
1553			if (img != null)
1554			{
1555				elt.style.backgroundImage = 'url(' + img + ')';
1556				elt.style.backgroundPosition = 'center center';
1557				elt.style.backgroundRepeat = 'no-repeat';
1558				elt.style.backgroundSize = '24px 24px';
1559				elt.style.width = '34px';
1560				elt.innerHTML = '';
1561			}
1562			else if (!small)
1563			{
1564				elt.style.backgroundImage = 'url(' + mxWindow.prototype.normalizeImage + ')';
1565				elt.style.backgroundPosition = 'right 6px center';
1566				elt.style.backgroundRepeat = 'no-repeat';
1567				elt.style.paddingRight = '22px';
1568			}
1569
1570			return elt;
1571		};
1572
1573		function addMenuItem(label, fn, small, tooltip, action, img)
1574		{
1575			var btn = document.createElement('a');
1576			btn.className = (urlParams['sketch'] == '1') ? 'geToolbarButton' : 'geMenuItem';
1577			btn.style.display = 'inline-block';
1578			btn.style.boxSizing = 'border-box';
1579			btn.style.height = '30px';
1580			btn.style.padding = '6px';
1581			btn.style.position = 'relative';
1582			btn.style.verticalAlign = 'top';
1583			btn.style.top = '0px';
1584
1585			if (urlParams['sketch'] == '1')
1586			{
1587				btn.style.borderStyle = 'none';
1588				btn.style.boxShadow = 'none';
1589				btn.style.padding = '6px';
1590				btn.style.margin = '0px';
1591			}
1592
1593			if (ui.statusContainer != null)
1594			{
1595				menubar.insertBefore(btn, ui.statusContainer);
1596			}
1597			else
1598			{
1599				menubar.appendChild(btn);
1600			}
1601
1602			if (img != null)
1603			{
1604				btn.style.backgroundImage = 'url(' + img + ')';
1605				btn.style.backgroundPosition = 'center center';
1606				btn.style.backgroundRepeat = 'no-repeat';
1607				btn.style.backgroundSize = '24px 24px';
1608				btn.style.width = '34px';
1609			}
1610			else
1611			{
1612				mxUtils.write(btn, label);
1613			}
1614
1615    		// Prevents focus
1616            mxEvent.addListener(btn, (mxClient.IS_POINTER) ? 'pointerdown' : 'mousedown',
1617            	mxUtils.bind(this, function(evt)
1618    		{
1619    			evt.preventDefault();
1620    		}));
1621
1622            mxEvent.addListener(btn, 'click', function(evt)
1623            {
1624            	if (btn.getAttribute('disabled') != 'disabled')
1625            	{
1626            		fn(evt);
1627            	}
1628
1629                mxEvent.consume(evt);
1630            });
1631
1632            if (small == null)
1633            {
1634                btn.style.marginRight = '4px';
1635            }
1636
1637            if (tooltip != null)
1638            {
1639                btn.setAttribute('title', tooltip);
1640            }
1641
1642            if (action != null)
1643            {
1644                function updateState()
1645                {
1646                    if (action.isEnabled())
1647                    {
1648                        btn.removeAttribute('disabled');
1649                        btn.style.cursor = 'pointer';
1650                    }
1651                    else
1652                    {
1653                        btn.setAttribute('disabled', 'disabled');
1654                        btn.style.cursor = 'default';
1655                    }
1656                };
1657
1658                action.addListener('stateChanged', updateState);
1659				graph.addListener('enabledChanged', updateState);
1660                updateState();
1661            }
1662
1663            return btn;
1664        };
1665
1666        function createGroup(btns, op, container)
1667        {
1668            var btnGroup = document.createElement('div');
1669            btnGroup.className = 'geMenuItem';
1670            btnGroup.style.display = 'inline-block';
1671            btnGroup.style.verticalAlign = 'top';
1672            btnGroup.style.marginRight = '6px';
1673            btnGroup.style.padding = '0 4px 0 4px';
1674            btnGroup.style.height = '30px';
1675            btnGroup.style.position = 'relative';
1676            btnGroup.style.top = '0px';
1677
1678			if (urlParams['sketch'] == '1')
1679			{
1680				btnGroup.style.boxShadow = 'none';
1681			}
1682
1683            for (var i = 0; i < btns.length; i++)
1684            {
1685            	if (btns[i] != null)
1686            	{
1687					if (urlParams['sketch'] == '1')
1688					{
1689						btns[i].style.padding = '10px 8px';
1690						btns[i].style.width = '30px';
1691					}
1692
1693            		btns[i].style.margin = '0px';
1694	                btns[i].style.boxShadow = 'none';
1695	                btnGroup.appendChild(btns[i]);
1696            	}
1697            }
1698
1699            if (op != null)
1700            {
1701            	mxUtils.setOpacity(btnGroup, op);
1702            }
1703
1704			if (ui.statusContainer != null && urlParams['sketch'] != '1')
1705            {
1706            	menubar.insertBefore(btnGroup, ui.statusContainer);
1707            }
1708            else
1709            {
1710            	menubar.appendChild(btnGroup);
1711            }
1712
1713            return btnGroup;
1714        };
1715
1716		ui.statusContainer = ui.createStatusContainer();
1717		ui.statusContainer.style.position = 'relative';
1718		ui.statusContainer.style.maxWidth = '';
1719		ui.statusContainer.style.marginTop = '7px';
1720		ui.statusContainer.style.marginLeft = '6px';
1721		ui.statusContainer.style.color = 'gray';
1722		ui.statusContainer.style.cursor = 'default';
1723
1724		function updateTitle()
1725		{
1726			var file = ui.getCurrentFile();
1727
1728			if (file != null && file.getTitle() != null)
1729			{
1730				var mode = file.getMode();
1731
1732				if (mode == 'google')
1733				{
1734					mode = 'googleDrive';
1735				}
1736				else if (mode == 'github')
1737				{
1738					mode = 'gitHub';
1739				}
1740				else if (mode == 'gitlab')
1741				{
1742					mode = 'gitLab';
1743				}
1744				else if (mode == 'onedrive')
1745				{
1746					mode = 'oneDrive';
1747				}
1748
1749				mode = mxResources.get(mode);
1750				menubar.setAttribute('title', file.getTitle() + ((mode != null) ? ' (' + mode + ')' : ''));
1751			}
1752			else
1753			{
1754				menubar.removeAttribute('title');
1755			}
1756		};
1757
1758		// Hides popup menus
1759		var uiHideCurrentMenu = ui.hideCurrentMenu;
1760
1761		ui.hideCurrentMenu = function()
1762		{
1763			uiHideCurrentMenu.apply(this, arguments);
1764			this.editor.graph.popupMenuHandler.hideMenu();
1765		};
1766
1767		// Connects the status bar to the editor status
1768		var uiDescriptorChanged = ui.descriptorChanged;
1769
1770		ui.descriptorChanged = function()
1771		{
1772			uiDescriptorChanged.apply(this, arguments);
1773			updateTitle();
1774		};
1775
1776		ui.setStatusText(ui.editor.getStatus());
1777		menubar.appendChild(ui.statusContainer);
1778
1779		ui.buttonContainer = document.createElement('div');
1780		ui.buttonContainer.style.cssText = 'position:absolute;right:0px;padding-right:34px;top:10px;' +
1781			'white-space:nowrap;padding-top:2px;background-color:inherit;';
1782		menubar.appendChild(ui.buttonContainer);
1783
1784		// Container for the user element
1785		ui.menubarContainer = ui.buttonContainer;
1786
1787        ui.tabContainer = document.createElement('div');
1788		ui.tabContainer.className = 'geTabContainer';
1789        ui.tabContainer.style.cssText = 'position:absolute;left:0px;right:0px;bottom:0px;height:30px;white-space:nowrap;' +
1790            'margin-bottom:-2px;visibility:hidden;';
1791
1792        var previousParent = ui.diagramContainer.parentNode;
1793
1794        var wrapper = document.createElement('div');
1795        wrapper.style.cssText = 'position:absolute;top:0px;left:0px;right:0px;bottom:0px;overflow:hidden;';
1796        ui.diagramContainer.style.top = (urlParams['sketch'] == '1') ? '0px' : '47px';
1797
1798        var viewZoomMenu = ui.menus.get('viewZoom');
1799		var insertImage = (urlParams['sketch'] != '1') ? Editor.plusImage : Editor.shapesImage;
1800		var footer = (urlParams['sketch'] == '1') ? document.createElement('div') : null;
1801		var picker = (urlParams['sketch'] == '1') ? document.createElement('div') : null;
1802		var toolbar = (urlParams['sketch'] == '1') ? document.createElement('div') : null;
1803
1804		var refreshSidebar = mxUtils.bind(this, function()
1805		{
1806			if (this.sidebar != null)
1807			{
1808				this.sidebar.graph.stylesheet.styles =
1809					mxUtils.clone(graph.stylesheet.styles);
1810				this.sidebar.container.innerHTML = '';
1811				this.sidebar.palettes = new Object();
1812				this.sidebar.init();
1813
1814				if (urlParams['sketch'] == '1')
1815				{
1816					this.scratchpad = null;
1817					this.toggleScratchpad();
1818				}
1819			}
1820
1821			graph.refresh();
1822			graph.view.validateBackground();
1823		});
1824
1825		ui.addListener('darkModeChanged', refreshSidebar);
1826		ui.addListener('sketchModeChanged', refreshSidebar);
1827
1828		var inlineSizeChanged = mxUtils.bind(this, function()
1829		{
1830			if (Editor.inlineFullscreen)
1831			{
1832				toolbar.style.left = '10px';
1833				toolbar.style.top = '10px';
1834
1835				picker.style.left = '10px';
1836				picker.style.top = '60px';
1837
1838				footer.style.top = '10px';
1839				footer.style.right = '12px';
1840				footer.style.left = '';
1841
1842				ui.diagramContainer.setAttribute('data-bounds', ui.diagramContainer.style.top + ' ' +
1843					ui.diagramContainer.style.left + ' ' + ui.diagramContainer.style.width + ' ' +
1844					ui.diagramContainer.style.height);
1845
1846				ui.diagramContainer.style.top = '0px';
1847				ui.diagramContainer.style.left = '0px';
1848				ui.diagramContainer.style.bottom = '0px';
1849				ui.diagramContainer.style.right = '0px';
1850				ui.diagramContainer.style.width = '';
1851				ui.diagramContainer.style.height = '';
1852			}
1853			else
1854			{
1855				var bounds = ui.diagramContainer.getAttribute('data-bounds');
1856
1857				if (bounds != null)
1858				{
1859					ui.diagramContainer.style.background = 'transparent';
1860					ui.diagramContainer.removeAttribute('data-bounds');
1861					var gb = graph.getGraphBounds();
1862					var tokens = bounds.split(' ');
1863
1864					ui.diagramContainer.style.top = tokens[0];
1865					ui.diagramContainer.style.left = tokens[1];
1866					ui.diagramContainer.style.width = (gb.width + 50) + 'px';
1867					ui.diagramContainer.style.height = (gb.height + 46) + 'px';
1868					ui.diagramContainer.style.bottom = '';
1869					ui.diagramContainer.style.right = '';
1870
1871					var parent = window.opener || window.parent;
1872					parent.postMessage(JSON.stringify({
1873						event: 'resize',
1874						rect: ui.diagramContainer.getBoundingClientRect()
1875					}), '*');
1876					ui.refresh();
1877				}
1878
1879				toolbar.style.left = ui.diagramContainer.offsetLeft + 'px';
1880				toolbar.style.top = (ui.diagramContainer.offsetTop -
1881					toolbar.offsetHeight - 4) + 'px';
1882
1883				picker.style.display = '';
1884				picker.style.left = (ui.diagramContainer.offsetLeft -
1885					picker.offsetWidth - 4) + 'px';
1886				picker.style.top = ui.diagramContainer.offsetTop + 'px';
1887
1888				footer.style.left = (ui.diagramContainer.offsetLeft +
1889					ui.diagramContainer.offsetWidth -
1890					footer.offsetWidth) + 'px';
1891				footer.style.top = toolbar.style.top;
1892				footer.style.right = '';
1893
1894				ui.bottomResizer.style.left = (ui.diagramContainer.offsetLeft +
1895					(ui.diagramContainer.offsetWidth -
1896					ui.bottomResizer.offsetWidth) / 2) + 'px';
1897				ui.bottomResizer.style.top = (ui.diagramContainer.offsetTop +
1898					ui.diagramContainer.offsetHeight -
1899					ui.bottomResizer.offsetHeight / 2 - 1) + 'px';
1900
1901				ui.rightResizer.style.left = (ui.diagramContainer.offsetLeft +
1902					ui.diagramContainer.offsetWidth -
1903					ui.rightResizer.offsetWidth / 2 - 1) + 'px';
1904				ui.rightResizer.style.top = (ui.diagramContainer.offsetTop +
1905					(ui.diagramContainer.offsetHeight -
1906					ui.bottomResizer.offsetHeight) / 2) + 'px';
1907			}
1908
1909			ui.bottomResizer.style.visibility = (Editor.inlineFullscreen) ? 'hidden' : '';
1910			ui.rightResizer.style.visibility = ui.bottomResizer.style.visibility;
1911			menubar.style.display = 'none';
1912			toolbar.style.visibility = '';
1913			footer.style.visibility = '';
1914		});
1915
1916		var inlineFullscreenChanged = mxUtils.bind(this, function()
1917		{
1918			fullscreenElt.style.backgroundImage = 'url(' + ((!Editor.inlineFullscreen) ?
1919				Editor.fullscreenImage : Editor.fullscreenExitImage) + ')';
1920			this.diagramContainer.style.background = (Editor.inlineFullscreen) ?
1921				(Editor.isDarkMode() ? Editor.darkColor : '#ffffff') : 'transparent';
1922			inlineSizeChanged();
1923		});
1924
1925		var editInlineStart = mxUtils.bind(this, function()
1926		{
1927			inlineFullscreenChanged();
1928			toggleFormat(ui, true);
1929			ui.initFormatWindow();
1930			var r  = this.diagramContainer.getBoundingClientRect();
1931			this.formatWindow.window.setLocation(r.x + r.width + 4, r.y);
1932		});
1933
1934		ui.addListener('inlineFullscreenChanged', inlineFullscreenChanged);
1935		ui.addListener('editInlineStart', editInlineStart);
1936
1937		if (urlParams['embedInline'] == '1')
1938		{
1939			ui.addListener('darkModeChanged', editInlineStart);
1940		}
1941
1942		ui.addListener('editInlineStop', mxUtils.bind(this, function(evt)
1943		{
1944			ui.diagramContainer.style.width = '10px';
1945			ui.diagramContainer.style.height = '10px';
1946			ui.diagramContainer.style.border = '';
1947			ui.bottomResizer.style.visibility = 'hidden';
1948			ui.rightResizer.style.visibility = 'hidden';
1949			toolbar.style.visibility = 'hidden';
1950			footer.style.visibility = 'hidden';
1951			picker.style.display = 'none';
1952		}));
1953
1954		// Stops panning while freehand is active
1955		if (Graph.touchStyle)
1956		{
1957			graph.panningHandler.isPanningTrigger = function(me)
1958			{
1959				var evt = me.getEvent();
1960
1961			 	return (me.getState() == null && (!mxEvent.isMouseEvent(evt) &&
1962					!graph.freehand.isDrawing())) ||
1963			 		(mxEvent.isPopupTrigger(evt) && (me.getState() == null ||
1964			 		mxEvent.isControlDown(evt) || mxEvent.isShiftDown(evt)));
1965			};
1966		}
1967
1968		// Hides hover icons if freehand is active
1969		if (ui.hoverIcons != null)
1970		{
1971			var hoverIconsUpdate = ui.hoverIcons.update;
1972
1973			ui.hoverIcons.update = function()
1974			{
1975				if (!graph.freehand.isDrawing())
1976				{
1977					hoverIconsUpdate.apply(this, arguments);
1978				}
1979			};
1980		}
1981
1982		// Removes sketch style from freehand shapes
1983		if (graph.freehand != null)
1984		{
1985			var freehandCreateStyle = graph.freehand.createStyle;
1986
1987			graph.freehand.createStyle = function(stencil)
1988			{
1989				return freehandCreateStyle.apply(this, arguments) + 'sketch=0;';
1990			};
1991		}
1992
1993		if (urlParams['sketch'] == '1')
1994		{
1995			picker.className = 'geToolbarContainer';
1996			footer.className = 'geToolbarContainer';
1997			toolbar.className = 'geToolbarContainer';
1998			menubar.className = 'geToolbarContainer';
1999
2000			ui.picker = picker;
2001			var statusVisible = false;
2002
2003			mxEvent.addListener(menubar, 'mouseenter', function()
2004			{
2005				ui.statusContainer.style.display = 'inline-block';
2006			});
2007
2008			mxEvent.addListener(menubar, 'mouseleave', function()
2009			{
2010				if (!statusVisible)
2011				{
2012					ui.statusContainer.style.display = 'none';
2013				}
2014			});
2015
2016			var setNotificationTitle = mxUtils.bind(this, function(title)
2017			{
2018				if (ui.notificationBtn != null)
2019				{
2020					if (title != null)
2021					{
2022						ui.notificationBtn.setAttribute('title', title);
2023					}
2024					else
2025					{
2026						ui.notificationBtn.removeAttribute('title');
2027					}
2028				}
2029			});
2030
2031			// Connects the status bar to the editor status and
2032			// moves status to bell icon tooltip for trivial messages
2033			if (urlParams['embed'] != '1')
2034			{
2035				menubar.style.visibility = (menubar.clientWidth < 14) ? 'hidden' : '';
2036
2037				ui.editor.addListener('statusChanged', mxUtils.bind(this, function()
2038				{
2039					ui.setStatusText(ui.editor.getStatus());
2040
2041					if (ui.statusContainer.children.length == 0 ||
2042						(ui.statusContainer.children.length == 1 &&
2043						typeof ui.statusContainer.firstChild.getAttribute === 'function' &&
2044						ui.statusContainer.firstChild.getAttribute('class') == null))
2045					{
2046						var title = (ui.statusContainer.firstChild != null &&
2047							typeof ui.statusContainer.firstChild.getAttribute === 'function') ?
2048							ui.statusContainer.firstChild.getAttribute('title') :
2049							ui.editor.getStatus();
2050						setNotificationTitle(title);
2051						var file = ui.getCurrentFile();
2052						var key = (file != null) ? file.savingStatusKey : DrawioFile.prototype.savingStatusKey;
2053
2054						if (title == mxResources.get(key) + '...')
2055						{
2056							ui.statusContainer.innerHTML = '<img title="' + mxUtils.htmlEntities(
2057								mxResources.get(key)) + '...' + '"src="' + Editor.tailSpin + '">';
2058							ui.statusContainer.style.display = 'inline-block';
2059							statusVisible = true;
2060						}
2061						else if (ui.buttonContainer.clientWidth > 6)
2062						{
2063							ui.statusContainer.style.display = 'none';
2064							statusVisible = false;
2065						}
2066					}
2067					else
2068					{
2069						ui.statusContainer.style.display = 'inline-block';
2070						setNotificationTitle(null);
2071
2072						statusVisible = true;
2073					}
2074
2075					menubar.style.visibility = (menubar.clientWidth > 12) ? '' : 'hidden';
2076				}));
2077			}
2078			else
2079			{
2080				ui.editor.addListener('statusChanged', mxUtils.bind(this, function()
2081				{
2082					menubar.style.visibility = (menubar.clientWidth > 16) ? '' : 'hidden';
2083				}));
2084			}
2085
2086			elt = addMenu('diagram', null, Editor.menuImage);
2087			elt.style.boxShadow = 'none';
2088			elt.style.padding = '6px';
2089			elt.style.margin = '0px';
2090			toolbar.appendChild(elt);
2091
2092			mxEvent.disableContextMenu(elt);
2093
2094			mxEvent.addGestureListeners(elt, mxUtils.bind(this, function(evt)
2095			{
2096				if (mxEvent.isShiftDown(evt) || mxEvent.isAltDown(evt) ||
2097					mxEvent.isMetaDown(evt) || mxEvent.isControlDown(evt) ||
2098					mxEvent.isPopupTrigger(evt))
2099				{
2100					this.appIconClicked(evt);
2101				}
2102			}), null, null);
2103
2104			ui.statusContainer.style.position = '';
2105			ui.statusContainer.style.display = 'none';
2106			ui.statusContainer.style.margin = '0px';
2107			ui.statusContainer.style.padding = '6px 0px';
2108			ui.statusContainer.style.maxWidth = Math.min(iw - 240, 280) + 'px';
2109			ui.statusContainer.style.display = 'inline-block';
2110			ui.statusContainer.style.textOverflow = 'ellipsis';
2111
2112			ui.buttonContainer.style.position = '';
2113			ui.buttonContainer.style.paddingRight = '0px';
2114			ui.buttonContainer.style.display = 'inline-block';
2115
2116			var foldImg = document.createElement('a');
2117			foldImg.style.padding = '0px';
2118			foldImg.style.boxShadow = 'none';
2119			foldImg.className = 'geMenuItem';
2120			foldImg.style.display = 'inline-block';
2121			foldImg.style.width = '40px';
2122			foldImg.style.height = '12px';
2123			foldImg.style.marginBottom = '-2px';
2124			foldImg.style.backgroundImage = 'url(' + mxWindow.prototype.normalizeImage + ')';
2125			foldImg.style.backgroundPosition = 'top center';
2126			foldImg.style.backgroundRepeat = 'no-repeat';
2127			foldImg.setAttribute('title', 'Minimize'/*TODO:mxResources.get('minimize')*/);
2128
2129			var collapsed = false;
2130
2131			var initPicker = mxUtils.bind(this, function()
2132			{
2133				picker.innerHTML = '';
2134
2135				if (!collapsed)
2136				{
2137					function addElt(elt, title, cursor)
2138					{
2139						if (title != null)
2140						{
2141							elt.setAttribute('title', title);
2142						}
2143
2144						elt.style.cursor = (cursor != null) ? cursor : 'default';
2145						elt.style.margin = '2px 0px';
2146						picker.appendChild(elt);
2147						mxUtils.br(picker);
2148
2149						return elt;
2150					};
2151
2152					// Append sidebar elements
2153					addElt(ui.sidebar.createVertexTemplate('text;strokeColor=none;fillColor=none;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;',
2154						60, 30, 'Text', mxResources.get('text'), true, false, null, true, true), mxResources.get('text') +
2155						' (' +  Editor.ctrlKey + '+Shift+X' + ')');
2156					addElt(ui.sidebar.createVertexTemplate('shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;' +
2157						'fontColor=#000000;darkOpacity=0.05;fillColor=#FFF9B2;strokeColor=none;fillStyle=solid;' +
2158						'direction=west;gradientDirection=north;gradientColor=#FFF2A1;shadow=1;size=20;pointerEvents=1;',
2159						140, 160, '', mxResources.get('note'), true, false, null, true), mxResources.get('note'));
2160					addElt(ui.sidebar.createVertexTemplate('rounded=0;whiteSpace=wrap;html=1;', 160, 80,
2161						'', mxResources.get('rectangle'), true, false, null, true), mxResources.get('rectangle') +
2162						' (' +  Editor.ctrlKey + '+K' + ')');
2163					addElt(ui.sidebar.createVertexTemplate('ellipse;whiteSpace=wrap;html=1;', 160, 100,
2164						'', mxResources.get('ellipse'), true, false, null, true), mxResources.get('ellipse'));
2165
2166					(function()
2167					{
2168						var edgeStyle = 'edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;';
2169						var cell = new mxCell('', new mxGeometry(0, 0, graph.defaultEdgeLength, 0), edgeStyle);
2170						cell.geometry.setTerminalPoint(new mxPoint(0, 0), true);
2171						cell.geometry.setTerminalPoint(new mxPoint(cell.geometry.width, 0), false);
2172						cell.geometry.points = [];
2173						cell.geometry.relative = true;
2174						cell.edge = true;
2175
2176						addElt(ui.sidebar.createEdgeTemplateFromCells([cell],
2177							cell.geometry.width, cell.geometry.height,
2178							mxResources.get('line'), true, null, true, false),
2179							mxResources.get('line'));
2180
2181						cell = cell.clone();
2182						cell.style = edgeStyle + 'shape=flexArrow;rounded=1;startSize=8;endSize=8;';
2183						cell.geometry.width = graph.defaultEdgeLength + 20;
2184						cell.geometry.setTerminalPoint(new mxPoint(0, 20), true);
2185						cell.geometry.setTerminalPoint(new mxPoint(cell.geometry.width, 20), false);
2186
2187						var elt = addElt(ui.sidebar.createEdgeTemplateFromCells([cell],
2188							cell.geometry.width, 40, mxResources.get('arrow'),
2189							true, null, true, false), mxResources.get('arrow'));
2190						elt.style.borderBottom = '1px solid ' + (Editor.isDarkMode() ? '#505050' : 'lightgray');
2191						elt.style.paddingBottom = '14px';
2192						elt.style.marginBottom = '14px';
2193				 	})();
2194
2195					function addAction(action, label, image)
2196					{
2197						var elt = addMenuItem('', action.funct, null, label, action, image);
2198						elt.style.width = '40px';
2199						elt.style.opacity = '0.7';
2200
2201						return addElt(elt, null, 'pointer');
2202					};
2203
2204					addAction(ui.actions.get('insertFreehand'), mxResources.get('freehand'), Editor.freehandImage);
2205					var toggleShapesAction = ui.actions.get('toggleShapes');
2206					addAction(toggleShapesAction, mxResources.get('shapes') + ' (' + toggleShapesAction.shortcut + ')', insertImage);
2207
2208					elt = addMenu('table', null, Editor.tableImage);
2209					elt.style.boxShadow = 'none';
2210					elt.style.opacity = '0.7';
2211					elt.style.padding = '6px';
2212					elt.style.margin = '0px';
2213					elt.style.width = '37px';
2214					addElt(elt, null, 'pointer');
2215
2216					addAction(ui.actions.get('insertTemplate'), mxResources.get('template'), Editor.templateImage);
2217				}
2218
2219				if (urlParams['embedInline'] != '1')
2220				{
2221					picker.appendChild(foldImg);
2222				}
2223			});
2224
2225			mxEvent.addListener(foldImg, 'click', mxUtils.bind(this, function()
2226			{
2227				if (collapsed)
2228				{
2229					mxUtils.setPrefixedStyle(picker.style, 'transform', 'translate(0, -50%)');
2230					picker.style.padding = '8px 6px 4px';
2231					picker.style.top = '50%';
2232					picker.style.bottom = '';
2233					picker.style.height = '';
2234					foldImg.style.backgroundImage = 'url(' + mxWindow.prototype.normalizeImage + ')';
2235					foldImg.style.width = '40px';
2236					foldImg.style.height = '12px';
2237					foldImg.setAttribute('title', 'Minimize'/*TODO:mxResources.get('minimize')*/);
2238					collapsed = false;
2239					initPicker();
2240				}
2241				else
2242				{
2243					picker.innerHTML = '';
2244					picker.appendChild(foldImg);
2245					mxUtils.setPrefixedStyle(picker.style, 'transform', 'translate(0, 0)');
2246					picker.style.top = '';
2247					picker.style.bottom = '12px';
2248					picker.style.padding = '0px';
2249					picker.style.height = '24px';
2250					foldImg.style.height = '24px';
2251					foldImg.style.backgroundImage = 'url(' + Editor.plusImage + ')';
2252					foldImg.setAttribute('title', mxResources.get('insert'));
2253					foldImg.style.width = '24px';
2254					collapsed = true;
2255				}
2256			}));
2257
2258			initPicker();
2259
2260			ui.addListener('darkModeChanged', initPicker);
2261			ui.addListener('sketchModeChanged', initPicker);
2262		}
2263		else
2264		{
2265			// Connects the status bar to the editor status
2266			ui.editor.addListener('statusChanged', mxUtils.bind(this, function()
2267			{
2268				ui.setStatusText(ui.editor.getStatus());
2269			}));
2270		}
2271
2272		if (viewZoomMenu != null)
2273		{
2274			var fitFunction = function(evt)
2275	        {
2276	            graph.popupMenuHandler.hideMenu();
2277
2278				if (mxEvent.isAltDown(evt))
2279				{
2280					ui.actions.get('customZoom').funct();
2281				}
2282				else
2283				{
2284		        	ui.actions.get('smartFit').funct();
2285				}
2286	        };
2287
2288        	var zoomInAction = ui.actions.get('zoomIn');
2289			var zoomOutAction = ui.actions.get('zoomOut');
2290			var resetViewAction = ui.actions.get('resetView');
2291			var fullscreenAction = ui.actions.get('fullscreen');
2292			var toggleDarkAction = ui.actions.get('toggleDarkMode');
2293        	var undoAction = ui.actions.get('undo');
2294        	var redoAction = ui.actions.get('redo');
2295	        var undoElt = addMenuItem('', undoAction.funct, null, mxResources.get('undo') + ' (' + undoAction.shortcut + ')', undoAction, Editor.undoImage);
2296	        var redoElt = addMenuItem('', redoAction.funct, null, mxResources.get('redo') + ' (' + redoAction.shortcut + ')', redoAction, Editor.redoImage);
2297			var fullscreenElt = addMenuItem('', fullscreenAction.funct, null, mxResources.get('fullscreen'), fullscreenAction, Editor.fullscreenImage);
2298
2299			if (footer != null)
2300			{
2301				fullscreenElt.parentNode.removeChild(fullscreenElt);
2302				var deleteAction = ui.actions.get('delete');
2303				var deleteElt = addMenuItem('', deleteAction.funct, null, mxResources.get('delete'), deleteAction, Editor.trashImage);
2304				deleteElt.style.opacity = '0.1';
2305	        	toolbar.appendChild(deleteElt);
2306
2307				deleteAction.addListener('stateChanged', function()
2308				{
2309					deleteElt.style.opacity = (deleteAction.enabled) ? '' : '0.1';
2310				});
2311
2312				var undoListener = function()
2313				{
2314					undoElt.style.display = (ui.editor.undoManager.history.length > 0 ||
2315						graph.isEditing()) ? 'inline-block' : 'none';
2316					redoElt.style.display = undoElt.style.display;
2317
2318					undoElt.style.opacity = (undoAction.enabled) ? '' : '0.1';
2319					redoElt.style.opacity = (redoAction.enabled) ? '' : '0.1';
2320				};
2321
2322				toolbar.appendChild(undoElt);
2323				toolbar.appendChild(redoElt);
2324
2325				undoAction.addListener('stateChanged', undoListener);
2326				redoAction.addListener('stateChanged', undoListener);
2327				undoListener();
2328
2329				if (urlParams['layers'] != null)
2330				{
2331					var layersAction = ui.actions.get('layers');
2332					var layersElt = addMenuItem('', layersAction.funct, null, mxResources.get('layers'), layersAction, Editor.layersImage);
2333					layersElt.style.opacity = '0.4';
2334					footer.appendChild(layersElt);
2335				}
2336
2337				if (urlParams['tags'] != null)
2338				{
2339					var tagsAction = ui.actions.get('tags');
2340					var tagsElt = addMenuItem('', tagsAction.funct, null, mxResources.get('tags'), tagsAction, Editor.tagsImage);
2341					tagsElt.style.opacity = '0.4';
2342					footer.appendChild(tagsElt);
2343				}
2344
2345				var outlineAction = ui.actions.get('outline');
2346				var outlineElt = addMenuItem('', outlineAction.funct, null, mxResources.get('outline'), outlineAction, Editor.outlineImage);
2347				footer.appendChild(outlineElt);
2348
2349				var zoomOutElt = addMenuItem('', zoomOutAction.funct, true, mxResources.get('zoomOut') +
2350					' (' + Editor.ctrlKey + ' -/Alt+Mousewheel)', zoomOutAction, Editor.zoomOutImage);
2351				footer.appendChild(zoomOutElt);
2352
2353				var elt = document.createElement('div');
2354				elt.innerHTML = '100%';
2355				elt.setAttribute('title', mxResources.get('fitWindow') + '/' + mxResources.get('resetView') + ' (Enter)');
2356				elt.style.display = 'inline-block';
2357				elt.style.cursor = 'pointer';
2358				elt.style.textAlign = 'center';
2359				elt.style.whiteSpace = 'nowrap';
2360	        	elt.style.paddingRight = '10px';
2361				elt.style.textDecoration = 'none';
2362				elt.style.verticalAlign = 'top';
2363				elt.style.padding = '6px 0';
2364				elt.style.fontSize = '14px';
2365				elt.style.width = '40px';
2366				elt.style.opacity = '0.4';
2367				footer.appendChild(elt);
2368
2369				mxEvent.addListener(elt, 'click', fitFunction);
2370
2371				var zoomInElt = addMenuItem('', zoomInAction.funct, true, mxResources.get('zoomIn') +
2372					' (' + Editor.ctrlKey + ' +/Alt+Mousewheel)', zoomInAction, Editor.zoomInImage);
2373				footer.appendChild(zoomInElt);
2374
2375				if (urlParams['embedInline'] == '1')
2376				{
2377					footer.appendChild(fullscreenElt);
2378					var exitAction = ui.actions.get('exit');
2379					footer.appendChild(addMenuItem('', exitAction.funct, null, mxResources.get('exit'), exitAction, Editor.closeImage));
2380				}
2381
2382				var pageMenu = this.createPageMenuTab(false);
2383				pageMenu.style.display = 'none';
2384				pageMenu.style.position = '';
2385				pageMenu.style.marginLeft = '';
2386				pageMenu.style.top = '';
2387				pageMenu.style.left = '';
2388				pageMenu.style.height = '100%';
2389				pageMenu.style.lineHeight = '';
2390				pageMenu.style.borderStyle = 'none';
2391				pageMenu.style.padding = '3px 0';
2392				pageMenu.style.margin = '0px';
2393				pageMenu.style.background = '';
2394				pageMenu.style.border = '';
2395				pageMenu.style.boxShadow = 'none';
2396				pageMenu.style.verticalAlign = 'top';
2397				pageMenu.firstChild.style.height = '100%';
2398				pageMenu.firstChild.style.opacity = '0.6';
2399				pageMenu.firstChild.style.margin = '0px';
2400				footer.appendChild(pageMenu);
2401
2402				function pagesVisibleChanged()
2403				{
2404					pageMenu.style.display = ui.pages != null &&
2405						(urlParams['pages'] == '1' || ui.pages.length > 1 ||
2406						Editor.pagesVisible) ? 'inline-block' : 'none';
2407				};
2408
2409				// Page menu only visible for multiple pages
2410				ui.addListener('fileDescriptorChanged', pagesVisibleChanged);
2411				ui.addListener('pagesVisibleChanged', pagesVisibleChanged);
2412				pagesVisibleChanged();
2413
2414				ui.tabContainer.style.visibility = 'hidden';
2415				menubar.style.cssText = 'position:absolute;right:12px;top:10px;height:30px;z-index:1;border-radius:4px;' +
2416					'box-shadow:0px 0px 3px 1px #d1d1d1;padding:6px 0px 6px 6px;border-bottom:1px solid lightgray;' +
2417					'text-align:right;white-space:nowrap;overflow:hidden;user-select:none;';
2418				toolbar.style.cssText = 'position:absolute;left:10px;top:10px;height:30px;z-index:1;border-radius:4px;' +
2419					'box-shadow:0px 0px 3px 1px #d1d1d1;padding:6px;border-bottom:1px solid lightgray;' +
2420					'text-align:right;white-space:nowrap;overflow:hidden;user-select:none;';
2421				footer.style.cssText = 'position:absolute;right:12px;bottom:12px;height:28px;z-index:1;border-radius:4px;' +
2422					'box-shadow:0px 0px 3px 1px #d1d1d1;padding:8px;white-space:nowrap;user-select:none;';
2423				wrapper.appendChild(toolbar);
2424				wrapper.appendChild(footer);
2425
2426				picker.style.cssText = 'position:absolute;left:10px;z-index:1;border-radius:4px;' +
2427					'box-shadow:0px 0px 3px 1px #d1d1d1;padding:8px 6px 4px 6px;white-space:nowrap;' +
2428					'transform:translate(0, -50%);top:50%;user-select:none;';
2429				wrapper.appendChild(picker);
2430
2431				window.setTimeout(function()
2432				{
2433					mxUtils.setPrefixedStyle(picker.style, 'transition', 'transform .3s ease-out');
2434				}, 0);
2435
2436				if (urlParams['format-toolbar'] == '1')
2437				{
2438					this.installFormatToolbar(wrapper);
2439				}
2440			}
2441			else
2442			{
2443				var fitElt = addMenuItem('', fitFunction, true, mxResources.get('fit') + ' (' + Editor.ctrlKey + '+H)', resetViewAction, Editor.zoomFitImage);
2444
2445				menubar.style.cssText = 'position:absolute;left:0px;right:0px;top:0px;height:30px;padding:8px;' +
2446					'text-align:left;white-space:nowrap;';
2447				this.tabContainer.style.right = '70px';
2448				var elt = menuObj.addMenu('100%', viewZoomMenu.funct);
2449				elt.setAttribute('title', mxResources.get('zoom') + ' (Alt+Mousewheel)');
2450				elt.style.whiteSpace = 'nowrap';
2451	        	elt.style.paddingRight = '10px';
2452				elt.style.textDecoration = 'none';
2453				elt.style.textDecoration = 'none';
2454				elt.style.overflow = 'hidden';
2455				elt.style.visibility = 'hidden';
2456				elt.style.textAlign = 'center';
2457				elt.style.cursor = 'pointer';
2458				elt.style.height = (parseInt(ui.tabContainerHeight) - 1) + 'px';
2459				elt.style.lineHeight = (parseInt(ui.tabContainerHeight) + 1) + 'px';
2460				elt.style.position = 'absolute';
2461				elt.style.display = 'block';
2462				elt.style.fontSize = '12px';
2463				elt.style.width = '59px';
2464				elt.style.right = '0px';
2465				elt.style.bottom = '0px';
2466	        	elt.style.backgroundImage = 'url(' + mxWindow.prototype.minimizeImage + ')';
2467	        	elt.style.backgroundPosition = 'right 6px center';
2468	        	elt.style.backgroundRepeat = 'no-repeat';
2469				wrapper.appendChild(elt);
2470			}
2471
2472	    	// Updates the label if the scale changes
2473			(function(elt)
2474			{
2475				var updateZoom = mxUtils.bind(this, function()
2476				{
2477					elt.innerHTML = Math.round(ui.editor.graph.view.scale * 100) + '%';
2478				});
2479
2480				ui.editor.graph.view.addListener(mxEvent.EVENT_SCALE, updateZoom);
2481				ui.editor.addListener('resetGraphView', updateZoom);
2482				ui.editor.addListener('pageSelected', updateZoom);
2483			})(elt);
2484
2485	    	// Augments setGraphEnabled to update visible state
2486	    	var uiSetGraphEnabled = ui.setGraphEnabled;
2487
2488	    	ui.setGraphEnabled = function()
2489	    	{
2490	    		uiSetGraphEnabled.apply(this, arguments);
2491
2492	    		if (this.tabContainer != null)
2493	    		{
2494	    			elt.style.visibility = this.tabContainer.style.visibility;
2495    	        	this.diagramContainer.style.bottom = (this.tabContainer.style.visibility != 'hidden' &&
2496						footer == null) ? this.tabContainerHeight + 'px' : '0px';
2497	    		}
2498	    	};
2499		}
2500
2501        wrapper.appendChild(menubar);
2502        wrapper.appendChild(ui.diagramContainer);
2503        previousParent.appendChild(wrapper);
2504        ui.updateTabContainer();
2505
2506		if (footer == null)
2507		{
2508        	wrapper.appendChild(ui.tabContainer);
2509		}
2510
2511        var langMenuElt = null;
2512
2513        function refreshMenu()
2514        {
2515			if (urlParams['sketch'] == '1')
2516			{
2517				if (urlParams['embedInline'] != '1')
2518				{
2519					toolbar.style.left = (picker.offsetTop - picker.offsetHeight / 2 < 58) ? '70px' : '10px';
2520				}
2521			}
2522			else
2523			{
2524				// Removes all existing menu items
2525				var node = menubar.firstChild;
2526
2527				while (node != null)
2528				{
2529					var temp = node.nextSibling;
2530
2531					if (node.className == 'geMenuItem' || node.className == 'geItem')
2532					{
2533						node.parentNode.removeChild(node);
2534					}
2535
2536					node = temp;
2537				}
2538
2539				before = menubar.firstChild;
2540				iw = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
2541				var small = iw < 1000 || urlParams['sketch'] == '1';
2542				var appElt = null;
2543
2544				if (!small)
2545				{
2546					appElt = addMenu('diagram');
2547				}
2548
2549				var temp = (small) ? addMenu('diagram', null, Editor.drawLogoImage) : null;
2550
2551				if (temp != null)
2552				{
2553					appElt = temp;
2554				}
2555
2556		        createGroup([appElt, addMenuItem(mxResources.get('shapes'), ui.actions.get('toggleShapes').funct, null,
2557					mxResources.get('shapes'), ui.actions.get('image'), (small) ? Editor.shapesImage : null),
2558	       			addMenuItem(mxResources.get('format'), ui.actions.get('toggleFormat').funct, null,
2559	       			mxResources.get('format') + ' (' + ui.actions.get('formatPanel').shortcut + ')', ui.actions.get('image'),
2560	   				(small) ? Editor.formatImage : null)],
2561	   				(small) ? 60 : null);
2562
2563		        var elt = addMenu('insert', true, (small) ? insertImage : null);
2564	        	createGroup([elt, addMenuItem(mxResources.get('delete'), ui.actions.get('delete').funct,
2565					null, mxResources.get('delete'), ui.actions.get('delete'),
2566		        	(small) ? Editor.trashImage : null)], (small) ? 60 : null);
2567
2568		        if (iw >= 411)
2569		        {
2570			        createGroup([undoElt, redoElt], 60);
2571
2572			        if (iw >= 520)
2573			        {
2574				        createGroup([fitElt,
2575					        (iw >= 640) ? addMenuItem('', zoomInAction.funct, true, mxResources.get('zoomIn') + ' (' + Editor.ctrlKey + ' +)',
2576								zoomInAction, Editor.zoomInImage) : null,
2577					        (iw >= 640) ? addMenuItem('', zoomOutAction.funct, true, mxResources.get('zoomOut') + ' (' + Editor.ctrlKey + ' -)',
2578								zoomOutAction, Editor.zoomOutImage) : null], 60);
2579			        }
2580		        }
2581			}
2582
2583			if (appElt != null)
2584			{
2585				mxEvent.disableContextMenu(appElt);
2586
2587				mxEvent.addGestureListeners(appElt, mxUtils.bind(this, function(evt)
2588				{
2589					if (mxEvent.isShiftDown(evt) || mxEvent.isAltDown(evt) ||
2590						mxEvent.isMetaDown(evt) || mxEvent.isControlDown(evt) ||
2591						mxEvent.isPopupTrigger(evt))
2592					{
2593						ui.appIconClicked(evt);
2594					}
2595				}), null, null);
2596			}
2597
2598	        var langMenu = ui.menus.get('language');
2599
2600			if (langMenu != null && !mxClient.IS_CHROMEAPP &&
2601				!EditorUi.isElectronApp && iw >= 600 &&
2602				urlParams['sketch'] != '1')
2603			{
2604				if (langMenuElt == null)
2605				{
2606					var elt = menuObj.addMenu('', langMenu.funct);
2607					elt.setAttribute('title', mxResources.get('language'));
2608					elt.className = 'geToolbarButton';
2609					elt.style.backgroundImage = 'url(' + Editor.globeImage + ')';
2610		        	elt.style.backgroundPosition = 'center center';
2611		        	elt.style.backgroundRepeat = 'no-repeat';
2612		        	elt.style.backgroundSize = '24px 24px';
2613					elt.style.position = 'absolute';
2614		        	elt.style.height = '24px';
2615		        	elt.style.width = '24px';
2616					elt.style.zIndex = '1';
2617					elt.style.right = '8px';
2618					elt.style.cursor = 'pointer';
2619					elt.style.top = (urlParams['embed'] == '1') ? '12px' : '11px';
2620					menubar.appendChild(elt);
2621					langMenuElt = elt;
2622				}
2623
2624				ui.buttonContainer.style.paddingRight = '34px';
2625			}
2626			else
2627			{
2628				ui.buttonContainer.style.paddingRight = '4px';
2629
2630				if (langMenuElt != null)
2631				{
2632					langMenuElt.parentNode.removeChild(langMenuElt);
2633					langMenuElt = null;
2634				}
2635			}
2636        };
2637
2638        refreshMenu();
2639
2640        mxEvent.addListener(window, 'resize', function()
2641		{
2642        	refreshMenu();
2643
2644            if (ui.sidebarWindow != null)
2645            {
2646                ui.sidebarWindow.window.fit();
2647            }
2648
2649            if (ui.formatWindow != null)
2650            {
2651            	ui.formatWindow.window.fit();
2652            }
2653
2654            if (ui.actions.outlineWindow != null)
2655            {
2656            	ui.actions.outlineWindow.window.fit();
2657            }
2658
2659            if (ui.actions.layersWindow != null)
2660            {
2661            	ui.actions.layersWindow.window.fit();
2662            }
2663
2664            if (ui.menus.tagsWindow != null)
2665            {
2666            	ui.menus.tagsWindow.window.fit();
2667            }
2668
2669            if (ui.menus.findWindow != null)
2670            {
2671            	ui.menus.findWindow.window.fit();
2672            }
2673
2674            if (ui.menus.findReplaceWindow != null)
2675            {
2676            	ui.menus.findReplaceWindow.window.fit();
2677            }
2678		});
2679
2680		if (urlParams['embedInline'] == '1')
2681		{
2682			document.body.style.cursor = 'text';
2683			picker.style.transform = '';
2684
2685			mxEvent.addGestureListeners(ui.diagramContainer.parentNode, function(evt)
2686			{
2687				if (mxEvent.getSource(evt) ==
2688					ui.diagramContainer.parentNode)
2689				{
2690					ui.embedExitPoint = new mxPoint(mxEvent.getClientX(evt), mxEvent.getClientY(evt));
2691					ui.sendEmbeddedSvgExport();
2692				}
2693			});
2694
2695			var div = document.createElement('div');
2696			div.style.position = 'absolute';
2697			div.style.width = '10px';
2698			div.style.height = '10px';
2699			div.style.borderRadius = '5px';
2700			div.style.border = '1px solid gray';
2701			div.style.background = '#ffffff';
2702			div.style.cursor = 'row-resize';
2703			ui.diagramContainer.parentNode.appendChild(div);
2704			ui.bottomResizer = div;
2705
2706			var x0 = null;
2707			var y0 = null;
2708			var w0 = null;
2709			var h0 = null;
2710
2711			mxEvent.addGestureListeners(div, function(evt)
2712			{
2713				h0 = parseInt(ui.diagramContainer.style.height);
2714				y0 = mxEvent.getClientY(evt);
2715				graph.popupMenuHandler.hideMenu();
2716				mxEvent.consume(evt);
2717			});
2718
2719			div = div.cloneNode(false);
2720			div.style.cursor = 'col-resize';
2721			ui.diagramContainer.parentNode.appendChild(div);
2722			ui.rightResizer = div;
2723
2724			mxEvent.addGestureListeners(div, function(evt)
2725			{
2726				w0 = parseInt(ui.diagramContainer.style.width);
2727				x0 = mxEvent.getClientX(evt);
2728				graph.popupMenuHandler.hideMenu();
2729				mxEvent.consume(evt);
2730			});
2731
2732			mxEvent.addGestureListeners(document.body, null, function(evt)
2733			{
2734				var changed = false;
2735
2736				if (x0 != null)
2737				{
2738					ui.diagramContainer.style.width = Math.max(20,
2739						w0 + mxEvent.getClientX(evt) - x0) + 'px';
2740					changed = true;
2741				}
2742
2743				if (y0 != null)
2744				{
2745					ui.diagramContainer.style.height = Math.max(20,
2746						h0 + mxEvent.getClientY(evt) - y0) + 'px';
2747					changed = true;
2748				}
2749
2750				if (changed)
2751				{
2752					var parent = window.opener || window.parent;
2753					parent.postMessage(JSON.stringify({
2754						event: 'resize',
2755						fullscreen: Editor.inlineFullscreen,
2756						rect: ui.diagramContainer.getBoundingClientRect()
2757					}), '*');
2758					inlineSizeChanged();
2759					ui.refresh();
2760				}
2761			}, function(evt)
2762			{
2763				if (x0 != null || y0 != null)
2764				{
2765					mxEvent.consume(evt);
2766				}
2767
2768				x0 = null;
2769				y0 = null;
2770			});
2771
2772			this.diagramContainer.style.borderRadius = '4px';
2773			document.body.style.backgroundColor = 'transparent';
2774			ui.bottomResizer.style.visibility = 'hidden';
2775			ui.rightResizer.style.visibility = 'hidden';
2776			toolbar.style.visibility = 'hidden';
2777			footer.style.visibility = 'hidden';
2778			picker.style.display = 'none';
2779		}
2780
2781		if (urlParams['prefetchFonts'] == '1')
2782		{
2783			ui.editor.loadFonts();
2784		}
2785	};
2786};
2787
2788(function()
2789{
2790	var initialized = false;
2791
2792	// ChromeApp has async local storage
2793	if (uiTheme == 'min' && !initialized && !mxClient.IS_CHROMEAPP)
2794	{
2795		EditorUi.initMinimalTheme();
2796		initialized = true;
2797	}
2798
2799	var uiInitTheme = EditorUi.initTheme;
2800
2801	// For async startup like chromeos
2802	EditorUi.initTheme = function()
2803	{
2804		uiInitTheme.apply(this, arguments);
2805
2806		if (uiTheme == 'min' && !initialized)
2807		{
2808			this.initMinimalTheme();
2809			initialized = true;
2810		}
2811	};
2812})();
2813