1/**
2 * Copyright (c) 2006-2012, JGraph Ltd
3 */
4Format = function(editorUi, container)
5{
6	this.editorUi = editorUi;
7	this.container = container;
8};
9
10/**
11 * Background color for inactive tabs.
12 */
13Format.inactiveTabBackgroundColor = '#f1f3f4';
14
15/**
16 * Icons for markers (24x16).
17 */
18Format.classicFilledMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 L 10 2 L 5 8 L 10 14 Z M 0 8 L 24 8" stroke="#404040" fill="#404040"/>', 32, 20);
19Format.classicThinFilledMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 L 8 4 L 3 8 L 8 12 Z M 0 8 L 24 8" stroke="#404040" fill="#404040"/>', 32, 20);
20Format.openFilledMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 8 0 L 0 8 L 8 16 M 0 8 L 24 8" stroke="#404040" fill="transparent"/>', 32, 20);
21Format.openThinFilledMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 8 4 L 0 8 L 8 12 M 0 8 L 24 8" stroke="#404040" fill="transparent"/>', 32, 20);
22Format.openAsyncFilledMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 8 4 L 0 8 L 24 8" stroke="#404040" fill="transparent"/>', 32, 20);
23Format.blockFilledMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 L 8 2 L 8 14 Z M 0 8 L 24 8" stroke="#404040" fill="#404040"/>', 32, 20);
24Format.blockThinFilledMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 L 8 4 L 8 12 Z M 0 8 L 24 8" stroke="#404040" fill="#404040"/>', 32, 20);
25Format.asyncFilledMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 6 8 L 6 4 L 0 8 L 24 8" stroke="#404040" fill="#404040"/>', 32, 20);
26Format.ovalFilledMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 A 5 5 0 0 1 5 3 A 5 5 0 0 1 11 8 A 5 5 0 0 1 5 13 A 5 5 0 0 1 0 8 Z M 10 8 L 24 8" stroke="#404040" fill="#404040"/>', 32, 20);
27Format.diamondFilledMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 L 6 2 L 12 8 L 6 14 Z M 0 8 L 24 8" stroke="#404040" fill="#404040"/>', 32, 20);
28Format.diamondThinFilledMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 L 8 3 L 16 8 L 8 13 Z M 0 8 L 24 8" stroke="#404040" fill="#404040"/>', 32, 20);
29Format.classicMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 L 10 2 L 5 8 L 10 14 Z M 5 8 L 24 8" stroke="#404040" fill="transparent"/>', 32, 20);
30Format.classicThinMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 L 8 4 L 5 8 L 8 12 Z M 5 8 L 24 8" stroke="#404040" fill="transparent"/>', 32, 20);
31Format.blockMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 L 8 2 L 8 14 Z M 8 8 L 24 8" stroke="#404040" fill="transparent"/>', 32, 20);
32Format.blockThinMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 L 8 4 L 8 12 Z M 8 8 L 24 8" stroke="#404040" fill="transparent"/>', 32, 20);
33Format.asyncMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 6 8 L 6 4 L 0 8 L 24 8" stroke="#404040" fill="transparent"/>', 32, 20);
34Format.ovalMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 A 5 5 0 0 1 5 3 A 5 5 0 0 1 11 8 A 5 5 0 0 1 5 13 A 5 5 0 0 1 0 8 Z M 10 8 L 24 8" stroke="#404040" fill="transparent"/>', 32, 20);
35Format.diamondMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 L 6 2 L 12 8 L 6 14 Z M 12 8 L 24 8" stroke="#404040" fill="transparent"/>', 32, 20);
36Format.diamondThinMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 L 8 3 L 16 8 L 8 13 Z M 16 8 L 24 8" stroke="#404040" fill="transparent"/>', 32, 20);
37Format.boxMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 0 3 L 10 3 L 10 13 L 0 13 Z M 10 8 L 24 8" stroke="#404040" fill="transparent"/>', 32, 20);
38Format.halfCircleMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 0 3 A 5 5 0 0 1 5 8 A 5 5 0 0 1 0 13 M 5 8 L 24 8" stroke="#404040" fill="transparent"/>', 32, 20);
39Format.dashMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 0 2 L 12 14 M 0 8 L 24 8" stroke="#404040" fill="transparent"/>', 32, 20);
40Format.crossMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 0 2 L 12 14 M 12 2 L 0 14 M 0 8 L 24 8" stroke="#404040" fill="transparent"/>', 32, 20);
41Format.circlePlusMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 A 6 6 0 0 1 6 2 A 6 6 0 0 1 12 8 A 6 6 0 0 1 6 14 A 6 6 0 0 1 0 8 Z M 6 2 L 6 14 M 0 8 L 24 8" stroke="#404040" fill="transparent"/>', 32, 20);
42Format.circleMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 A 6 6 0 0 1 6 2 A 6 6 0 0 1 12 8 A 6 6 0 0 1 6 14 A 6 6 0 0 1 0 8 Z M 12 8 L 24 8" stroke="#404040" fill="transparent"/>', 32, 20);
43Format.ERmandOneMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 6 2 L 6 14 M 9 2 L 9 14 M 0 8 L 24 8" stroke="#404040" fill="transparent"/>', 32, 20);
44Format.ERmanyMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 0 2 L 12 8 L 0 14 M 0 8 L 24 8" stroke="#404040" fill="transparent"/>', 32, 20);
45Format.ERoneToManyMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 0 2 L 12 8 L 0 14 M 15 2 L 15 14 M 0 8 L 24 8" stroke="#404040" fill="transparent"/>', 32, 20);
46Format.ERzeroToOneMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 8 8 A 5 5 0 0 1 13 3 A 5 5 0 0 1 18 8 A 5 5 0 0 1 13 13 A 5 5 0 0 1 8 8 Z M 0 8 L 8 8 M 18 8 L 24 8 M 4 3 L 4 13" stroke="#404040" fill="transparent"/>', 32, 20);
47Format.ERzeroToManyMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 8 8 A 5 5 0 0 1 13 3 A 5 5 0 0 1 18 8 A 5 5 0 0 1 13 13 A 5 5 0 0 1 8 8 Z M 0 8 L 8 8 M 18 8 L 24 8 M 0 3 L 8 8 L 0 13" stroke="#404040" fill="transparent"/>', 32, 20);
48Format.EROneMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 5 2 L 5 14 M 0 8 L 24 8" stroke="#404040" fill="transparent"/>', 32, 20);
49Format.baseDashMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 0 2 L 0 14 M 0 8 L 24 8" stroke="#404040" fill="transparent"/>', 32, 20);
50Format.doubleBlockMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 L 8 2 L 8 14 Z M 8 8 L 16 2 L 16 14 Z M 16 8 L 24 8" stroke="#404040" fill="transparent"/>', 32, 20);
51Format.doubleBlockFilledMarkerImage = Graph.createSvgImage(20, 22, '<path transform="translate(4,2)" stroke-width="2" d="M 0 8 L 8 2 L 8 14 Z M 8 8 L 16 2 L 16 14 Z M 16 8 L 24 8" stroke="#404040" fill="#404040"/>', 32, 20);
52
53/**
54 * Adds a style change item to the given menu.
55 */
56Format.processMenuIcon = function(elt, transform)
57{
58	 var imgs = elt.getElementsByTagName('img');
59
60	 if (imgs.length > 0)
61	 {
62		 if (Editor.isDarkMode())
63		 {
64			 imgs[0].style.filter = 'invert(100%)';
65		 }
66
67		 imgs[0].className = 'geIcon';
68		 imgs[0].style.padding = '0px';
69		 imgs[0].style.margin = '0 0 0 2px';
70
71		 if (transform != null)
72		 {
73		 	mxUtils.setPrefixedStyle(imgs[0].style, 'transform', transform);
74		 }
75	 }
76
77	 return elt;
78};
79
80/**
81 * Returns information about the current selection.
82 */
83Format.prototype.labelIndex = 0;
84
85/**
86 * Returns information about the current selection.
87 */
88Format.prototype.diagramIndex = 0;
89
90/**
91 * Returns information about the current selection.
92 */
93Format.prototype.currentIndex = 0;
94
95/**
96 * Returns information about the current selection.
97 */
98Format.prototype.showCloseButton = true;
99
100/**
101 * Background color for inactive tabs.
102 */
103Format.prototype.roundableShapes = ['label', 'rectangle', 'internalStorage', 'corner',
104	'parallelogram', 'swimlane', 'triangle', 'trapezoid', 'ext', 'step', 'tee', 'process',
105	'link', 'rhombus', 'offPageConnector', 'loopLimit', 'hexagon', 'manualInput', 'card',
106	'curlyBracket', 'singleArrow', 'callout', 'doubleArrow', 'flexArrow', 'umlLifeline'];
107
108/**
109 * Adds the label menu items to the given menu and parent.
110 */
111Format.prototype.init = function()
112{
113	var ui = this.editorUi;
114	var editor = ui.editor;
115	var graph = editor.graph;
116
117	this.update = mxUtils.bind(this, function(sender, evt)
118	{
119		this.clearSelectionState();
120		this.refresh();
121	});
122
123	graph.getSelectionModel().addListener(mxEvent.CHANGE, this.update);
124	graph.addListener(mxEvent.EDITING_STARTED, this.update);
125	graph.addListener(mxEvent.EDITING_STOPPED, this.update);
126	graph.getModel().addListener(mxEvent.CHANGE, this.update);
127	graph.getView().addListener('unitChanged', this.update);
128
129	graph.addListener(mxEvent.ROOT, mxUtils.bind(this, function()
130	{
131		this.refresh();
132	}));
133
134	ui.addListener('styleChanged', mxUtils.bind(this, function(sender, evt)
135	{
136		this.refresh();
137	}));
138
139	editor.addListener('autosaveChanged', mxUtils.bind(this, function()
140	{
141		this.refresh();
142	}));
143
144	this.refresh();
145};
146
147/**
148 * Returns information about the current selection.
149 */
150Format.prototype.clearSelectionState = function()
151{
152	this.selectionState = null;
153};
154
155/**
156 * Returns information about the current selection.
157 */
158Format.prototype.getSelectionState = function()
159{
160	if (this.selectionState == null)
161	{
162		this.selectionState = this.createSelectionState();
163	}
164
165	return this.selectionState;
166};
167
168/**
169 * Returns information about the current selection.
170 */
171Format.prototype.createSelectionState = function()
172{
173	var cells = this.editorUi.editor.graph.getSelectionCells();
174	var result = this.initSelectionState();
175	var initial = true;
176
177	for (var i = 0; i < cells.length; i++)
178	{
179		var style = this.editorUi.editor.graph.getCurrentCellStyle(cells[i]);
180
181		if (mxUtils.getValue(style, mxConstants.STYLE_EDITABLE, '1') != '0')
182		{
183			this.updateSelectionStateForCell(result, cells[i], cells, initial);
184			initial = false;
185		}
186	}
187
188	return result;
189};
190
191/**
192 * Returns information about the current selection.
193 */
194Format.prototype.initSelectionState = function()
195{
196	return {vertices: [], edges: [], cells: [], x: null, y: null, width: null, height: null,
197		style: {}, containsImage: false, containsLabel: false, fill: true, glass: true,
198		rounded: true, autoSize: false, image: true, shadow: true, lineJumps: true, resizable: true,
199		table: false, cell: false, row: false, movable: true, rotatable: true, stroke: true};
200};
201
202/**
203 * Returns information about the current selection.
204 */
205Format.prototype.updateSelectionStateForCell = function(result, cell, cells, initial)
206{
207	var graph = this.editorUi.editor.graph;
208	result.cells.push(cell);
209
210	if (graph.getModel().isVertex(cell))
211	{
212		result.resizable = result.resizable && graph.isCellResizable(cell);
213		result.rotatable = result.rotatable && graph.isCellRotatable(cell);
214		result.movable = result.movable && graph.isCellMovable(cell) &&
215			!graph.isTableRow(cell) && !graph.isTableCell(cell);
216		result.table = result.table || graph.isTable(cell);
217		result.cell = result.cell || graph.isTableCell(cell);
218		result.row = result.row || graph.isTableRow(cell);
219		result.vertices.push(cell);
220		var geo = graph.getCellGeometry(cell);
221
222		if (geo != null)
223		{
224			if (geo.width > 0)
225			{
226				if (result.width == null)
227				{
228					result.width = geo.width;
229				}
230				else if (result.width != geo.width)
231				{
232					result.width = '';
233				}
234			}
235			else
236			{
237				result.containsLabel = true;
238			}
239
240			if (geo.height > 0)
241			{
242				if (result.height == null)
243				{
244					result.height = geo.height;
245				}
246				else if (result.height != geo.height)
247				{
248					result.height = '';
249				}
250			}
251			else
252			{
253				result.containsLabel = true;
254			}
255
256			if (!geo.relative || geo.offset != null)
257			{
258				var x = (geo.relative) ? geo.offset.x : geo.x;
259				var y = (geo.relative) ? geo.offset.y : geo.y;
260
261				if (result.x == null)
262				{
263					result.x = x;
264				}
265				else if (result.x != x)
266				{
267					result.x = '';
268				}
269
270				if (result.y == null)
271				{
272					result.y = y;
273				}
274				else if (result.y != y)
275				{
276					result.y = '';
277				}
278			}
279		}
280	}
281	else if (graph.getModel().isEdge(cell))
282	{
283		result.edges.push(cell);
284		result.resizable = false;
285		result.rotatable = false;
286		result.movable = false;
287	}
288
289	var state = graph.view.getState(cell);
290
291	if (state != null)
292	{
293		result.autoSize = result.autoSize || this.isAutoSizeState(state);
294		result.glass = result.glass && this.isGlassState(state);
295		result.rounded = result.rounded && this.isRoundedState(state);
296		result.lineJumps = result.lineJumps && this.isLineJumpState(state);
297		result.image = result.image && this.isImageState(state);
298		result.shadow = result.shadow && this.isShadowState(state);
299		result.fill = result.fill && this.isFillState(state);
300		result.stroke = result.stroke && this.isStrokeState(state);
301
302		var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null);
303		result.containsImage = result.containsImage || shape == 'image';
304		graph.mergeStyle(state.style, result.style, initial);
305	}
306};
307
308/**
309 * Returns information about the current selection.
310 */
311Format.prototype.isFillState = function(state)
312{
313	return !this.isSpecialColor(state.style[mxConstants.STYLE_FILLCOLOR]) &&
314		(state.view.graph.model.isVertex(state.cell) ||
315		mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null) == 'arrow' ||
316		mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null) == 'filledEdge' ||
317		mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null) == 'flexArrow');
318};
319
320/**
321 * Returns information about the current selection.
322 */
323Format.prototype.isStrokeState = function(state)
324{
325	return !this.isSpecialColor(state.style[mxConstants.STYLE_STROKECOLOR]);
326};
327
328/**
329 * Returns information about the current selection.
330 */
331Format.prototype.isSpecialColor = function(color)
332{
333	return mxUtils.indexOf([mxConstants.STYLE_STROKECOLOR,
334		mxConstants.STYLE_FILLCOLOR, 'inherit', 'swimlane',
335		'indicated'], color) >= 0;
336};
337
338/**
339 * Returns information about the current selection.
340 */
341Format.prototype.isGlassState = function(state)
342{
343	var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null);
344
345	return (shape == 'label' || shape == 'rectangle' || shape == 'internalStorage' ||
346			shape == 'ext' || shape == 'umlLifeline' || shape == 'swimlane' ||
347			shape == 'process');
348};
349
350/**
351 * Returns information about the current selection.
352 */
353Format.prototype.isRoundedState = function(state)
354{
355	return (state.shape != null) ? state.shape.isRoundable() :
356		mxUtils.indexOf(this.roundableShapes, mxUtils.getValue(state.style,
357		mxConstants.STYLE_SHAPE, null)) >= 0;
358};
359
360/**
361 * Returns information about the current selection.
362 */
363Format.prototype.isLineJumpState = function(state)
364{
365	var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null);
366	var curved = mxUtils.getValue(state.style, mxConstants.STYLE_CURVED, false);
367
368	return !curved && (shape == 'connector' || shape == 'filledEdge');
369};
370
371/**
372 * Returns information about the current selection.
373 */
374Format.prototype.isAutoSizeState = function(state)
375{
376	return mxUtils.getValue(state.style, mxConstants.STYLE_AUTOSIZE, null) == '1';
377};
378
379/**
380 * Returns information about the current selection.
381 */
382Format.prototype.isImageState = function(state)
383{
384	var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null);
385
386	return (shape == 'label' || shape == 'image');
387};
388
389/**
390 * Returns information about the current selection.
391 */
392Format.prototype.isShadowState = function(state)
393{
394	var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null);
395
396	return (shape != 'image');
397};
398
399/**
400 * Adds the label menu items to the given menu and parent.
401 */
402Format.prototype.clear = function()
403{
404	this.container.innerHTML = '';
405
406	// Destroy existing panels
407	if (this.panels != null)
408	{
409		for (var i = 0; i < this.panels.length; i++)
410		{
411			this.panels[i].destroy();
412		}
413	}
414
415	this.panels = [];
416};
417
418/**
419 * Adds the label menu items to the given menu and parent.
420 */
421Format.prototype.refresh = function()
422{
423	if (this.pendingRefresh != null)
424	{
425		window.clearTimeout(this.pendingRefresh);
426		this.pendingRefresh = null;
427	}
428
429	this.pendingRefresh = window.setTimeout(mxUtils.bind(this, function()
430	{
431		this.immediateRefresh();
432	}));
433};
434
435/**
436 * Adds the label menu items to the given menu and parent.
437 */
438Format.prototype.immediateRefresh = function()
439{
440	// Performance tweak: No refresh needed if not visible
441	if (this.container.style.width == '0px')
442	{
443		return;
444	}
445
446	this.clear();
447	var ui = this.editorUi;
448	var graph = ui.editor.graph;
449
450	var div = document.createElement('div');
451	div.style.whiteSpace = 'nowrap';
452	div.style.color = 'rgb(112, 112, 112)';
453	div.style.textAlign = 'left';
454	div.style.cursor = 'default';
455
456	var label = document.createElement('div');
457	label.className = 'geFormatSection';
458	label.style.textAlign = 'center';
459	label.style.fontWeight = 'bold';
460	label.style.paddingTop = '8px';
461	label.style.fontSize = '13px';
462	label.style.borderWidth = '0px 0px 1px 1px';
463	label.style.borderStyle = 'solid';
464	label.style.display = 'inline-block';
465	label.style.height = '25px';
466	label.style.overflow = 'hidden';
467	label.style.width = '100%';
468	this.container.appendChild(div);
469
470	// Prevents text selection
471    mxEvent.addListener(label, (mxClient.IS_POINTER) ? 'pointerdown' : 'mousedown',
472        mxUtils.bind(this, function(evt)
473	{
474		evt.preventDefault();
475	}));
476
477	var ss = this.getSelectionState();
478	var containsLabel = ss.containsLabel;
479	var currentLabel = null;
480	var currentPanel = null;
481
482	var addClickHandler = mxUtils.bind(this, function(elt, panel, index, lastEntry)
483	{
484		var clickHandler = mxUtils.bind(this, function(evt)
485		{
486			if (currentLabel != elt)
487			{
488				if (containsLabel)
489				{
490					this.labelIndex = index;
491				}
492				else if (graph.isSelectionEmpty())
493				{
494					this.diagramIndex = index;
495				}
496				else
497				{
498					this.currentIndex = index;
499				}
500
501				if (currentLabel != null)
502				{
503					currentLabel.style.backgroundColor = Format.inactiveTabBackgroundColor;
504					currentLabel.style.borderBottomWidth = '1px';
505				}
506
507				currentLabel = elt;
508				currentLabel.style.backgroundColor = '';
509				currentLabel.style.borderBottomWidth = '0px';
510
511				if (currentPanel != panel)
512				{
513					if (currentPanel != null)
514					{
515						currentPanel.style.display = 'none';
516					}
517
518					currentPanel = panel;
519					currentPanel.style.display = '';
520				}
521			}
522		});
523
524		mxEvent.addListener(elt, 'click', clickHandler);
525
526		// Prevents text selection
527	    mxEvent.addListener(elt, (mxClient.IS_POINTER) ? 'pointerdown' : 'mousedown',
528        	mxUtils.bind(this, function(evt)
529    	{
530			evt.preventDefault();
531		}));
532
533		if ((lastEntry && currentLabel == null) ||
534			(index == ((containsLabel) ? this.labelIndex : ((graph.isSelectionEmpty()) ?
535			this.diagramIndex : this.currentIndex))))
536		{
537			// Invokes handler directly as a workaround for no click on DIV in KHTML.
538			clickHandler();
539		}
540	});
541
542	var idx = 0;
543
544	if (graph.isSelectionEmpty())
545	{
546		mxUtils.write(label, mxResources.get('diagram'));
547		label.style.borderLeftWidth = '0px';
548
549		div.appendChild(label);
550		var diagramPanel = div.cloneNode(false);
551		this.panels.push(new DiagramFormatPanel(this, ui, diagramPanel));
552		this.container.appendChild(diagramPanel);
553
554		if (Editor.styles != null)
555		{
556			diagramPanel.style.display = 'none';
557			label.style.width = (this.showCloseButton) ? '106px' : '50%';
558			label.style.cursor = 'pointer';
559			label.style.backgroundColor = Format.inactiveTabBackgroundColor;
560
561			var label2 = label.cloneNode(false);
562			label2.style.borderLeftWidth = '1px';
563			label2.style.borderRightWidth = '1px';
564			label2.style.backgroundColor = Format.inactiveTabBackgroundColor;
565
566			addClickHandler(label, diagramPanel, idx++);
567
568			var stylePanel = div.cloneNode(false);
569			stylePanel.style.display = 'none';
570			mxUtils.write(label2, mxResources.get('style'));
571			div.appendChild(label2);
572			this.panels.push(new DiagramStylePanel(this, ui, stylePanel));
573			this.container.appendChild(stylePanel);
574
575			addClickHandler(label2, stylePanel, idx++);
576		}
577
578		// Adds button to hide the format panel since
579		// people don't seem to find the toolbar button
580		// and the menu item in the format menu
581		if (this.showCloseButton)
582		{
583			var label2 = label.cloneNode(false);
584			label2.style.borderLeftWidth = '1px';
585			label2.style.borderRightWidth = '1px';
586			label2.style.borderBottomWidth = '1px';
587			label2.style.backgroundColor = Format.inactiveTabBackgroundColor;
588			label2.style.position = 'absolute';
589			label2.style.right = '0px';
590			label2.style.top = '0px';
591			label2.style.width = '25px';
592
593			var img = document.createElement('img');
594			img.setAttribute('border', '0');
595			img.setAttribute('src', Dialog.prototype.closeImage);
596			img.setAttribute('title', mxResources.get('hide'));
597			img.style.position = 'absolute';
598			img.style.display = 'block';
599			img.style.right = '0px';
600			img.style.top = '8px';
601			img.style.cursor = 'pointer';
602			img.style.marginTop = '1px';
603			img.style.marginRight = '6px';
604			img.style.border = '1px solid transparent';
605			img.style.padding = '1px';
606			img.style.opacity = 0.5;
607			label2.appendChild(img)
608
609			mxEvent.addListener(img, 'click', function()
610			{
611				ui.actions.get('formatPanel').funct();
612			});
613
614			div.appendChild(label2);
615		}
616	}
617	else if (graph.isEditing())
618	{
619		mxUtils.write(label, mxResources.get('text'));
620		div.appendChild(label);
621		this.panels.push(new TextFormatPanel(this, ui, div));
622	}
623	else
624	{
625		label.style.backgroundColor = Format.inactiveTabBackgroundColor;
626		label.style.borderLeftWidth = '1px';
627		label.style.cursor = 'pointer';
628		label.style.width = (containsLabel || ss.cells.length == 0) ? '50%' : '33.3%';
629		var label2 = label.cloneNode(false);
630		var label3 = label2.cloneNode(false);
631
632		// Workaround for ignored background in IE
633		label2.style.backgroundColor = Format.inactiveTabBackgroundColor;
634		label3.style.backgroundColor = Format.inactiveTabBackgroundColor;
635
636		// Style
637		if (containsLabel)
638		{
639			label2.style.borderLeftWidth = '0px';
640		}
641		else
642		{
643			label.style.borderLeftWidth = '0px';
644			mxUtils.write(label, mxResources.get('style'));
645			div.appendChild(label);
646
647			var stylePanel = div.cloneNode(false);
648			stylePanel.style.display = 'none';
649			this.panels.push(new StyleFormatPanel(this, ui, stylePanel));
650			this.container.appendChild(stylePanel);
651
652			addClickHandler(label, stylePanel, idx++);
653		}
654
655		// Text
656		mxUtils.write(label2, mxResources.get('text'));
657		div.appendChild(label2);
658
659		var textPanel = div.cloneNode(false);
660		textPanel.style.display = 'none';
661		this.panels.push(new TextFormatPanel(this, ui, textPanel));
662		this.container.appendChild(textPanel);
663
664		// Arrange
665		mxUtils.write(label3, mxResources.get('arrange'));
666		div.appendChild(label3);
667
668		var arrangePanel = div.cloneNode(false);
669		arrangePanel.style.display = 'none';
670		this.panels.push(new ArrangePanel(this, ui, arrangePanel));
671		this.container.appendChild(arrangePanel);
672
673		if (ss.cells.length > 0)
674		{
675			addClickHandler(label2, textPanel, idx++);
676		}
677		else
678		{
679			label2.style.display = 'none';
680		}
681
682		addClickHandler(label3, arrangePanel, idx++, true);
683	}
684};
685
686/**
687 * Base class for format panels.
688 */
689BaseFormatPanel = function(format, editorUi, container)
690{
691	this.format = format;
692	this.editorUi = editorUi;
693	this.container = container;
694	this.listeners = [];
695};
696
697/**
698 *
699 */
700BaseFormatPanel.prototype.buttonBackgroundColor = 'white';
701
702/**
703 * Install input handler.
704 */
705BaseFormatPanel.prototype.installInputHandler = function(input, key, defaultValue, min, max, unit, textEditFallback, isFloat)
706{
707	unit = (unit != null) ? unit : '';
708	isFloat = (isFloat != null) ? isFloat : false;
709
710	var ui = this.editorUi;
711	var graph = ui.editor.graph;
712
713	min = (min != null) ? min : 1;
714	max = (max != null) ? max : 999;
715
716	var selState = null;
717	var updating = false;
718
719	var update = mxUtils.bind(this, function(evt)
720	{
721		var value = (isFloat) ? parseFloat(input.value) : parseInt(input.value);
722
723		// Special case: angle mod 360
724		if (!isNaN(value) && key == mxConstants.STYLE_ROTATION)
725		{
726			// Workaround for decimal rounding errors in floats is to
727			// use integer and round all numbers to two decimal point
728			value = mxUtils.mod(Math.round(value * 100), 36000) / 100;
729		}
730
731		value = Math.min(max, Math.max(min, (isNaN(value)) ? defaultValue : value));
732
733		if (graph.cellEditor.isContentEditing() && textEditFallback)
734		{
735			if (!updating)
736			{
737				updating = true;
738
739				if (selState != null)
740				{
741					graph.cellEditor.restoreSelection(selState);
742					selState = null;
743				}
744
745				textEditFallback(value);
746				input.value = value + unit;
747
748				// Restore focus and selection in input
749				updating = false;
750			}
751		}
752		else if (value != mxUtils.getValue(this.format.getSelectionState().style, key, defaultValue))
753		{
754			if (graph.isEditing())
755			{
756				graph.stopEditing(true);
757			}
758
759			graph.getModel().beginUpdate();
760			try
761			{
762				var cells = this.format.getSelectionState().cells;
763				graph.setCellStyles(key, value, cells);
764
765				// Handles special case for fontSize where HTML labels are parsed and updated
766				if (key == mxConstants.STYLE_FONTSIZE)
767				{
768					graph.updateLabelElements(cells, function(elt)
769					{
770						elt.style.fontSize = value + 'px';
771						elt.removeAttribute('size');
772					});
773				}
774
775				for (var i = 0; i < cells.length; i++)
776				{
777					if (graph.model.getChildCount(cells[i]) == 0)
778					{
779						graph.autoSizeCell(cells[i], false);
780					}
781				}
782
783				ui.fireEvent(new mxEventObject('styleChanged', 'keys', [key],
784						'values', [value], 'cells', cells));
785			}
786			finally
787			{
788				graph.getModel().endUpdate();
789			}
790		}
791
792		input.value = value + unit;
793		mxEvent.consume(evt);
794	});
795
796	if (textEditFallback && graph.cellEditor.isContentEditing())
797	{
798		// KNOWN: Arrow up/down clear selection text in quirks/IE 8
799		// Text size via arrow button limits to 16 in IE11. Why?
800		mxEvent.addListener(input, 'mousedown', function()
801		{
802			if (document.activeElement == graph.cellEditor.textarea)
803			{
804				selState = graph.cellEditor.saveSelection();
805			}
806		});
807
808		mxEvent.addListener(input, 'touchstart', function()
809		{
810			if (document.activeElement == graph.cellEditor.textarea)
811			{
812				selState = graph.cellEditor.saveSelection();
813			}
814		});
815	}
816
817	mxEvent.addListener(input, 'change', update);
818	mxEvent.addListener(input, 'blur', update);
819
820	return update;
821};
822
823/**
824 * Adds the given option.
825 */
826BaseFormatPanel.prototype.createPanel = function()
827{
828	var div = document.createElement('div');
829	div.className = 'geFormatSection';
830	div.style.padding = '12px 0px 12px 14px';
831
832	return div;
833};
834
835/**
836 * Adds the given option.
837 */
838BaseFormatPanel.prototype.createTitle = function(title)
839{
840	var div = document.createElement('div');
841	div.style.padding = '0px 0px 6px 0px';
842	div.style.whiteSpace = 'nowrap';
843	div.style.overflow = 'hidden';
844	div.style.width = '200px';
845	div.style.fontWeight = 'bold';
846	mxUtils.write(div, title);
847
848	return div;
849};
850
851/**
852 *
853 */
854BaseFormatPanel.prototype.createStepper = function(input, update, step, height, disableFocus, defaultValue, isFloat)
855{
856	step = (step != null) ? step : 1;
857	height = (height != null) ? height : 9;
858	var bigStep = 10 * step;
859
860	var stepper = document.createElement('div');
861	mxUtils.setPrefixedStyle(stepper.style, 'borderRadius', '3px');
862	stepper.style.border = '1px solid rgb(192, 192, 192)';
863	stepper.style.position = 'absolute';
864
865	var up = document.createElement('div');
866	up.style.borderBottom = '1px solid rgb(192, 192, 192)';
867	up.style.position = 'relative';
868	up.style.height = height + 'px';
869	up.style.width = '10px';
870	up.className = 'geBtnUp';
871	stepper.appendChild(up);
872
873	var down = up.cloneNode(false);
874	down.style.border = 'none';
875	down.style.height = height + 'px';
876	down.className = 'geBtnDown';
877	stepper.appendChild(down);
878
879	mxEvent.addGestureListeners(down, function(evt)
880	{
881		// Stops text selection on shift+click
882		mxEvent.consume(evt);
883	}, null, function(evt)
884	{
885		if (input.value == '')
886		{
887			input.value = defaultValue || '2';
888		}
889
890		var val = isFloat? parseFloat(input.value) : parseInt(input.value);
891
892		if (!isNaN(val))
893		{
894			input.value = val - (mxEvent.isShiftDown(evt) ? bigStep : step);
895
896			if (update != null)
897			{
898				update(evt);
899			}
900		}
901
902		mxEvent.consume(evt);
903	});
904
905	mxEvent.addGestureListeners(up, function(evt)
906	{
907		// Stops text selection on shift+click
908		mxEvent.consume(evt);
909	}, null, function(evt)
910	{
911		if (input.value == '')
912		{
913			input.value = defaultValue || '0';
914		}
915
916		var val = isFloat? parseFloat(input.value) : parseInt(input.value);
917
918		if (!isNaN(val))
919		{
920			input.value = val + (mxEvent.isShiftDown(evt) ? bigStep : step);
921
922			if (update != null)
923			{
924				update(evt);
925			}
926		}
927
928		mxEvent.consume(evt);
929	});
930
931	// Disables transfer of focus to DIV but also :active CSS
932	// so it's only used for fontSize where the focus should
933	// stay on the selected text, but not for any other input.
934	if (disableFocus)
935	{
936		var currentSelection = null;
937
938		mxEvent.addGestureListeners(stepper,
939			function(evt)
940			{
941				mxEvent.consume(evt);
942			},
943			null,
944			function(evt)
945			{
946				// Workaround for lost current selection in page because of focus in IE
947				if (currentSelection != null)
948				{
949					try
950					{
951						currentSelection.select();
952					}
953					catch (e)
954					{
955						// ignore
956					}
957
958					currentSelection = null;
959					mxEvent.consume(evt);
960				}
961			}
962		);
963	}
964	else
965	{
966		// Stops propagation on checkbox labels
967		mxEvent.addListener(stepper, 'click', function(evt)
968		{
969			mxEvent.consume(evt);
970		});
971	}
972
973	return stepper;
974};
975
976/**
977 * Adds the given option.
978 */
979BaseFormatPanel.prototype.createOption = function(label, isCheckedFn, setCheckedFn, listener, fn)
980{
981	var div = document.createElement('div');
982	div.style.padding = '3px 0px 3px 0px';
983	div.style.whiteSpace = 'nowrap';
984	div.style.textOverflow = 'ellipsis';
985	div.style.overflow = 'hidden';
986	div.style.width = '200px';
987	div.style.height = '18px';
988
989	var cb = document.createElement('input');
990	cb.setAttribute('type', 'checkbox');
991	cb.style.margin = '1px 6px 0px 0px';
992	cb.style.verticalAlign = 'top';
993	div.appendChild(cb);
994
995	var span = document.createElement('span');
996	span.style.verticalAlign = 'top';
997	mxUtils.write(span, label);
998	div.appendChild(span);
999
1000	var applying = false;
1001	var value = isCheckedFn();
1002
1003	var apply = function(newValue)
1004	{
1005		if (!applying)
1006		{
1007			applying = true;
1008
1009			if (newValue)
1010			{
1011				cb.setAttribute('checked', 'checked');
1012				cb.defaultChecked = true;
1013				cb.checked = true;
1014			}
1015			else
1016			{
1017				cb.removeAttribute('checked');
1018				cb.defaultChecked = false;
1019				cb.checked = false;
1020			}
1021
1022			if (value != newValue)
1023			{
1024				value = newValue;
1025
1026				// Checks if the color value needs to be updated in the model
1027				if (isCheckedFn() != value)
1028				{
1029					setCheckedFn(value);
1030				}
1031			}
1032
1033			applying = false;
1034		}
1035	};
1036
1037	mxEvent.addListener(div, 'click', function(evt)
1038	{
1039		if (cb.getAttribute('disabled') != 'disabled')
1040		{
1041			// Toggles checkbox state for click on label
1042			var source = mxEvent.getSource(evt);
1043
1044			if (source == div || source == span)
1045			{
1046				cb.checked = !cb.checked;
1047			}
1048
1049			apply(cb.checked);
1050		}
1051	});
1052
1053	apply(value);
1054
1055	if (listener != null)
1056	{
1057		listener.install(apply);
1058		this.listeners.push(listener);
1059	}
1060
1061	if (fn != null)
1062	{
1063		fn(div);
1064	}
1065
1066	return div;
1067};
1068
1069/**
1070 * The string 'null' means use null in values.
1071 */
1072BaseFormatPanel.prototype.createCellOption = function(label, key, defaultValue, enabledValue, disabledValue, fn, action, stopEditing, cells)
1073{
1074	var ui = this.editorUi;
1075	var editor = ui.editor;
1076	var graph = editor.graph;
1077	var self = this;
1078
1079	enabledValue = (enabledValue != null) ? ((enabledValue == 'null') ? null : enabledValue) : 1;
1080	disabledValue = (disabledValue != null) ? ((disabledValue == 'null') ? null : disabledValue) : 0;
1081
1082	var style = (cells != null) ? graph.getCommonStyle(cells) : this.format.getSelectionState().style;
1083
1084	return this.createOption(label, function()
1085	{
1086		return mxUtils.getValue(style, key, defaultValue) != disabledValue;
1087	}, function(checked)
1088	{
1089		if (stopEditing)
1090		{
1091			graph.stopEditing();
1092		}
1093
1094		if (action != null)
1095		{
1096			action.funct();
1097		}
1098		else
1099		{
1100			graph.getModel().beginUpdate();
1101			try
1102			{
1103				var temp = (cells != null) ? cells : self.format.getSelectionState().cells;
1104				var value = (checked) ? enabledValue : disabledValue;
1105				graph.setCellStyles(key, value, temp);
1106
1107				if (fn != null)
1108				{
1109					fn(temp, value);
1110				}
1111
1112				ui.fireEvent(new mxEventObject('styleChanged', 'keys',
1113					[key], 'values', [value], 'cells', temp));
1114			}
1115			finally
1116			{
1117				graph.getModel().endUpdate();
1118			}
1119		}
1120	},
1121	{
1122		install: function(apply)
1123		{
1124			this.listener = function()
1125			{
1126				apply(mxUtils.getValue(style, key, defaultValue) != disabledValue);
1127			};
1128
1129			graph.getModel().addListener(mxEvent.CHANGE, this.listener);
1130		},
1131		destroy: function()
1132		{
1133			graph.getModel().removeListener(this.listener);
1134		}
1135	});
1136};
1137
1138/**
1139 * Adds the given color option.
1140 */
1141BaseFormatPanel.prototype.createColorOption = function(label, getColorFn, setColorFn,
1142	defaultColor, listener, callbackFn, hideCheckbox, defaultColorValue)
1143{
1144	var div = document.createElement('div');
1145	div.style.padding = '3px 0px 3px 0px';
1146	div.style.whiteSpace = 'nowrap';
1147	div.style.overflow = 'hidden';
1148	div.style.width = '200px';
1149	div.style.height = '18px';
1150
1151	var cb = document.createElement('input');
1152	cb.setAttribute('type', 'checkbox');
1153	cb.style.margin = '1px 6px 0px 0px';
1154	cb.style.verticalAlign = 'top';
1155
1156	if (!hideCheckbox)
1157	{
1158		div.appendChild(cb);
1159	}
1160
1161	var span = document.createElement('span');
1162	span.style.verticalAlign = 'top';
1163	mxUtils.write(span, label);
1164	div.appendChild(span);
1165
1166	var title = 'Shift+Click for Color Dropper';
1167	var value = getColorFn();
1168	var applying = false;
1169	var btn = null;
1170
1171	var apply = function(color, disableUpdate, forceUpdate)
1172	{
1173		if (!applying)
1174		{
1175			var defaultValue = (defaultColor == 'null') ? null : defaultColor;
1176
1177			applying = true;
1178			color = (/(^#?[a-zA-Z0-9]*$)/.test(color)) ? color : defaultValue;
1179			var tempColor = (color != null && color != mxConstants.NONE) ? color : defaultValue;
1180
1181			var div = document.createElement('div');
1182			div.style.width = '36px';
1183			div.style.height = '12px';
1184			div.style.margin = '3px';
1185			div.style.border = '1px solid black';
1186			div.style.backgroundColor = (tempColor == 'default') ? defaultColorValue : tempColor;
1187
1188			btn.innerHTML = '';
1189			btn.appendChild(div);
1190
1191			if (color != null && color != mxConstants.NONE && color.length > 1 && typeof color === 'string')
1192			{
1193				var clr = (color.charAt(0) == '#') ? color.substring(1).toUpperCase() : color;
1194				var name = ColorDialog.prototype.colorNames[clr];
1195				btn.setAttribute('title', (name != null) ? name + ' (' + title + ')' : title);
1196			}
1197
1198			if (color != null && color != mxConstants.NONE)
1199			{
1200				cb.setAttribute('checked', 'checked');
1201				cb.defaultChecked = true;
1202				cb.checked = true;
1203			}
1204			else
1205			{
1206				cb.removeAttribute('checked');
1207				cb.defaultChecked = false;
1208				cb.checked = false;
1209			}
1210
1211			btn.style.display = (cb.checked || hideCheckbox) ? '' : 'none';
1212
1213			if (callbackFn != null)
1214			{
1215				callbackFn(color == 'null' ? null : color);
1216			}
1217
1218			if (!disableUpdate)
1219			{
1220				value = color;
1221
1222				// Checks if the color value needs to be updated in the model
1223				if (forceUpdate || hideCheckbox || getColorFn() != value)
1224				{
1225					setColorFn(value == 'null' ? null : value, value);
1226				}
1227			}
1228
1229			applying = false;
1230		}
1231	};
1232
1233	var clrInput = document.createElement('input');
1234	clrInput.setAttribute('type', 'color');
1235	clrInput.style.visibility = 'hidden';
1236	clrInput.style.width = '0px';
1237	clrInput.style.height = '0px';
1238	clrInput.style.border = 'none';
1239	div.appendChild(clrInput);
1240
1241	btn = mxUtils.button('', mxUtils.bind(this, function(evt)
1242	{
1243		var color = value;
1244
1245		if (color == 'default')
1246		{
1247			color = defaultColorValue;
1248		}
1249
1250		if (mxEvent.isShiftDown(evt) && !mxClient.IS_IE && !mxClient.IS_IE11)
1251		{
1252			clrInput.value = color;
1253			clrInput.click();
1254
1255			mxEvent.addListener(clrInput, 'input', function()
1256			{
1257				apply(clrInput.value, null, true);
1258			});
1259		}
1260		else
1261		{
1262			this.editorUi.pickColor(color, function(newColor)
1263			{
1264				apply(newColor, null, true);
1265			}, defaultColorValue);
1266		}
1267
1268		mxEvent.consume(evt);
1269	}));
1270
1271	btn.style.position = 'absolute';
1272	btn.style.marginTop = '-3px';
1273	btn.style.left = '178px';
1274	btn.style.height = '22px';
1275	btn.className = 'geColorBtn';
1276	btn.style.display = (cb.checked || hideCheckbox) ? '' : 'none';
1277	div.appendChild(btn);
1278
1279	var clr = (value != null && typeof value === 'string' && value.charAt(0) == '#') ? value.substring(1).toUpperCase() : value;
1280	var name = ColorDialog.prototype.colorNames[clr];
1281	btn.setAttribute('title', (name != null) ? name + ' (' + title + ')' : title);
1282
1283	mxEvent.addListener(div, 'click', function(evt)
1284	{
1285		var source = mxEvent.getSource(evt);
1286
1287		if (source == cb || source.nodeName != 'INPUT')
1288		{
1289			// Toggles checkbox state for click on label
1290			if (source != cb)
1291			{
1292				cb.checked = !cb.checked;
1293			}
1294
1295			// Overrides default value with current value to make it easier
1296			// to restore previous value if the checkbox is clicked twice
1297			if (!cb.checked && value != null && value != mxConstants.NONE &&
1298				defaultColor != mxConstants.NONE)
1299			{
1300				defaultColor = value;
1301			}
1302
1303			apply((cb.checked) ? defaultColor : mxConstants.NONE);
1304		}
1305	});
1306
1307	apply(value, true);
1308
1309	if (listener != null)
1310	{
1311		listener.install(apply);
1312		this.listeners.push(listener);
1313	}
1314
1315	return div;
1316};
1317
1318/**
1319 *
1320 */
1321BaseFormatPanel.prototype.createCellColorOption = function(label, colorKey, defaultColor, callbackFn, setStyleFn, defaultColorValue)
1322{
1323	var ui = this.editorUi;
1324	var editor = ui.editor;
1325	var graph = editor.graph;
1326	var self = this;
1327
1328	return this.createColorOption(label, function()
1329	{
1330		// Seems to be null sometimes, not sure why...
1331		var state = graph.view.getState(self.format.getSelectionState().cells[0]);
1332
1333		if (state != null)
1334		{
1335			return mxUtils.getValue(state.style, colorKey, null);
1336		}
1337
1338		return null;
1339	}, function(color, realValue)
1340	{
1341		graph.getModel().beginUpdate();
1342		try
1343		{
1344			var cells = self.format.getSelectionState().cells;
1345			graph.setCellStyles(colorKey, color, cells);
1346
1347			if (setStyleFn != null)
1348			{
1349				setStyleFn(color);
1350			}
1351
1352			ui.fireEvent(new mxEventObject('styleChanged', 'keys', [colorKey],
1353				'values', [color], 'cells', cells));
1354		}
1355		finally
1356		{
1357			graph.getModel().endUpdate();
1358		}
1359	}, defaultColor || mxConstants.NONE,
1360	{
1361		install: function(apply)
1362		{
1363			this.listener = function()
1364			{
1365				// Seems to be null sometimes, not sure why...
1366				var state = graph.view.getState(self.format.getSelectionState().cells[0]);
1367
1368				if (state != null)
1369				{
1370					apply(mxUtils.getValue(state.style, colorKey, null), true);
1371				}
1372			};
1373
1374			graph.getModel().addListener(mxEvent.CHANGE, this.listener);
1375		},
1376		destroy: function()
1377		{
1378			graph.getModel().removeListener(this.listener);
1379		}
1380	}, callbackFn, null, defaultColorValue);
1381};
1382
1383/**
1384 *
1385 */
1386BaseFormatPanel.prototype.addArrow = function(elt, height)
1387{
1388	height = (height != null) ? height : 10;
1389
1390	var arrow = document.createElement('div');
1391	arrow.style.display = 'inline-block';
1392	arrow.style.paddingRight = '4px';
1393	arrow.style.padding = '6px';
1394
1395	var m = (10 - height);
1396
1397	if (m == 2)
1398	{
1399		arrow.style.paddingTop = 6 + 'px';
1400	}
1401	else if (m > 0)
1402	{
1403		arrow.style.paddingTop = (6 - m) + 'px';
1404	}
1405	else
1406	{
1407		arrow.style.marginTop = '-2px';
1408	}
1409
1410	arrow.style.height = height + 'px';
1411	arrow.style.borderLeft = '1px solid #a0a0a0';
1412
1413	var img = document.createElement('img');
1414	img.setAttribute('border', '0');
1415	img.setAttribute('valign', 'middle');
1416	img.setAttribute('src', Toolbar.prototype.dropDownImage);
1417	arrow.appendChild(img);
1418
1419	var img = arrow.getElementsByTagName('img')[0];
1420	img.style.position = 'relative';
1421	img.style.left = '1px';
1422	img.style.top = (mxClient.IS_FF) ? '0px' : '-4px';
1423	mxUtils.setOpacity(arrow, 70);
1424
1425	var symbol = elt.getElementsByTagName('div')[0];
1426
1427	if (symbol != null)
1428	{
1429		symbol.style.paddingRight = '6px';
1430		symbol.style.marginLeft = '4px';
1431		symbol.style.marginTop = '-1px';
1432		symbol.style.display = 'inline-block';
1433		mxUtils.setOpacity(symbol, 60);
1434	}
1435
1436	mxUtils.setOpacity(elt, 100);
1437	elt.style.border = '1px solid #a0a0a0';
1438	elt.style.backgroundColor = this.buttonBackgroundColor;
1439	elt.style.backgroundImage = 'none';
1440	elt.style.width = 'auto';
1441	elt.className += ' geColorBtn';
1442	mxUtils.setPrefixedStyle(elt.style, 'borderRadius', '3px');
1443
1444	elt.appendChild(arrow);
1445
1446	return symbol;
1447};
1448
1449/**
1450 *
1451 */
1452BaseFormatPanel.prototype.addUnitInput = function(container, unit, right, width, update, step, marginTop, disableFocus, isFloat)
1453{
1454	marginTop = (marginTop != null) ? marginTop : 0;
1455
1456	var input = document.createElement('input');
1457	input.style.position = 'absolute';
1458	input.style.textAlign = 'right';
1459	input.style.marginTop = '-2px';
1460	input.style.left = (228 - right - width) + 'px';
1461	input.style.width = width + 'px';
1462	input.style.height = '21px';
1463	input.style.border = '1px solid rgb(160, 160, 160)';
1464	input.style.borderRadius = '4px';
1465	input.style.boxSizing = 'border-box';
1466
1467	container.appendChild(input);
1468
1469	var stepper = this.createStepper(input, update, step, null, disableFocus, null, isFloat);
1470	stepper.style.marginTop = (marginTop - 2) + 'px';
1471	stepper.style.left = (228 - right) + 'px';
1472	container.appendChild(stepper);
1473
1474	return input;
1475};
1476
1477/**
1478 *
1479 */
1480BaseFormatPanel.prototype.createRelativeOption = function(label, key, width, handler, init)
1481{
1482	width = (width != null) ? width : 52;
1483
1484	var graph = this.editorUi.editor.graph;
1485	var div = this.createPanel();
1486	div.style.paddingTop = '10px';
1487	div.style.paddingBottom = '10px';
1488	mxUtils.write(div, label);
1489	div.style.fontWeight = 'bold';
1490
1491	var update = mxUtils.bind(this, function(evt)
1492	{
1493		if (handler != null)
1494		{
1495			handler(input);
1496		}
1497		else
1498		{
1499			var value = parseInt(input.value);
1500			value = Math.min(100, Math.max(0, (isNaN(value)) ? 100 : value));
1501			var state = graph.view.getState(this.format.getSelectionState().cells[0]);
1502
1503			if (state != null && value != mxUtils.getValue(state.style, key, 100))
1504			{
1505				// Removes entry in style (assumes 100 is default for relative values)
1506				if (value == 100)
1507				{
1508					value = null;
1509				}
1510
1511				var cells = this.format.getSelectionState().cells;
1512				graph.setCellStyles(key, value, cells);
1513				this.editorUi.fireEvent(new mxEventObject('styleChanged', 'keys', [key],
1514					'values', [value], 'cells', cells));
1515			}
1516
1517			input.value = ((value != null) ? value : '100') + ' %';
1518		}
1519
1520		mxEvent.consume(evt);
1521	});
1522
1523	var input = this.addUnitInput(div, '%', 16, width, update, 10, -15, handler != null);
1524
1525	if (key != null)
1526	{
1527		var listener = mxUtils.bind(this, function(sender, evt, force)
1528		{
1529			if (force || input != document.activeElement)
1530			{
1531				var ss = this.format.getSelectionState();
1532				var tmp = parseInt(mxUtils.getValue(ss.style, key, 100));
1533				input.value = (isNaN(tmp)) ? '' : tmp + ' %';
1534			}
1535		});
1536
1537		mxEvent.addListener(input, 'keydown', function(e)
1538		{
1539			if (e.keyCode == 13)
1540			{
1541				graph.container.focus();
1542				mxEvent.consume(e);
1543			}
1544			else if (e.keyCode == 27)
1545			{
1546				listener(null, null, true);
1547				graph.container.focus();
1548				mxEvent.consume(e);
1549			}
1550		});
1551
1552		graph.getModel().addListener(mxEvent.CHANGE, listener);
1553		this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }});
1554		listener();
1555	}
1556
1557	mxEvent.addListener(input, 'blur', update);
1558	mxEvent.addListener(input, 'change', update);
1559
1560	if (init != null)
1561	{
1562		init(input);
1563	}
1564
1565	return div;
1566};
1567
1568/**
1569 *
1570 */
1571BaseFormatPanel.prototype.addLabel = function(div, title, right, width)
1572{
1573	width = (width != null) ? width : 61;
1574
1575	var label = document.createElement('div');
1576	mxUtils.write(label, title);
1577	label.style.position = 'absolute';
1578	label.style.left = (240 - right - width) + 'px';
1579	label.style.width = width + 'px';
1580	label.style.marginTop = '6px';
1581	label.style.textAlign = 'center';
1582	div.appendChild(label);
1583};
1584
1585/**
1586 *
1587 */
1588BaseFormatPanel.prototype.addKeyHandler = function(input, listener)
1589{
1590	mxEvent.addListener(input, 'keydown', mxUtils.bind(this, function(e)
1591	{
1592		if (e.keyCode == 13)
1593		{
1594			this.editorUi.editor.graph.container.focus();
1595			mxEvent.consume(e);
1596		}
1597		else if (e.keyCode == 27)
1598		{
1599			if (listener != null)
1600			{
1601				listener(null, null, true);
1602			}
1603
1604			this.editorUi.editor.graph.container.focus();
1605			mxEvent.consume(e);
1606		}
1607	}));
1608};
1609
1610/**
1611 *
1612 */
1613BaseFormatPanel.prototype.styleButtons = function(elts)
1614{
1615	for (var i = 0; i < elts.length; i++)
1616	{
1617		mxUtils.setPrefixedStyle(elts[i].style, 'borderRadius', '3px');
1618		mxUtils.setOpacity(elts[i], 100);
1619		elts[i].style.border = '1px solid #a0a0a0';
1620		elts[i].style.padding = '4px';
1621		elts[i].style.paddingTop = '3px';
1622		elts[i].style.paddingRight = '1px';
1623		elts[i].style.margin = '1px';
1624		elts[i].style.marginRight = '2px';
1625		elts[i].style.width = '24px';
1626		elts[i].style.height = '20px';
1627		elts[i].className += ' geColorBtn';
1628	}
1629};
1630
1631/**
1632 * Adds the label menu items to the given menu and parent.
1633 */
1634BaseFormatPanel.prototype.destroy = function()
1635{
1636	if (this.listeners != null)
1637	{
1638		for (var i = 0; i < this.listeners.length; i++)
1639		{
1640			this.listeners[i].destroy();
1641		}
1642
1643		this.listeners = null;
1644	}
1645};
1646
1647/**
1648 * Adds the label menu items to the given menu and parent.
1649 */
1650ArrangePanel = function(format, editorUi, container)
1651{
1652	BaseFormatPanel.call(this, format, editorUi, container);
1653	this.init();
1654};
1655
1656mxUtils.extend(ArrangePanel, BaseFormatPanel);
1657
1658/**
1659 * Adds the label menu items to the given menu and parent.
1660 */
1661ArrangePanel.prototype.init = function()
1662{
1663	var graph = this.editorUi.editor.graph;
1664	var ss = this.format.getSelectionState();
1665
1666	if (ss.cells.length > 0)
1667	{
1668		this.container.appendChild(this.addLayerOps(this.createPanel()));
1669		// Special case that adds two panels
1670		this.addGeometry(this.container);
1671		this.addEdgeGeometry(this.container);
1672
1673		if (!ss.containsLabel || ss.edges.length == 0)
1674		{
1675			this.container.appendChild(this.addAngle(this.createPanel()));
1676		}
1677
1678		if (!ss.containsLabel && ss.edges.length == 0 &&
1679			ss.style.shape != 'rectangle' &&
1680			ss.style.shape != 'label')
1681		{
1682			this.container.appendChild(this.addFlip(this.createPanel()));
1683		}
1684
1685		if (ss.vertices.length > 1)
1686		{
1687			this.container.appendChild(this.addAlign(this.createPanel()));
1688			this.container.appendChild(this.addDistribute(this.createPanel()));
1689		}
1690
1691		this.container.appendChild(this.addTable(this.createPanel()));
1692	}
1693
1694	this.container.appendChild(this.addGroupOps(this.createPanel()));
1695
1696	if (ss.containsLabel)
1697	{
1698		// Adds functions from hidden style format panel
1699		var span = document.createElement('div');
1700		span.style.width = '100%';
1701		span.style.marginTop = '0px';
1702		span.style.fontWeight = 'bold';
1703		span.style.padding = '10px 0 0 14px';
1704		mxUtils.write(span, mxResources.get('style'));
1705		this.container.appendChild(span);
1706
1707		new StyleFormatPanel(this.format, this.editorUi, this.container);
1708	}
1709};
1710
1711/**
1712 *
1713 */
1714ArrangePanel.prototype.addTable = function(div)
1715{
1716	var ui = this.editorUi;
1717	var editor = ui.editor;
1718	var graph = editor.graph;
1719	var ss = this.format.getSelectionState();
1720	div.style.paddingTop = '6px';
1721	div.style.paddingBottom = '10px';
1722
1723	var span = document.createElement('div');
1724	span.style.marginTop = '2px';
1725	span.style.marginBottom = '8px';
1726	span.style.fontWeight = 'bold';
1727	mxUtils.write(span, mxResources.get('table'));
1728	div.appendChild(span);
1729
1730	var panel = document.createElement('div');
1731	panel.style.position = 'relative';
1732	panel.style.paddingLeft = '0px';
1733	panel.style.borderWidth = '0px';
1734	panel.style.width = '220px';
1735	panel.className = 'geToolbarContainer';
1736
1737	var isTable = graph.isTable(ss.vertices[0]) ||
1738		graph.isTableRow(ss.vertices[0]) ||
1739		graph.isTableCell(ss.vertices[0]);
1740	var isStack = graph.isStack(ss.vertices[0]) ||
1741		graph.isStackChild(ss.vertices[0]);
1742
1743	var showCols = isTable;
1744	var showRows = isTable;
1745
1746	if (isStack)
1747	{
1748		var style = (graph.isStack(ss.vertices[0])) ? ss.style :
1749			graph.getCellStyle(graph.model.getParent(ss.vertices[0]));
1750
1751		showRows = style['horizontalStack'] == '0';
1752		showCols = !showRows;
1753	}
1754
1755	var btns = [];
1756
1757	if (showCols)
1758	{
1759		btns = btns.concat([
1760			ui.toolbar.addButton('geSprite-insertcolumnbefore', mxResources.get('insertColumnBefore'),
1761			mxUtils.bind(this, function()
1762			{
1763				try
1764				{
1765					if (isStack)
1766					{
1767						graph.insertLane(ss.vertices[0], true);
1768					}
1769					else
1770					{
1771						graph.insertTableColumn(ss.vertices[0], true);
1772					}
1773				}
1774				catch (e)
1775				{
1776					ui.handleError(e);
1777				}
1778			}), panel),
1779			ui.toolbar.addButton('geSprite-insertcolumnafter', mxResources.get('insertColumnAfter'),
1780			mxUtils.bind(this, function()
1781			{
1782				try
1783				{
1784					if (isStack)
1785					{
1786						graph.insertLane(ss.vertices[0], false);
1787					}
1788					else
1789					{
1790						graph.insertTableColumn(ss.vertices[0], false);
1791					}
1792				}
1793				catch (e)
1794				{
1795					ui.handleError(e);
1796				}
1797			}), panel),
1798			ui.toolbar.addButton('geSprite-deletecolumn', mxResources.get('deleteColumn'),
1799			mxUtils.bind(this, function()
1800			{
1801				try
1802				{
1803					if (isStack)
1804					{
1805						graph.deleteLane(ss.vertices[0]);
1806					}
1807					else
1808					{
1809						graph.deleteTableColumn(ss.vertices[0]);
1810					}
1811				}
1812				catch (e)
1813				{
1814					ui.handleError(e);
1815				}
1816			}), panel)]);
1817	}
1818
1819	if (showRows)
1820	{
1821		btns = btns.concat([ui.toolbar.addButton('geSprite-insertrowbefore', mxResources.get('insertRowBefore'),
1822			mxUtils.bind(this, function()
1823			{
1824				try
1825				{
1826					if (isStack)
1827					{
1828						graph.insertLane(ss.vertices[0], true);
1829					}
1830					else
1831					{
1832						graph.insertTableRow(ss.vertices[0], true);
1833					}
1834				}
1835				catch (e)
1836				{
1837					ui.handleError(e);
1838				}
1839			}), panel),
1840			ui.toolbar.addButton('geSprite-insertrowafter', mxResources.get('insertRowAfter'),
1841			mxUtils.bind(this, function()
1842			{
1843				try
1844				{
1845					if (isStack)
1846					{
1847						graph.insertLane(ss.vertices[0], false);
1848					}
1849					else
1850					{
1851						graph.insertTableRow(ss.vertices[0], false);
1852					}
1853				}
1854				catch (e)
1855				{
1856					ui.handleError(e);
1857				}
1858			}), panel),
1859			ui.toolbar.addButton('geSprite-deleterow', mxResources.get('deleteRow'),
1860			mxUtils.bind(this, function()
1861			{
1862				try
1863				{
1864					if (isStack)
1865					{
1866						graph.deleteLane(ss.vertices[0]);
1867					}
1868					else
1869					{
1870						graph.deleteTableRow(ss.vertices[0]);
1871					}
1872				}
1873				catch (e)
1874				{
1875					ui.handleError(e);
1876				}
1877			}), panel)]);
1878	}
1879
1880	if (btns.length > 0)
1881	{
1882		this.styleButtons(btns);
1883		div.appendChild(panel);
1884
1885		if (btns.length > 3)
1886		{
1887			btns[2].style.marginRight = '10px';
1888		}
1889	}
1890
1891	return div;
1892};
1893
1894/**
1895 *
1896 */
1897ArrangePanel.prototype.addLayerOps = function(div)
1898{
1899	var ui = this.editorUi;
1900	var graph = ui.editor.graph;
1901
1902	var btn = mxUtils.button(mxResources.get('toFront'), function(evt)
1903	{
1904		ui.actions.get('toFront').funct();
1905	})
1906
1907	btn.setAttribute('title', mxResources.get('toFront') + ' (' + this.editorUi.actions.get('toFront').shortcut + ')');
1908	btn.style.width = '104px';
1909	btn.style.marginRight = '2px';
1910	div.appendChild(btn);
1911
1912	var btn = mxUtils.button(mxResources.get('toBack'), function(evt)
1913	{
1914		ui.actions.get('toBack').funct();
1915	})
1916
1917	btn.setAttribute('title', mxResources.get('toBack') + ' (' + this.editorUi.actions.get('toBack').shortcut + ')');
1918	btn.style.width = '104px';
1919	div.appendChild(btn);
1920
1921	if (graph.getSelectionCount() == 1)
1922	{
1923		mxUtils.br(div);
1924
1925		var btn = mxUtils.button(mxResources.get('bringForward'), function(evt)
1926		{
1927			ui.actions.get('bringForward').funct();
1928		})
1929
1930		btn.setAttribute('title', mxResources.get('bringForward'));
1931		btn.style.width = '104px';
1932		btn.style.marginRight = '2px';
1933		btn.style.marginTop = '2px';
1934		div.appendChild(btn);
1935
1936		var btn = mxUtils.button(mxResources.get('sendBackward'), function(evt)
1937		{
1938			ui.actions.get('sendBackward').funct();
1939		})
1940
1941		btn.setAttribute('title', mxResources.get('sendBackward'));
1942		btn.style.width = '104px';
1943		btn.style.marginTop = '2px';
1944		div.appendChild(btn);
1945	}
1946
1947	return div;
1948};
1949
1950/**
1951 *
1952 */
1953ArrangePanel.prototype.addGroupOps = function(div)
1954{
1955	var ui = this.editorUi;
1956	var graph = ui.editor.graph;
1957	var ss = this.format.getSelectionState();
1958	var cell = ss.cells[0];
1959	var count = 0;
1960	var btn = null;
1961
1962	div.style.paddingTop = '8px';
1963	div.style.paddingBottom = '6px';
1964
1965	if (graph.getSelectionCount() > 1)
1966	{
1967		btn = mxUtils.button(mxResources.get('group'), function(evt)
1968		{
1969			ui.actions.get('group').funct();
1970		})
1971
1972		btn.setAttribute('title', mxResources.get('group') + ' (' + this.editorUi.actions.get('group').shortcut + ')');
1973		btn.style.width = '210px';
1974		btn.style.marginBottom = '2px';
1975		div.appendChild(btn);
1976		count++;
1977	}
1978	else if (ss.cells.length == 1 && !graph.getModel().isEdge(cell) && !graph.isSwimlane(cell) &&
1979		!graph.isTable(cell) && !ss.row && !ss.cell && graph.getModel().getChildCount(cell) > 0)
1980	{
1981		btn = mxUtils.button(mxResources.get('ungroup'), function(evt)
1982		{
1983			ui.actions.get('ungroup').funct();
1984		})
1985
1986		btn.setAttribute('title', mxResources.get('ungroup') + ' (' +
1987			this.editorUi.actions.get('ungroup').shortcut + ')');
1988		btn.style.width = '210px';
1989		btn.style.marginBottom = '2px';
1990		div.appendChild(btn);
1991		count++;
1992	}
1993
1994	if (graph.getModel().isVertex(graph.getSelectionCell()))
1995	{
1996		if (count > 0)
1997		{
1998			mxUtils.br(div);
1999			count = 0;
2000		}
2001
2002		var btn = mxUtils.button(mxResources.get('copySize'), function(evt)
2003		{
2004			ui.actions.get('copySize').funct(evt);
2005		});
2006
2007		btn.setAttribute('title', mxResources.get('copySize') + ' (' +
2008			this.editorUi.actions.get('copySize').shortcut + ')');
2009		btn.style.width = '210px';
2010		btn.style.marginBottom = '2px';
2011
2012		div.appendChild(btn);
2013		count++;
2014
2015		if (ui.copiedSize != null && ss.vertices.length > 0)
2016		{
2017			var btn2 = mxUtils.button(mxResources.get('pasteSize'), function(evt)
2018			{
2019				ui.actions.get('pasteSize').funct(evt);
2020			});
2021
2022			btn2.setAttribute('title', mxResources.get('pasteSize') + ' (' +
2023				this.editorUi.actions.get('pasteSize').shortcut + ')');
2024
2025			div.appendChild(btn2);
2026			count++;
2027
2028			btn.style.width = '104px';
2029			btn.style.marginBottom = '2px';
2030			btn2.style.width = '104px';
2031			btn2.style.marginBottom = '2px';
2032		}
2033	}
2034
2035	if (graph.getSelectionCount() > 0)
2036	{
2037		if (count > 0)
2038		{
2039			mxUtils.br(div);
2040			count = 0;
2041		}
2042
2043		var btn = mxUtils.button(mxResources.get('copyData'), function(evt)
2044		{
2045			if (mxEvent.isShiftDown(evt))
2046			{
2047				var result = graph.getDataForCells(graph.getSelectionCells());
2048
2049				var dlg = new EmbedDialog(ui, JSON.stringify(result, null, 2), null, null, function()
2050				{
2051					console.log(result);
2052					ui.alert('Written to Console (Dev Tools)');
2053				}, mxResources.get('copyData'), null, 'Console', 'data.json');
2054				ui.showDialog(dlg.container, 450, 240, true, true);
2055				dlg.init();
2056			}
2057			else
2058			{
2059				ui.actions.get('copyData').funct(evt);
2060			}
2061		});
2062
2063		btn.setAttribute('title', mxResources.get('copyData') + ' (' +
2064			this.editorUi.actions.get('copyData').shortcut + ')' +
2065			' Shift+Click to Extract Data');
2066		btn.style.width = '210px';
2067		btn.style.marginBottom = '2px';
2068
2069		div.appendChild(btn);
2070		count++;
2071
2072		if (ui.copiedValue != null && ss.cells.length > 0)
2073		{
2074			var btn2 = mxUtils.button(mxResources.get('pasteData'), function(evt)
2075			{
2076				ui.actions.get('pasteData').funct(evt);
2077			});
2078
2079			btn2.setAttribute('title', mxResources.get('pasteData') + ' (' +
2080				this.editorUi.actions.get('pasteData').shortcut + ')');
2081
2082			div.appendChild(btn2);
2083			count++;
2084
2085			btn.style.width = '104px';
2086			btn.style.marginBottom = '2px';
2087			btn2.style.width = '104px';
2088			btn2.style.marginBottom = '2px';
2089		}
2090	}
2091
2092	if (ss.cells.length == 1 && graph.getModel().isVertex(cell) && !ss.row &&
2093		!ss.cell && graph.getModel().isVertex(graph.getModel().getParent(cell)))
2094	{
2095		if (count > 0)
2096		{
2097			mxUtils.br(div);
2098		}
2099
2100		btn = mxUtils.button(mxResources.get('removeFromGroup'), function(evt)
2101		{
2102			ui.actions.get('removeFromGroup').funct();
2103		})
2104
2105		btn.setAttribute('title', mxResources.get('removeFromGroup'));
2106		btn.style.width = '210px';
2107		btn.style.marginBottom = '2px';
2108		div.appendChild(btn);
2109		count++;
2110	}
2111	else if (ss.cells.length > 0)
2112	{
2113		if (count > 0)
2114		{
2115			mxUtils.br(div);
2116		}
2117
2118		btn = mxUtils.button(mxResources.get('clearWaypoints'), mxUtils.bind(this, function(evt)
2119		{
2120			this.editorUi.actions.get('clearWaypoints').funct(evt);
2121		}));
2122
2123		btn.setAttribute('title', mxResources.get('clearWaypoints') + ' (' + this.editorUi.actions.get('clearWaypoints').shortcut + ')');
2124		btn.style.width = '210px';
2125		btn.style.marginBottom = '2px';
2126		div.appendChild(btn);
2127
2128		count++;
2129	}
2130
2131	if (ss.cells.length == 1)
2132	{
2133		if (count > 0)
2134		{
2135			mxUtils.br(div);
2136		}
2137
2138		btn = mxUtils.button(mxResources.get('editData'), mxUtils.bind(this, function(evt)
2139		{
2140			this.editorUi.actions.get('editData').funct();
2141		}));
2142
2143		btn.setAttribute('title', mxResources.get('editData') + ' (' + this.editorUi.actions.get('editData').shortcut + ')');
2144		btn.style.width = '104px';
2145		btn.style.marginBottom = '2px';
2146		div.appendChild(btn);
2147		count++;
2148
2149		btn = mxUtils.button(mxResources.get('editLink'), mxUtils.bind(this, function(evt)
2150		{
2151			this.editorUi.actions.get('editLink').funct();
2152		}));
2153
2154		btn.setAttribute('title', mxResources.get('editLink') + ' (' + this.editorUi.actions.get('editLink').shortcut + ')');
2155		btn.style.width = '104px';
2156		btn.style.marginLeft = '2px';
2157		btn.style.marginBottom = '2px';
2158		div.appendChild(btn);
2159		count++;
2160	}
2161
2162	if (count == 0)
2163	{
2164		div.style.display = 'none';
2165	}
2166
2167	return div;
2168};
2169
2170/**
2171 *
2172 */
2173ArrangePanel.prototype.addAlign = function(div)
2174{
2175	var graph = this.editorUi.editor.graph;
2176	div.style.paddingTop = '6px';
2177	div.style.paddingBottom = '12px';
2178	div.appendChild(this.createTitle(mxResources.get('align')));
2179
2180	var stylePanel = document.createElement('div');
2181	stylePanel.style.position = 'relative';
2182	stylePanel.style.whiteSpace = 'nowrap';
2183	stylePanel.style.paddingLeft = '0px';
2184	stylePanel.style.borderWidth = '0px';
2185	stylePanel.style.width = '220px';
2186	stylePanel.className = 'geToolbarContainer';
2187
2188	var left = this.editorUi.toolbar.addButton('geSprite-alignleft', mxResources.get('left'),
2189		function() { graph.alignCells(mxConstants.ALIGN_LEFT); }, stylePanel);
2190	var center = this.editorUi.toolbar.addButton('geSprite-aligncenter', mxResources.get('center'),
2191		function() { graph.alignCells(mxConstants.ALIGN_CENTER); }, stylePanel);
2192	var right = this.editorUi.toolbar.addButton('geSprite-alignright', mxResources.get('right'),
2193		function() { graph.alignCells(mxConstants.ALIGN_RIGHT); }, stylePanel);
2194
2195	var top = this.editorUi.toolbar.addButton('geSprite-aligntop', mxResources.get('top'),
2196		function() { graph.alignCells(mxConstants.ALIGN_TOP); }, stylePanel);
2197	var middle = this.editorUi.toolbar.addButton('geSprite-alignmiddle', mxResources.get('middle'),
2198		function() { graph.alignCells(mxConstants.ALIGN_MIDDLE); }, stylePanel);
2199	var bottom = this.editorUi.toolbar.addButton('geSprite-alignbottom', mxResources.get('bottom'),
2200		function() { graph.alignCells(mxConstants.ALIGN_BOTTOM); }, stylePanel);
2201
2202	this.styleButtons([left, center, right, top, middle, bottom]);
2203	right.style.marginRight = '10px';
2204	div.appendChild(stylePanel);
2205
2206	return div;
2207};
2208
2209/**
2210 *
2211 */
2212ArrangePanel.prototype.addFlip = function(div)
2213{
2214	var ui = this.editorUi;
2215	var editor = ui.editor;
2216	var graph = editor.graph;
2217	div.style.paddingTop = '6px';
2218	div.style.paddingBottom = '10px';
2219
2220	var span = document.createElement('div');
2221	span.style.marginTop = '2px';
2222	span.style.marginBottom = '8px';
2223	span.style.fontWeight = 'bold';
2224	mxUtils.write(span, mxResources.get('flip'));
2225	div.appendChild(span);
2226
2227	var btn = mxUtils.button(mxResources.get('horizontal'), function(evt)
2228	{
2229		graph.toggleCellStyles(mxConstants.STYLE_FLIPH, false);
2230	})
2231
2232	btn.setAttribute('title', mxResources.get('horizontal'));
2233	btn.style.width = '104px';
2234	btn.style.marginRight = '2px';
2235	div.appendChild(btn);
2236
2237	var btn = mxUtils.button(mxResources.get('vertical'), function(evt)
2238	{
2239		graph.toggleCellStyles(mxConstants.STYLE_FLIPV, false);
2240	})
2241
2242	btn.setAttribute('title', mxResources.get('vertical'));
2243	btn.style.width = '104px';
2244	div.appendChild(btn);
2245
2246	return div;
2247};
2248
2249/**
2250 *
2251 */
2252ArrangePanel.prototype.addDistribute = function(div)
2253{
2254	var ui = this.editorUi;
2255	var editor = ui.editor;
2256	var graph = editor.graph;
2257	div.style.paddingTop = '6px';
2258	div.style.paddingBottom = '12px';
2259
2260	div.appendChild(this.createTitle(mxResources.get('distribute')));
2261
2262	var btn = mxUtils.button(mxResources.get('horizontal'), function(evt)
2263	{
2264		graph.distributeCells(true);
2265	})
2266
2267	btn.setAttribute('title', mxResources.get('horizontal'));
2268	btn.style.width = '104px';
2269	btn.style.marginRight = '2px';
2270	div.appendChild(btn);
2271
2272	var btn = mxUtils.button(mxResources.get('vertical'), function(evt)
2273	{
2274		graph.distributeCells(false);
2275	})
2276
2277	btn.setAttribute('title', mxResources.get('vertical'));
2278	btn.style.width = '104px';
2279	div.appendChild(btn);
2280
2281	return div;
2282};
2283
2284/**
2285 *
2286 */
2287ArrangePanel.prototype.addAngle = function(div)
2288{
2289	var ui = this.editorUi;
2290	var editor = ui.editor;
2291	var graph = editor.graph;
2292	var ss = this.format.getSelectionState();
2293
2294	div.style.paddingBottom = '8px';
2295
2296	var span = document.createElement('div');
2297	span.style.position = 'absolute';
2298	span.style.width = '70px';
2299	span.style.marginTop = '0px';
2300	span.style.fontWeight = 'bold';
2301
2302	var input = null;
2303	var update = null;
2304	var btn = null;
2305
2306	if (ss.rotatable && !ss.table && !ss.row && !ss.cell)
2307	{
2308		mxUtils.write(span, mxResources.get('angle'));
2309		div.appendChild(span);
2310
2311		input = this.addUnitInput(div, '°', 16, 52, function()
2312		{
2313			update.apply(this, arguments);
2314		});
2315
2316		mxUtils.br(div);
2317		div.style.paddingTop = '10px';
2318	}
2319	else
2320	{
2321		div.style.paddingTop = '8px';
2322	}
2323
2324	if (!ss.containsLabel)
2325	{
2326		var label = mxResources.get('reverse');
2327
2328		if (ss.vertices.length > 0 && ss.edges.length > 0)
2329		{
2330			label = mxResources.get('turn') + ' / ' + label;
2331		}
2332		else if (ss.vertices.length > 0)
2333		{
2334			label = mxResources.get('turn');
2335		}
2336
2337		btn = mxUtils.button(label, function(evt)
2338		{
2339			ui.actions.get('turn').funct(evt);
2340		})
2341
2342		btn.setAttribute('title', label + ' (' + this.editorUi.actions.get('turn').shortcut + ')');
2343		btn.style.width = '210px';
2344		div.appendChild(btn);
2345
2346		if (input != null)
2347		{
2348			btn.style.marginTop = '8px';
2349		}
2350	}
2351
2352	if (input != null)
2353	{
2354		var listener = mxUtils.bind(this, function(sender, evt, force)
2355		{
2356			if (force || document.activeElement != input)
2357			{
2358				ss = this.format.getSelectionState();
2359				var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_ROTATION, 0));
2360				input.value = (isNaN(tmp)) ? '' : tmp  + '°';
2361			}
2362		});
2363
2364		update = this.installInputHandler(input, mxConstants.STYLE_ROTATION, 0, 0, 360, '°', null, true);
2365		this.addKeyHandler(input, listener);
2366
2367		graph.getModel().addListener(mxEvent.CHANGE, listener);
2368		this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }});
2369		listener();
2370	}
2371
2372	return div;
2373};
2374
2375BaseFormatPanel.prototype.getUnit = function()
2376{
2377	var unit = this.editorUi.editor.graph.view.unit;
2378
2379	switch(unit)
2380	{
2381		case mxConstants.POINTS:
2382			return 'pt';
2383		case mxConstants.INCHES:
2384			return '"';
2385		case mxConstants.MILLIMETERS:
2386			return 'mm';
2387		case mxConstants.METERS:
2388			return 'm';
2389	}
2390};
2391
2392BaseFormatPanel.prototype.inUnit = function(pixels)
2393{
2394	return this.editorUi.editor.graph.view.formatUnitText(pixels);
2395};
2396
2397BaseFormatPanel.prototype.fromUnit = function(value)
2398{
2399	var unit = this.editorUi.editor.graph.view.unit;
2400
2401	switch(unit)
2402	{
2403		case mxConstants.POINTS:
2404			return value;
2405		case mxConstants.INCHES:
2406			return value * mxConstants.PIXELS_PER_INCH;
2407		case mxConstants.MILLIMETERS:
2408			return value * mxConstants.PIXELS_PER_MM;
2409		case mxConstants.METERS:
2410			return value * mxConstants.PIXELS_PER_MM * 1000;
2411	}
2412};
2413
2414BaseFormatPanel.prototype.isFloatUnit = function()
2415{
2416	return this.editorUi.editor.graph.view.unit != mxConstants.POINTS;
2417};
2418
2419BaseFormatPanel.prototype.getUnitStep = function()
2420{
2421	var unit = this.editorUi.editor.graph.view.unit;
2422
2423	switch(unit)
2424	{
2425		case mxConstants.POINTS:
2426			return 1;
2427		case mxConstants.INCHES:
2428			return 0.1;
2429		case mxConstants.MILLIMETERS:
2430			return 0.5;
2431		case mxConstants.METERS:
2432			return 0.001;
2433	}
2434};
2435
2436/**
2437 *
2438 */
2439ArrangePanel.prototype.addGeometry = function(container)
2440{
2441	var panel = this;
2442	var ui = this.editorUi;
2443	var graph = ui.editor.graph;
2444	var model = graph.getModel();
2445	var rect = this.format.getSelectionState();
2446
2447	var div = this.createPanel();
2448	div.style.paddingBottom = '8px';
2449
2450	var span = document.createElement('div');
2451	span.style.position = 'absolute';
2452	span.style.width = '50px';
2453	span.style.marginTop = '0px';
2454	span.style.fontWeight = 'bold';
2455	mxUtils.write(span, mxResources.get('size'));
2456	div.appendChild(span);
2457
2458	var widthUpdate, heightUpdate, leftUpdate, topUpdate;
2459	var width = this.addUnitInput(div, this.getUnit(), 87, 52, function()
2460	{
2461		widthUpdate.apply(this, arguments);
2462	}, this.getUnitStep(), null, null, this.isFloatUnit());
2463	var height = this.addUnitInput(div, this.getUnit(), 16, 52, function()
2464	{
2465		heightUpdate.apply(this, arguments);
2466	}, this.getUnitStep(), null, null, this.isFloatUnit());
2467
2468	var autosizeBtn = document.createElement('div');
2469	autosizeBtn.className = 'geSprite geSprite-fit';
2470	autosizeBtn.setAttribute('title', mxResources.get('autosize') + ' (' + this.editorUi.actions.get('autosize').shortcut + ')');
2471	autosizeBtn.style.position = 'relative';
2472	autosizeBtn.style.cursor = 'pointer';
2473	autosizeBtn.style.marginTop = '-3px';
2474	autosizeBtn.style.border = '0px';
2475	autosizeBtn.style.left = '42px';
2476	mxUtils.setOpacity(autosizeBtn, 50);
2477
2478	mxEvent.addListener(autosizeBtn, 'mouseenter', function()
2479	{
2480		mxUtils.setOpacity(autosizeBtn, 100);
2481	});
2482
2483	mxEvent.addListener(autosizeBtn, 'mouseleave', function()
2484	{
2485		mxUtils.setOpacity(autosizeBtn, 50);
2486	});
2487
2488	mxEvent.addListener(autosizeBtn, 'click', function()
2489	{
2490		ui.actions.get('autosize').funct();
2491	});
2492
2493	div.appendChild(autosizeBtn);
2494
2495	if (rect.row)
2496	{
2497		width.style.visibility = 'hidden';
2498		width.nextSibling.style.visibility = 'hidden';
2499	}
2500	else
2501	{
2502		this.addLabel(div, mxResources.get('width'), 87);
2503	}
2504
2505	this.addLabel(div, mxResources.get('height'), 16);
2506	mxUtils.br(div);
2507
2508	var wrapper = document.createElement('div');
2509	wrapper.style.paddingTop = '8px';
2510	wrapper.style.paddingRight = '20px';
2511	wrapper.style.whiteSpace = 'nowrap';
2512	wrapper.style.textAlign = 'right';
2513	var opt = this.createCellOption(mxResources.get('constrainProportions'),
2514		mxConstants.STYLE_ASPECT, null, 'fixed', 'null');
2515	opt.style.width = '210px';
2516	wrapper.appendChild(opt);
2517
2518	if (!rect.cell && !rect.row)
2519	{
2520		div.appendChild(wrapper);
2521	}
2522	else
2523	{
2524		autosizeBtn.style.visibility = 'hidden';
2525	}
2526
2527	var constrainCheckbox = opt.getElementsByTagName('input')[0];
2528	this.addKeyHandler(width, listener);
2529	this.addKeyHandler(height, listener);
2530
2531	widthUpdate = this.addGeometryHandler(width, function(geo, value, cell)
2532	{
2533		if (graph.isTableCell(cell))
2534		{
2535			graph.setTableColumnWidth(cell, value - geo.width, true);
2536
2537			// Blocks processing in caller
2538			return true;
2539		}
2540		else if (geo.width > 0)
2541		{
2542			var value = Math.max(1, panel.fromUnit(value));
2543
2544			if (constrainCheckbox.checked)
2545			{
2546				geo.height = Math.round((geo.height * value * 100) / geo.width) / 100;
2547			}
2548
2549			geo.width = value;
2550		}
2551	});
2552	heightUpdate = this.addGeometryHandler(height, function(geo, value, cell)
2553	{
2554		if (graph.isTableCell(cell))
2555		{
2556			cell = graph.model.getParent(cell);
2557		}
2558
2559		if (graph.isTableRow(cell))
2560		{
2561			graph.setTableRowHeight(cell, value - geo.height);
2562
2563			// Blocks processing in caller
2564			return true;
2565		}
2566		else if (geo.height > 0)
2567		{
2568			var value = Math.max(1, panel.fromUnit(value));
2569
2570			if (constrainCheckbox.checked)
2571			{
2572				geo.width = Math.round((geo.width  * value * 100) / geo.height) / 100;
2573			}
2574
2575			geo.height = value;
2576		}
2577	});
2578
2579	if (rect.resizable || rect.row || rect.cell)
2580	{
2581		container.appendChild(div);
2582	}
2583
2584	var div2 = this.createPanel();
2585	div2.style.paddingBottom = '30px';
2586
2587	var span = document.createElement('div');
2588	span.style.position = 'absolute';
2589	span.style.width = '70px';
2590	span.style.marginTop = '0px';
2591	span.style.fontWeight = 'bold';
2592	mxUtils.write(span, mxResources.get('position'));
2593	div2.appendChild(span);
2594
2595	var left = this.addUnitInput(div2, this.getUnit(), 87, 52, function()
2596	{
2597		leftUpdate.apply(this, arguments);
2598	}, this.getUnitStep(), null, null, this.isFloatUnit());
2599	var top = this.addUnitInput(div2, this.getUnit(), 16, 52, function()
2600	{
2601		topUpdate.apply(this, arguments);
2602	}, this.getUnitStep(), null, null, this.isFloatUnit());
2603
2604	mxUtils.br(div2);
2605
2606	this.addLabel(div2, mxResources.get('left'), 87);
2607	this.addLabel(div2, mxResources.get('top'), 16);
2608
2609	var listener = mxUtils.bind(this, function(sender, evt, force)
2610	{
2611		rect = this.format.getSelectionState();
2612
2613		if (!rect.containsLabel && rect.vertices.length == graph.getSelectionCount() &&
2614			rect.width != null && rect.height != null)
2615		{
2616			div.style.display = '';
2617
2618			if (force || document.activeElement != width)
2619			{
2620				width.value = this.inUnit(rect.width) + ((rect.width == '') ? '' : ' ' + this.getUnit());
2621			}
2622
2623			if (force || document.activeElement != height)
2624			{
2625				height.value = this.inUnit(rect.height) + ((rect.height == '') ? '' : ' ' + this.getUnit());
2626			}
2627		}
2628		else
2629		{
2630			div.style.display = 'none';
2631		}
2632
2633		if (rect.vertices.length == graph.getSelectionCount() &&
2634			rect.x != null && rect.y != null)
2635		{
2636			div2.style.display = '';
2637
2638			if (force || document.activeElement != left)
2639			{
2640				left.value = this.inUnit(rect.x)  + ((rect.x == '') ? '' : ' ' + this.getUnit());
2641			}
2642
2643			if (force || document.activeElement != top)
2644			{
2645				top.value = this.inUnit(rect.y) + ((rect.y == '') ? '' : ' ' + this.getUnit());
2646			}
2647		}
2648		else
2649		{
2650			div2.style.display = 'none';
2651		}
2652	});
2653
2654	this.addKeyHandler(left, listener);
2655	this.addKeyHandler(top, listener);
2656
2657	model.addListener(mxEvent.CHANGE, listener);
2658	this.listeners.push({destroy: function() { model.removeListener(listener); }});
2659	listener();
2660
2661	leftUpdate = this.addGeometryHandler(left, function(geo, value)
2662	{
2663		value = panel.fromUnit(value);
2664
2665		if (geo.relative)
2666		{
2667			geo.offset.x = value;
2668		}
2669		else
2670		{
2671			geo.x = value;
2672		}
2673	});
2674	topUpdate = this.addGeometryHandler(top, function(geo, value)
2675	{
2676		value = panel.fromUnit(value);
2677
2678		if (geo.relative)
2679		{
2680			geo.offset.y = value;
2681		}
2682		else
2683		{
2684			geo.y = value;
2685		}
2686	});
2687
2688	if (rect.movable)
2689	{
2690		if (rect.edges.length == 0 && rect.vertices.length == 1 &&
2691			model.isEdge(model.getParent(rect.vertices[0])))
2692		{
2693			var geo = graph.getCellGeometry(rect.vertices[0]);
2694
2695			if (geo != null && geo.relative)
2696			{
2697				var btn = mxUtils.button(mxResources.get('center'), mxUtils.bind(this, function(evt)
2698				{
2699					model.beginUpdate();
2700					try
2701					{
2702						geo = geo.clone();
2703						geo.x = 0;
2704						geo.y = 0;
2705						geo.offset = new mxPoint();
2706						model.setGeometry(rect.vertices[0], geo);
2707					}
2708					finally
2709					{
2710						model.endUpdate();
2711					}
2712				}));
2713
2714				btn.setAttribute('title', mxResources.get('center'));
2715				btn.style.width = '210px';
2716				btn.style.position = 'absolute';
2717				mxUtils.br(div2);
2718				mxUtils.br(div2);
2719				div2.appendChild(btn);
2720			}
2721		}
2722		container.appendChild(div2);
2723	}
2724};
2725
2726/**
2727 *
2728 */
2729ArrangePanel.prototype.addGeometryHandler = function(input, fn)
2730{
2731	var ui = this.editorUi;
2732	var graph = ui.editor.graph;
2733	var initialValue = null;
2734	var panel = this;
2735
2736	function update(evt)
2737	{
2738		if (input.value != '')
2739		{
2740			var value = parseFloat(input.value);
2741
2742			if (isNaN(value))
2743			{
2744				input.value = initialValue + ' ' + panel.getUnit();
2745			}
2746			else if (value != initialValue)
2747			{
2748				graph.getModel().beginUpdate();
2749				try
2750				{
2751					var cells = panel.format.getSelectionState().cells;
2752
2753					for (var i = 0; i < cells.length; i++)
2754					{
2755						if (graph.getModel().isVertex(cells[i]))
2756						{
2757							var geo = graph.getCellGeometry(cells[i]);
2758
2759							if (geo != null)
2760							{
2761								geo = geo.clone();
2762
2763								if (!fn(geo, value, cells[i]))
2764								{
2765									var state = graph.view.getState(cells[i]);
2766
2767									if (state != null && graph.isRecursiveVertexResize(state))
2768									{
2769										graph.resizeChildCells(cells[i], geo);
2770									}
2771
2772									graph.getModel().setGeometry(cells[i], geo);
2773									graph.constrainChildCells(cells[i]);
2774								}
2775							}
2776						}
2777					}
2778				}
2779				finally
2780				{
2781					graph.getModel().endUpdate();
2782				}
2783
2784				initialValue = value;
2785				input.value = value + ' ' + panel.getUnit();
2786			}
2787		}
2788
2789		mxEvent.consume(evt);
2790	};
2791
2792	mxEvent.addListener(input, 'blur', update);
2793	mxEvent.addListener(input, 'change', update);
2794	mxEvent.addListener(input, 'focus', function()
2795	{
2796		initialValue = input.value;
2797	});
2798
2799	return update;
2800};
2801
2802ArrangePanel.prototype.addEdgeGeometryHandler = function(input, fn)
2803{
2804    var ui = this.editorUi;
2805    var graph = ui.editor.graph;
2806    var initialValue = null;
2807	var panel = this;
2808
2809    function update(evt)
2810    {
2811        if (input.value != '')
2812        {
2813            var value = parseFloat(input.value);
2814
2815            if (isNaN(value))
2816            {
2817                input.value = initialValue + ' pt';
2818            }
2819            else if (value != initialValue)
2820            {
2821                graph.getModel().beginUpdate();
2822                try
2823                {
2824                    var cells = panel.format.getSelectionState().cells;
2825
2826                    for (var i = 0; i < cells.length; i++)
2827                    {
2828                        if (graph.getModel().isEdge(cells[i]))
2829                        {
2830                            var geo = graph.getCellGeometry(cells[i]);
2831
2832                            if (geo != null)
2833                            {
2834                                geo = geo.clone();
2835                                fn(geo, value);
2836
2837                                graph.getModel().setGeometry(cells[i], geo);
2838                            }
2839                        }
2840                    }
2841                }
2842                finally
2843                {
2844                    graph.getModel().endUpdate();
2845                }
2846
2847                initialValue = value;
2848                input.value = value + ' pt';
2849            }
2850        }
2851
2852        mxEvent.consume(evt);
2853    };
2854
2855    mxEvent.addListener(input, 'blur', update);
2856    mxEvent.addListener(input, 'change', update);
2857    mxEvent.addListener(input, 'focus', function()
2858    {
2859        initialValue = input.value;
2860    });
2861
2862    return update;
2863};
2864
2865/**
2866 *
2867 */
2868ArrangePanel.prototype.addEdgeGeometry = function(container)
2869{
2870	var ui = this.editorUi;
2871	var graph = ui.editor.graph;
2872	var rect = this.format.getSelectionState();
2873	var div = this.createPanel();
2874
2875	var span = document.createElement('div');
2876	span.style.position = 'absolute';
2877	span.style.width = '70px';
2878	span.style.marginTop = '0px';
2879	span.style.fontWeight = 'bold';
2880	mxUtils.write(span, mxResources.get('width'));
2881	div.appendChild(span);
2882
2883	var widthUpdate, xtUpdate, ytUpdate, xsUpdate, ysUpdate;
2884	var width = this.addUnitInput(div, 'pt', 12, 44, function()
2885	{
2886		widthUpdate.apply(this, arguments);
2887	});
2888
2889	mxUtils.br(div);
2890	this.addKeyHandler(width, listener);
2891
2892	var widthUpdate = mxUtils.bind(this, function(evt)
2893	{
2894		// Maximum stroke width is 999
2895		var value = parseInt(width.value);
2896		value = Math.min(999, Math.max(1, (isNaN(value)) ? 1 : value));
2897
2898		if (value != mxUtils.getValue(rect.style, 'width', mxCellRenderer.defaultShapes['flexArrow'].prototype.defaultWidth))
2899		{
2900			var cells = this.format.getSelectionState().cells;
2901			graph.setCellStyles('width', value, cells);
2902			ui.fireEvent(new mxEventObject('styleChanged', 'keys', ['width'],
2903					'values', [value], 'cells', cells));
2904		}
2905
2906		width.value = value + ' pt';
2907		mxEvent.consume(evt);
2908	});
2909
2910	mxEvent.addListener(width, 'blur', widthUpdate);
2911	mxEvent.addListener(width, 'change', widthUpdate);
2912
2913	container.appendChild(div);
2914
2915	var divs = this.createPanel();
2916	divs.style.paddingBottom = '30px';
2917
2918	var span = document.createElement('div');
2919	span.style.position = 'absolute';
2920	span.style.width = '70px';
2921	span.style.marginTop = '0px';
2922	mxUtils.write(span, mxResources.get('linestart'));
2923	divs.appendChild(span);
2924
2925	var xs = this.addUnitInput(divs, 'pt', 87, 52, function()
2926	{
2927		xsUpdate.apply(this, arguments);
2928	});
2929	var ys = this.addUnitInput(divs, 'pt', 16, 52, function()
2930	{
2931		ysUpdate.apply(this, arguments);
2932	});
2933
2934	mxUtils.br(divs);
2935	this.addLabel(divs, mxResources.get('left'), 87);
2936	this.addLabel(divs, mxResources.get('top'), 16);
2937	container.appendChild(divs);
2938	this.addKeyHandler(xs, listener);
2939	this.addKeyHandler(ys, listener);
2940
2941	var divt = this.createPanel();
2942	divt.style.paddingBottom = '30px';
2943
2944	var span = document.createElement('div');
2945	span.style.position = 'absolute';
2946	span.style.width = '70px';
2947	span.style.marginTop = '0px';
2948	mxUtils.write(span, mxResources.get('lineend'));
2949	divt.appendChild(span);
2950
2951	var xt = this.addUnitInput(divt, 'pt', 87, 52, function()
2952	{
2953		xtUpdate.apply(this, arguments);
2954	});
2955	var yt = this.addUnitInput(divt, 'pt', 16, 52, function()
2956	{
2957		ytUpdate.apply(this, arguments);
2958	});
2959
2960	mxUtils.br(divt);
2961	this.addLabel(divt, mxResources.get('left'), 87);
2962	this.addLabel(divt, mxResources.get('top'), 16);
2963	container.appendChild(divt);
2964	this.addKeyHandler(xt, listener);
2965	this.addKeyHandler(yt, listener);
2966
2967	var listener = mxUtils.bind(this, function(sender, evt, force)
2968	{
2969		rect = this.format.getSelectionState();
2970		var cell = rect.cells[0];
2971
2972		if (rect.style.shape == 'link' || rect.style.shape == 'flexArrow')
2973		{
2974			div.style.display = '';
2975
2976			if (force || document.activeElement != width)
2977			{
2978				var value = mxUtils.getValue(rect.style, 'width',
2979					mxCellRenderer.defaultShapes['flexArrow'].prototype.defaultWidth);
2980				width.value = value + ' pt';
2981			}
2982		}
2983		else
2984		{
2985			div.style.display = 'none';
2986		}
2987
2988		if (rect.cells.length == 1 && graph.model.isEdge(cell))
2989		{
2990			var geo = graph.model.getGeometry(cell);
2991
2992			if (geo.sourcePoint != null && graph.model.getTerminal(cell, true) == null)
2993			{
2994				xs.value = geo.sourcePoint.x;
2995				ys.value = geo.sourcePoint.y;
2996			}
2997			else
2998			{
2999				divs.style.display = 'none';
3000			}
3001
3002			if (geo.targetPoint != null && graph.model.getTerminal(cell, false) == null)
3003			{
3004				xt.value = geo.targetPoint.x;
3005				yt.value = geo.targetPoint.y;
3006			}
3007			else
3008			{
3009				divt.style.display = 'none';
3010			}
3011		}
3012		else
3013		{
3014			divs.style.display = 'none';
3015			divt.style.display = 'none';
3016		}
3017	});
3018
3019	xsUpdate = this.addEdgeGeometryHandler(xs, function(geo, value)
3020	{
3021		geo.sourcePoint.x = value;
3022	});
3023
3024	ysUpdate = this.addEdgeGeometryHandler(ys, function(geo, value)
3025	{
3026		geo.sourcePoint.y = value;
3027	});
3028
3029	xtUpdate = this.addEdgeGeometryHandler(xt, function(geo, value)
3030	{
3031		geo.targetPoint.x = value;
3032	});
3033
3034	ytUpdate = this.addEdgeGeometryHandler(yt, function(geo, value)
3035	{
3036		geo.targetPoint.y = value;
3037	});
3038
3039	graph.getModel().addListener(mxEvent.CHANGE, listener);
3040	this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }});
3041	listener();
3042};
3043
3044/**
3045 * Adds the label menu items to the given menu and parent.
3046 */
3047TextFormatPanel = function(format, editorUi, container)
3048{
3049	BaseFormatPanel.call(this, format, editorUi, container);
3050	this.init();
3051};
3052
3053mxUtils.extend(TextFormatPanel, BaseFormatPanel);
3054
3055/**
3056 * Adds the label menu items to the given menu and parent.
3057 */
3058TextFormatPanel.prototype.init = function()
3059{
3060	this.container.style.borderBottom = 'none';
3061	this.addFont(this.container);
3062};
3063
3064/**
3065 * Adds the label menu items to the given menu and parent.
3066 */
3067TextFormatPanel.prototype.addFont = function(container)
3068{
3069	var ui = this.editorUi;
3070	var editor = ui.editor;
3071	var graph = editor.graph;
3072	var ss = this.format.getSelectionState();
3073
3074	var title = this.createTitle(mxResources.get('font'));
3075	title.style.paddingLeft = '14px';
3076	title.style.paddingTop = '10px';
3077	title.style.paddingBottom = '6px';
3078	container.appendChild(title);
3079
3080	var stylePanel = this.createPanel();
3081	stylePanel.style.paddingTop = '2px';
3082	stylePanel.style.paddingBottom = '2px';
3083	stylePanel.style.position = 'relative';
3084	stylePanel.style.marginLeft = '-2px';
3085	stylePanel.style.borderWidth = '0px';
3086	stylePanel.className = 'geToolbarContainer';
3087
3088	if (graph.cellEditor.isContentEditing())
3089	{
3090		var cssPanel = stylePanel.cloneNode();
3091
3092		var cssMenu = this.editorUi.toolbar.addMenu(mxResources.get('style'),
3093			mxResources.get('style'), true, 'formatBlock', cssPanel, null, true);
3094		cssMenu.style.color = 'rgb(112, 112, 112)';
3095		cssMenu.style.whiteSpace = 'nowrap';
3096		cssMenu.style.overflow = 'hidden';
3097		cssMenu.style.margin = '0px';
3098		this.addArrow(cssMenu);
3099		cssMenu.style.width = '200px';
3100		cssMenu.style.height = '15px';
3101
3102		var arrow = cssMenu.getElementsByTagName('div')[0];
3103		arrow.style.cssFloat = 'right';
3104		container.appendChild(cssPanel);
3105	}
3106
3107	container.appendChild(stylePanel);
3108
3109	var colorPanel = this.createPanel();
3110	colorPanel.style.marginTop = '8px';
3111	colorPanel.style.borderTop = '1px solid #c0c0c0';
3112	colorPanel.style.paddingTop = '6px';
3113	colorPanel.style.paddingBottom = '6px';
3114
3115	var fontMenu = this.editorUi.toolbar.addMenu('Helvetica', mxResources.get('fontFamily'),
3116		true, 'fontFamily', stylePanel, null, true);
3117	fontMenu.style.color = 'rgb(112, 112, 112)';
3118	fontMenu.style.whiteSpace = 'nowrap';
3119	fontMenu.style.overflow = 'hidden';
3120	fontMenu.style.margin = '0px';
3121
3122	this.addArrow(fontMenu);
3123	fontMenu.style.width = '200px';
3124	fontMenu.style.height = '15px';
3125
3126	var stylePanel2 = stylePanel.cloneNode(false);
3127	stylePanel2.style.marginLeft = '-3px';
3128	var fontStyleItems = this.editorUi.toolbar.addItems(['bold', 'italic', 'underline'], stylePanel2, true);
3129	fontStyleItems[0].setAttribute('title', mxResources.get('bold') + ' (' + this.editorUi.actions.get('bold').shortcut + ')');
3130	fontStyleItems[1].setAttribute('title', mxResources.get('italic') + ' (' + this.editorUi.actions.get('italic').shortcut + ')');
3131	fontStyleItems[2].setAttribute('title', mxResources.get('underline') + ' (' + this.editorUi.actions.get('underline').shortcut + ')');
3132
3133	var verticalItem = this.editorUi.toolbar.addItems(['vertical'], stylePanel2, true)[0];
3134
3135	container.appendChild(stylePanel2);
3136
3137	this.styleButtons(fontStyleItems);
3138	this.styleButtons([verticalItem]);
3139
3140	var stylePanel3 = stylePanel.cloneNode(false);
3141	stylePanel3.style.marginLeft = '-3px';
3142	stylePanel3.style.paddingBottom = '0px';
3143
3144	// Helper function to return a wrapper function does not pass any arguments
3145	var callFn = function(fn)
3146	{
3147		return function()
3148		{
3149			return fn();
3150		};
3151	};
3152
3153	var left = this.editorUi.toolbar.addButton('geSprite-left', mxResources.get('left'),
3154		(graph.cellEditor.isContentEditing()) ?
3155		function(evt)
3156		{
3157			graph.cellEditor.alignText(mxConstants.ALIGN_LEFT, evt);
3158			ui.fireEvent(new mxEventObject('styleChanged',
3159				'keys', [mxConstants.STYLE_ALIGN],
3160				'values', [mxConstants.ALIGN_LEFT],
3161				'cells', ss.cells));
3162		} : callFn(this.editorUi.menus.createStyleChangeFunction([mxConstants.STYLE_ALIGN], [mxConstants.ALIGN_LEFT])), stylePanel3);
3163	var center = this.editorUi.toolbar.addButton('geSprite-center', mxResources.get('center'),
3164		(graph.cellEditor.isContentEditing()) ?
3165		function(evt)
3166		{
3167			graph.cellEditor.alignText(mxConstants.ALIGN_CENTER, evt);
3168			ui.fireEvent(new mxEventObject('styleChanged',
3169				'keys', [mxConstants.STYLE_ALIGN],
3170				'values', [mxConstants.ALIGN_CENTER],
3171				'cells', ss.cells));
3172		} : callFn(this.editorUi.menus.createStyleChangeFunction([mxConstants.STYLE_ALIGN], [mxConstants.ALIGN_CENTER])), stylePanel3);
3173	var right = this.editorUi.toolbar.addButton('geSprite-right', mxResources.get('right'),
3174		(graph.cellEditor.isContentEditing()) ?
3175		function(evt)
3176		{
3177			graph.cellEditor.alignText(mxConstants.ALIGN_RIGHT, evt);
3178			ui.fireEvent(new mxEventObject('styleChanged',
3179				'keys', [mxConstants.STYLE_ALIGN],
3180				'values', [mxConstants.ALIGN_RIGHT],
3181				'cells', ss.cells));
3182		} : callFn(this.editorUi.menus.createStyleChangeFunction([mxConstants.STYLE_ALIGN], [mxConstants.ALIGN_RIGHT])), stylePanel3);
3183
3184	this.styleButtons([left, center, right]);
3185
3186	// Quick hack for strikethrough
3187	// TODO: Add translations and toggle state
3188	if (graph.cellEditor.isContentEditing())
3189	{
3190		var strike = this.editorUi.toolbar.addButton('geSprite-removeformat', mxResources.get('strikethrough'),
3191			function()
3192			{
3193				document.execCommand('strikeThrough', false, null);
3194			}, stylePanel2);
3195		this.styleButtons([strike]);
3196
3197		strike.firstChild.style.background = 'url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCI+PGRlZnM+PHBhdGggaWQ9ImEiIGQ9Ik0wIDBoMjR2MjRIMFYweiIvPjwvZGVmcz48Y2xpcFBhdGggaWQ9ImIiPjx1c2UgeGxpbms6aHJlZj0iI2EiIG92ZXJmbG93PSJ2aXNpYmxlIi8+PC9jbGlwUGF0aD48cGF0aCBjbGlwLXBhdGg9InVybCgjYikiIGZpbGw9IiMwMTAxMDEiIGQ9Ik03LjI0IDguNzVjLS4yNi0uNDgtLjM5LTEuMDMtLjM5LTEuNjcgMC0uNjEuMTMtMS4xNi40LTEuNjcuMjYtLjUuNjMtLjkzIDEuMTEtMS4yOS40OC0uMzUgMS4wNS0uNjMgMS43LS44My42Ni0uMTkgMS4zOS0uMjkgMi4xOC0uMjkuODEgMCAxLjU0LjExIDIuMjEuMzQuNjYuMjIgMS4yMy41NCAxLjY5Ljk0LjQ3LjQuODMuODggMS4wOCAxLjQzLjI1LjU1LjM4IDEuMTUuMzggMS44MWgtMy4wMWMwLS4zMS0uMDUtLjU5LS4xNS0uODUtLjA5LS4yNy0uMjQtLjQ5LS40NC0uNjgtLjItLjE5LS40NS0uMzMtLjc1LS40NC0uMy0uMS0uNjYtLjE2LTEuMDYtLjE2LS4zOSAwLS43NC4wNC0xLjAzLjEzLS4yOS4wOS0uNTMuMjEtLjcyLjM2LS4xOS4xNi0uMzQuMzQtLjQ0LjU1LS4xLjIxLS4xNS40My0uMTUuNjYgMCAuNDguMjUuODguNzQgMS4yMS4zOC4yNS43Ny40OCAxLjQxLjdINy4zOWMtLjA1LS4wOC0uMTEtLjE3LS4xNS0uMjV6TTIxIDEydi0ySDN2Mmg5LjYyYy4xOC4wNy40LjE0LjU1LjIuMzcuMTcuNjYuMzQuODcuNTEuMjEuMTcuMzUuMzYuNDMuNTcuMDcuMi4xMS40My4xMS42OSAwIC4yMy0uMDUuNDUtLjE0LjY2LS4wOS4yLS4yMy4zOC0uNDIuNTMtLjE5LjE1LS40Mi4yNi0uNzEuMzUtLjI5LjA4LS42My4xMy0xLjAxLjEzLS40MyAwLS44My0uMDQtMS4xOC0uMTNzLS42Ni0uMjMtLjkxLS40MmMtLjI1LS4xOS0uNDUtLjQ0LS41OS0uNzUtLjE0LS4zMS0uMjUtLjc2LS4yNS0xLjIxSDYuNGMwIC41NS4wOCAxLjEzLjI0IDEuNTguMTYuNDUuMzcuODUuNjUgMS4yMS4yOC4zNS42LjY2Ljk4LjkyLjM3LjI2Ljc4LjQ4IDEuMjIuNjUuNDQuMTcuOS4zIDEuMzguMzkuNDguMDguOTYuMTMgMS40NC4xMy44IDAgMS41My0uMDkgMi4xOC0uMjhzMS4yMS0uNDUgMS42Ny0uNzljLjQ2LS4zNC44Mi0uNzcgMS4wNy0xLjI3cy4zOC0xLjA3LjM4LTEuNzFjMC0uNi0uMS0xLjE0LS4zMS0xLjYxLS4wNS0uMTEtLjExLS4yMy0uMTctLjMzSDIxeiIvPjwvc3ZnPg==)';
3198		strike.firstChild.style.backgroundPosition = '2px 2px';
3199		strike.firstChild.style.backgroundSize = '18px 18px';
3200
3201		this.styleButtons([strike]);
3202	}
3203
3204	var top = this.editorUi.toolbar.addButton('geSprite-top', mxResources.get('top'),
3205		callFn(this.editorUi.menus.createStyleChangeFunction([mxConstants.STYLE_VERTICAL_ALIGN],
3206			[mxConstants.ALIGN_TOP])), stylePanel3);
3207	var middle = this.editorUi.toolbar.addButton('geSprite-middle', mxResources.get('middle'),
3208		callFn(this.editorUi.menus.createStyleChangeFunction([mxConstants.STYLE_VERTICAL_ALIGN],
3209			[mxConstants.ALIGN_MIDDLE])), stylePanel3);
3210	var bottom = this.editorUi.toolbar.addButton('geSprite-bottom', mxResources.get('bottom'),
3211		callFn(this.editorUi.menus.createStyleChangeFunction([mxConstants.STYLE_VERTICAL_ALIGN],
3212			[mxConstants.ALIGN_BOTTOM])), stylePanel3);
3213
3214	this.styleButtons([top, middle, bottom]);
3215
3216	container.appendChild(stylePanel3);
3217
3218	// Hack for updating UI state below based on current text selection
3219	// currentTable is the current selected DOM table updated below
3220	var sub, sup, full, tableWrapper, currentTable, tableCell, tableRow;
3221
3222	if (graph.cellEditor.isContentEditing())
3223	{
3224		top.style.display = 'none';
3225		middle.style.display = 'none';
3226		bottom.style.display = 'none';
3227		verticalItem.style.display = 'none';
3228
3229		full = this.editorUi.toolbar.addButton('geSprite-justifyfull', mxResources.get('block'),
3230			function()
3231			{
3232				if (full.style.opacity == 1)
3233				{
3234					document.execCommand('justifyfull', false, null);
3235				}
3236			}, stylePanel3);
3237		full.style.marginRight = '9px';
3238		full.style.opacity = 1;
3239
3240		this.styleButtons([full,
3241       		sub = this.editorUi.toolbar.addButton('geSprite-subscript',
3242       			mxResources.get('subscript') + ' (' + Editor.ctrlKey + '+,)',
3243			function()
3244			{
3245				document.execCommand('subscript', false, null);
3246			}, stylePanel3), sup = this.editorUi.toolbar.addButton('geSprite-superscript',
3247				mxResources.get('superscript') + ' (' + Editor.ctrlKey + '+.)',
3248			function()
3249			{
3250				document.execCommand('superscript', false, null);
3251			}, stylePanel3)]);
3252		sub.style.marginLeft = '10px';
3253
3254		var tmp = stylePanel3.cloneNode(false);
3255		tmp.style.paddingTop = '4px';
3256		var btns = [this.editorUi.toolbar.addButton('geSprite-orderedlist', mxResources.get('numberedList'),
3257				function()
3258				{
3259					document.execCommand('insertorderedlist', false, null);
3260				}, tmp),
3261			this.editorUi.toolbar.addButton('geSprite-unorderedlist', mxResources.get('bulletedList'),
3262				function()
3263				{
3264					document.execCommand('insertunorderedlist', false, null);
3265				}, tmp),
3266			this.editorUi.toolbar.addButton('geSprite-outdent', mxResources.get('decreaseIndent'),
3267				function()
3268				{
3269					document.execCommand('outdent', false, null);
3270				}, tmp),
3271			this.editorUi.toolbar.addButton('geSprite-indent', mxResources.get('increaseIndent'),
3272				function()
3273				{
3274					document.execCommand('indent', false, null);
3275				}, tmp),
3276			this.editorUi.toolbar.addButton('geSprite-removeformat', mxResources.get('removeFormat'),
3277				function()
3278				{
3279					document.execCommand('removeformat', false, null);
3280				}, tmp),
3281			this.editorUi.toolbar.addButton('geSprite-code', mxResources.get('html'),
3282				function()
3283				{
3284					graph.cellEditor.toggleViewMode();
3285				}, tmp)];
3286		this.styleButtons(btns);
3287		btns[btns.length - 2].style.marginLeft = '10px';
3288
3289		container.appendChild(tmp);
3290	}
3291	else
3292	{
3293		fontStyleItems[2].style.marginRight = '12px';
3294		right.style.marginRight = '12px';
3295	}
3296
3297	// Label position
3298	var stylePanel4 = stylePanel.cloneNode(false);
3299	stylePanel4.style.marginLeft = '0px';
3300	stylePanel4.style.paddingTop = '8px';
3301	stylePanel4.style.paddingBottom = '4px';
3302	stylePanel4.style.fontWeight = 'normal';
3303
3304	mxUtils.write(stylePanel4, mxResources.get('position'));
3305
3306	// Adds label position options
3307	var positionSelect = document.createElement('select');
3308	positionSelect.style.position = 'absolute';
3309	positionSelect.style.left = '126px';
3310	positionSelect.style.width = '98px';
3311	positionSelect.style.border = '1px solid rgb(160, 160, 160)';
3312	positionSelect.style.borderRadius = '4px';
3313	positionSelect.style.marginTop = '-2px';
3314
3315	var directions = ['topLeft', 'top', 'topRight', 'left', 'center', 'right', 'bottomLeft', 'bottom', 'bottomRight'];
3316	var lset = {'topLeft': [mxConstants.ALIGN_LEFT, mxConstants.ALIGN_TOP, mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_BOTTOM],
3317			'top': [mxConstants.ALIGN_CENTER, mxConstants.ALIGN_TOP, mxConstants.ALIGN_CENTER, mxConstants.ALIGN_BOTTOM],
3318			'topRight': [mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_TOP, mxConstants.ALIGN_LEFT, mxConstants.ALIGN_BOTTOM],
3319			'left': [mxConstants.ALIGN_LEFT, mxConstants.ALIGN_MIDDLE, mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_MIDDLE],
3320			'center': [mxConstants.ALIGN_CENTER, mxConstants.ALIGN_MIDDLE, mxConstants.ALIGN_CENTER, mxConstants.ALIGN_MIDDLE],
3321			'right': [mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_MIDDLE, mxConstants.ALIGN_LEFT, mxConstants.ALIGN_MIDDLE],
3322			'bottomLeft': [mxConstants.ALIGN_LEFT, mxConstants.ALIGN_BOTTOM, mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_TOP],
3323			'bottom': [mxConstants.ALIGN_CENTER, mxConstants.ALIGN_BOTTOM, mxConstants.ALIGN_CENTER, mxConstants.ALIGN_TOP],
3324			'bottomRight': [mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_BOTTOM, mxConstants.ALIGN_LEFT, mxConstants.ALIGN_TOP]};
3325
3326	for (var i = 0; i < directions.length; i++)
3327	{
3328		var positionOption = document.createElement('option');
3329		positionOption.setAttribute('value', directions[i]);
3330		mxUtils.write(positionOption, mxResources.get(directions[i]));
3331		positionSelect.appendChild(positionOption);
3332	}
3333
3334	stylePanel4.appendChild(positionSelect);
3335
3336	// Writing direction
3337	var stylePanel5 = stylePanel.cloneNode(false);
3338	stylePanel5.style.marginLeft = '0px';
3339	stylePanel5.style.paddingTop = '4px';
3340	stylePanel5.style.paddingBottom = '4px';
3341	stylePanel5.style.fontWeight = 'normal';
3342
3343	mxUtils.write(stylePanel5, mxResources.get('writingDirection'));
3344
3345	// Adds writing direction options
3346	// LATER: Handle reselect of same option in all selects (change event
3347	// is not fired for same option so have opened state on click) and
3348	// handle multiple different styles for current selection
3349	var dirSelect = document.createElement('select');
3350	dirSelect.style.position = 'absolute';
3351	dirSelect.style.border = '1px solid rgb(160, 160, 160)';
3352	dirSelect.style.left = '126px';
3353	dirSelect.style.width = '98px';
3354	dirSelect.style.borderRadius = '4px';
3355	dirSelect.style.marginTop = '-2px';
3356
3357	// NOTE: For automatic we use the value null since automatic
3358	// requires the text to be non formatted and non-wrapped
3359	var dirs = ['automatic', 'leftToRight', 'rightToLeft'];
3360	var dirSet = {'automatic': null,
3361			'leftToRight': mxConstants.TEXT_DIRECTION_LTR,
3362			'rightToLeft': mxConstants.TEXT_DIRECTION_RTL};
3363
3364	for (var i = 0; i < dirs.length; i++)
3365	{
3366		var dirOption = document.createElement('option');
3367		dirOption.setAttribute('value', dirs[i]);
3368		mxUtils.write(dirOption, mxResources.get(dirs[i]));
3369		dirSelect.appendChild(dirOption);
3370	}
3371
3372	stylePanel5.appendChild(dirSelect);
3373
3374	if (!graph.isEditing())
3375	{
3376		container.appendChild(stylePanel4);
3377
3378		mxEvent.addListener(positionSelect, 'change', function(evt)
3379		{
3380			graph.getModel().beginUpdate();
3381			try
3382			{
3383				var vals = lset[positionSelect.value];
3384
3385				if (vals != null)
3386				{
3387					graph.setCellStyles(mxConstants.STYLE_LABEL_POSITION, vals[0], ss.cells);
3388					graph.setCellStyles(mxConstants.STYLE_VERTICAL_LABEL_POSITION, vals[1], ss.cells);
3389					graph.setCellStyles(mxConstants.STYLE_ALIGN, vals[2], ss.cells);
3390					graph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN, vals[3], ss.cells);
3391				}
3392			}
3393			finally
3394			{
3395				graph.getModel().endUpdate();
3396			}
3397
3398			mxEvent.consume(evt);
3399		});
3400
3401		// LATER: Update dir in text editor while editing and update style with label
3402		// NOTE: The tricky part is handling and passing on the auto value
3403		container.appendChild(stylePanel5);
3404
3405		mxEvent.addListener(dirSelect, 'change', function(evt)
3406		{
3407			graph.setCellStyles(mxConstants.STYLE_TEXT_DIRECTION, dirSet[dirSelect.value], ss.cells);
3408			mxEvent.consume(evt);
3409		});
3410	}
3411
3412	// Fontsize
3413	var input = document.createElement('input');
3414	input.style.position = 'absolute';
3415	input.style.border = '1px solid rgb(160, 160, 160)';
3416	input.style.textAlign = 'right';
3417	input.style.marginTop = '4px';
3418	input.style.left = '161px';
3419	input.style.width = '53px';
3420	input.style.borderRadius = '4px';
3421	input.style.height = '23px';
3422	input.style.boxSizing = 'border-box';
3423	stylePanel2.appendChild(input);
3424
3425	// Workaround for font size 4 if no text is selected is update font size below
3426	// after first character was entered (as the font element is lazy created)
3427	var pendingFontSize = null;
3428
3429	var inputUpdate = this.installInputHandler(input, mxConstants.STYLE_FONTSIZE, Menus.prototype.defaultFontSize, 1, 999, ' pt',
3430	function(fontSize)
3431	{
3432		// IE does not support containsNode
3433		// KNOWN: Fixes font size issues but bypasses undo
3434		if (window.getSelection && !mxClient.IS_IE && !mxClient.IS_IE11)
3435		{
3436			var selection = window.getSelection();
3437			var container = (selection.rangeCount > 0) ? selection.getRangeAt(0).commonAncestorContainer :
3438				graph.cellEditor.textarea;
3439
3440			function updateSize(elt, ignoreContains)
3441			{
3442				if (graph.cellEditor.textarea != null && elt != graph.cellEditor.textarea &&
3443					graph.cellEditor.textarea.contains(elt) &&
3444					(ignoreContains || selection.containsNode(elt, true)))
3445				{
3446					if (elt.nodeName == 'FONT')
3447					{
3448						elt.removeAttribute('size');
3449						elt.style.fontSize = fontSize + 'px';
3450					}
3451					else
3452					{
3453						var css = mxUtils.getCurrentStyle(elt);
3454
3455						if (css.fontSize != fontSize + 'px')
3456						{
3457							if (mxUtils.getCurrentStyle(elt.parentNode).fontSize != fontSize + 'px')
3458							{
3459								elt.style.fontSize = fontSize + 'px';
3460							}
3461							else
3462							{
3463								elt.style.fontSize = '';
3464							}
3465						}
3466					}
3467				}
3468
3469				ui.fireEvent(new mxEventObject('styleChanged',
3470					'keys', [mxConstants.STYLE_FONTSIZE],
3471					'values', [fontSize], 'cells', ss.cells));
3472			};
3473
3474			// Wraps text node or mixed selection with leading text in a font element
3475			if (container == graph.cellEditor.textarea ||
3476				container.nodeType != mxConstants.NODETYPE_ELEMENT)
3477			{
3478				document.execCommand('fontSize', false, '1');
3479			}
3480
3481			if (container != graph.cellEditor.textarea)
3482			{
3483				container = container.parentNode;
3484			}
3485
3486			if (container != null && container.nodeType == mxConstants.NODETYPE_ELEMENT)
3487			{
3488				var elts = container.getElementsByTagName('*');
3489				updateSize(container);
3490
3491				for (var i = 0; i < elts.length; i++)
3492				{
3493					updateSize(elts[i]);
3494				}
3495			}
3496
3497			input.value = fontSize + ' pt';
3498		}
3499		else if (window.getSelection || document.selection)
3500		{
3501			// Checks selection
3502			var par = null;
3503
3504			if (document.selection)
3505			{
3506				par = document.selection.createRange().parentElement();
3507			}
3508			else
3509			{
3510				var selection = window.getSelection();
3511
3512				if (selection.rangeCount > 0)
3513				{
3514					par = selection.getRangeAt(0).commonAncestorContainer;
3515				}
3516			}
3517
3518			// Node.contains does not work for text nodes in IE11
3519			function isOrContains(container, node)
3520			{
3521			    while (node != null)
3522			    {
3523			        if (node === container)
3524			        {
3525			            return true;
3526			        }
3527
3528			        node = node.parentNode;
3529			    }
3530
3531			    return false;
3532			};
3533
3534			if (par != null && isOrContains(graph.cellEditor.textarea, par))
3535			{
3536				pendingFontSize = fontSize;
3537
3538				// Workaround for can't set font size in px is to change font size afterwards
3539				document.execCommand('fontSize', false, '4');
3540				var elts = graph.cellEditor.textarea.getElementsByTagName('font');
3541
3542				for (var i = 0; i < elts.length; i++)
3543				{
3544					if (elts[i].getAttribute('size') == '4')
3545					{
3546						elts[i].removeAttribute('size');
3547						elts[i].style.fontSize = pendingFontSize + 'px';
3548
3549						// Overrides fontSize in input with the one just assigned as a workaround
3550						// for potential fontSize values of parent elements that don't match
3551						window.setTimeout(function()
3552						{
3553							input.value = pendingFontSize + ' pt';
3554							pendingFontSize = null;
3555						}, 0);
3556
3557						break;
3558					}
3559				}
3560			}
3561		}
3562	}, true);
3563
3564	var stepper = this.createStepper(input, inputUpdate, 1, 10, true, Menus.prototype.defaultFontSize);
3565	stepper.style.display = input.style.display;
3566	stepper.style.marginTop = '4px';
3567	stepper.style.left = '214px';
3568
3569	stylePanel2.appendChild(stepper);
3570
3571	var arrow = fontMenu.getElementsByTagName('div')[0];
3572	arrow.style.cssFloat = 'right';
3573
3574	var bgColorApply = null;
3575	var currentBgColor = '#ffffff';
3576
3577	var fontColorApply = null;
3578	var currentFontColor = '#000000';
3579
3580	var bgPanel = (graph.cellEditor.isContentEditing()) ? this.createColorOption(mxResources.get('backgroundColor'), function()
3581	{
3582		return currentBgColor;
3583	}, function(color)
3584	{
3585		document.execCommand('backcolor', false, (color != mxConstants.NONE) ? color : 'transparent');
3586		ui.fireEvent(new mxEventObject('styleChanged',
3587			'keys', [mxConstants.STYLE_LABEL_BACKGROUNDCOLOR],
3588			'values', [color], 'cells', ss.cells));
3589	}, '#ffffff',
3590	{
3591		install: function(apply) { bgColorApply = apply; },
3592		destroy: function() { bgColorApply = null; }
3593	}, null, true) : this.createCellColorOption(mxResources.get('backgroundColor'),
3594		mxConstants.STYLE_LABEL_BACKGROUNDCOLOR, 'default', null, function(color)
3595	{
3596		graph.updateLabelElements(ss.cells, function(elt)
3597		{
3598			elt.style.backgroundColor = null;
3599		});
3600	}, graph.shapeBackgroundColor);
3601	bgPanel.style.fontWeight = 'bold';
3602
3603	var borderPanel = this.createCellColorOption(mxResources.get('borderColor'),
3604		mxConstants.STYLE_LABEL_BORDERCOLOR, 'default', null, null,
3605		graph.shapeForegroundColor);
3606	borderPanel.style.fontWeight = 'bold';
3607
3608	var defs = (ss.vertices.length >= 1) ? graph.stylesheet.getDefaultVertexStyle() : graph.stylesheet.getDefaultEdgeStyle();
3609	var panel = (graph.cellEditor.isContentEditing()) ? this.createColorOption(mxResources.get('fontColor'), function()
3610	{
3611		return currentFontColor;
3612	}, function(color)
3613	{
3614		if (mxClient.IS_FF)
3615		{
3616			// Workaround for Firefox that adds the font element around
3617			// anchor elements which ignore inherited colors is to move
3618			// the font element inside anchor elements
3619			var tmp = graph.cellEditor.textarea.getElementsByTagName('font');
3620			var oldFonts = [];
3621
3622			for (var i = 0; i < tmp.length; i++)
3623			{
3624				oldFonts.push(
3625				{
3626					node: tmp[i],
3627					color: tmp[i].getAttribute('color')
3628				});
3629			}
3630
3631			document.execCommand('forecolor', false, (color != mxConstants.NONE) ?
3632				color : 'transparent');
3633			ui.fireEvent(new mxEventObject('styleChanged',
3634				'keys', [mxConstants.STYLE_FONTCOLOR],
3635				'values', [color], 'cells', ss.cells));
3636
3637			// Finds the new or changed font element
3638			var newFonts = graph.cellEditor.textarea.getElementsByTagName('font');
3639
3640			for (var i = 0; i < newFonts.length; i++)
3641			{
3642				if (i >= oldFonts.length || newFonts[i] != oldFonts[i].node ||
3643					(newFonts[i] == oldFonts[i].node &&
3644						newFonts[i].getAttribute('color') != oldFonts[i].color))
3645				{
3646					var child = newFonts[i].firstChild;
3647
3648					// Moves the font element to inside the anchor element and adopts all children
3649					if (child != null && child.nodeName == 'A' && child.nextSibling ==
3650						null &&
3651						child.firstChild != null)
3652					{
3653						var parent = newFonts[i].parentNode;
3654						parent.insertBefore(child, newFonts[i]);
3655						var tmp = child.firstChild;
3656
3657						while (tmp != null)
3658						{
3659							var next = tmp.nextSibling;
3660							newFonts[i].appendChild(tmp);
3661							tmp = next;
3662						}
3663
3664						child.appendChild(newFonts[i]);
3665					}
3666
3667					break;
3668				}
3669			}
3670		}
3671		else
3672		{
3673			document.execCommand('forecolor', false, (color != mxConstants.NONE) ?
3674				color : 'transparent');
3675			ui.fireEvent(new mxEventObject('styleChanged',
3676				'keys', [mxConstants.STYLE_FONTCOLOR],
3677				'values', [color], 'cells', ss.cells));
3678		}
3679	}, (defs[mxConstants.STYLE_FONTCOLOR] != null) ? defs[mxConstants.STYLE_FONTCOLOR] : '#000000',
3680	{
3681		install: function(apply) { fontColorApply = apply; },
3682		destroy: function() { fontColorApply = null; }
3683	}, null, true) : this.createCellColorOption(mxResources.get('fontColor'),
3684		mxConstants.STYLE_FONTCOLOR, 'default', function(color)
3685	{
3686		if (color == mxConstants.NONE)
3687		{
3688			bgPanel.style.display = 'none';
3689		}
3690		else
3691		{
3692			bgPanel.style.display = '';
3693		}
3694
3695		borderPanel.style.display = bgPanel.style.display;
3696	}, function(color)
3697	{
3698		if (color == mxConstants.NONE)
3699		{
3700			graph.setCellStyles(mxConstants.STYLE_NOLABEL, '1', ss.cells);
3701		}
3702		else
3703		{
3704			graph.setCellStyles(mxConstants.STYLE_NOLABEL, null, ss.cells);
3705		}
3706
3707		graph.setCellStyles(mxConstants.STYLE_FONTCOLOR, color, ss.cells);
3708
3709		graph.updateLabelElements(ss.cells, function(elt)
3710		{
3711			elt.removeAttribute('color');
3712			elt.style.color = null;
3713		});
3714	}, graph.shapeForegroundColor);
3715	panel.style.fontWeight = 'bold';
3716
3717	colorPanel.appendChild(panel);
3718	colorPanel.appendChild(bgPanel);
3719
3720	if (!graph.cellEditor.isContentEditing())
3721	{
3722		colorPanel.appendChild(borderPanel);
3723	}
3724
3725	container.appendChild(colorPanel);
3726
3727	var extraPanel = this.createPanel();
3728	extraPanel.style.paddingTop = '2px';
3729	extraPanel.style.paddingBottom = '4px';
3730
3731	var wwCells = graph.filterSelectionCells(mxUtils.bind(this, function(cell)
3732	{
3733		var state = graph.view.getState(cell);
3734
3735		return state == null ||
3736			this.format.isAutoSizeState(state) ||
3737			graph.getModel().isEdge(cell) ||
3738			(!graph.isTableRow(cell) &&
3739			!graph.isTableCell(cell) &&
3740			!graph.isCellResizable(cell));
3741	}));
3742
3743	var wwOpt = this.createCellOption(mxResources.get('wordWrap'), mxConstants.STYLE_WHITE_SPACE,
3744		null, 'wrap', 'null', null, null, true, wwCells);
3745	wwOpt.style.fontWeight = 'bold';
3746
3747	// Word wrap in edge labels only supported via labelWidth style
3748	if (wwCells.length > 0)
3749	{
3750		extraPanel.appendChild(wwOpt);
3751	}
3752
3753	// Delegates switch of style to formattedText action as it also convertes newlines
3754	var htmlOpt = this.createCellOption(mxResources.get('formattedText'), 'html', 0,
3755		null, null, null, ui.actions.get('formattedText'));
3756	htmlOpt.style.fontWeight = 'bold';
3757	extraPanel.appendChild(htmlOpt);
3758
3759	var spacingPanel = this.createPanel();
3760	spacingPanel.style.paddingTop = '10px';
3761	spacingPanel.style.paddingBottom = '28px';
3762	spacingPanel.style.fontWeight = 'normal';
3763
3764	var span = document.createElement('div');
3765	span.style.position = 'absolute';
3766	span.style.width = '70px';
3767	span.style.marginTop = '0px';
3768	span.style.fontWeight = 'bold';
3769	mxUtils.write(span, mxResources.get('spacing'));
3770	spacingPanel.appendChild(span);
3771
3772	var topUpdate, globalUpdate, leftUpdate, bottomUpdate, rightUpdate;
3773	var topSpacing = this.addUnitInput(spacingPanel, 'pt', 87, 52, function()
3774	{
3775		topUpdate.apply(this, arguments);
3776	});
3777	var globalSpacing = this.addUnitInput(spacingPanel, 'pt', 16, 52, function()
3778	{
3779		globalUpdate.apply(this, arguments);
3780	});
3781
3782	mxUtils.br(spacingPanel);
3783	this.addLabel(spacingPanel, mxResources.get('top'), 87);
3784	this.addLabel(spacingPanel, mxResources.get('global'), 16);
3785	mxUtils.br(spacingPanel);
3786	mxUtils.br(spacingPanel);
3787
3788	var leftSpacing = this.addUnitInput(spacingPanel, 'pt', 158, 52, function()
3789	{
3790		leftUpdate.apply(this, arguments);
3791	});
3792	var bottomSpacing = this.addUnitInput(spacingPanel, 'pt', 87, 52, function()
3793	{
3794		bottomUpdate.apply(this, arguments);
3795	});
3796	var rightSpacing = this.addUnitInput(spacingPanel, 'pt', 16, 52, function()
3797	{
3798		rightUpdate.apply(this, arguments);
3799	});
3800
3801	mxUtils.br(spacingPanel);
3802	this.addLabel(spacingPanel, mxResources.get('left'), 158);
3803	this.addLabel(spacingPanel, mxResources.get('bottom'), 87);
3804	this.addLabel(spacingPanel, mxResources.get('right'), 16);
3805
3806	if (!graph.cellEditor.isContentEditing())
3807	{
3808		container.appendChild(extraPanel);
3809		container.appendChild(this.createRelativeOption(mxResources.get('opacity'), mxConstants.STYLE_TEXT_OPACITY));
3810		container.appendChild(spacingPanel);
3811	}
3812	else
3813	{
3814		var selState = null;
3815		var lineHeightInput = null;
3816
3817		container.appendChild(this.createRelativeOption(mxResources.get('lineheight'), null, null, function(input)
3818		{
3819			var value = (input.value == '') ? 120 : parseInt(input.value);
3820			value = Math.max(0, (isNaN(value)) ? 120 : value);
3821
3822			if (selState != null)
3823			{
3824				graph.cellEditor.restoreSelection(selState);
3825				selState = null;
3826			}
3827
3828			var selectedElement = graph.getSelectedElement();
3829			var node = selectedElement;
3830
3831			while (node != null && node.nodeType != mxConstants.NODETYPE_ELEMENT)
3832			{
3833				node = node.parentNode;
3834			}
3835
3836			if (node != null && node == graph.cellEditor.textarea && graph.cellEditor.textarea.firstChild != null)
3837			{
3838				if (graph.cellEditor.textarea.firstChild.nodeName != 'P')
3839				{
3840					graph.cellEditor.textarea.innerHTML = '<p>' + graph.cellEditor.textarea.innerHTML + '</p>';
3841				}
3842
3843				node = graph.cellEditor.textarea.firstChild;
3844			}
3845
3846			if (node != null && graph.cellEditor.textarea != null && node != graph.cellEditor.textarea &&
3847				graph.cellEditor.textarea.contains(node))
3848			{
3849				node.style.lineHeight = value / 100;
3850			}
3851
3852			input.value = value + ' %';
3853		}, function(input)
3854		{
3855			// Used in CSS handler to update current value
3856			lineHeightInput = input;
3857
3858			// KNOWN: Arrow up/down clear selection text in quirks/IE 8
3859			// Text size via arrow button limits to 16 in IE11. Why?
3860			mxEvent.addListener(input, 'mousedown', function()
3861			{
3862				if (document.activeElement == graph.cellEditor.textarea)
3863				{
3864					selState = graph.cellEditor.saveSelection();
3865				}
3866			});
3867
3868			mxEvent.addListener(input, 'touchstart', function()
3869			{
3870				if (document.activeElement == graph.cellEditor.textarea)
3871				{
3872					selState = graph.cellEditor.saveSelection();
3873				}
3874			});
3875
3876			input.value = '120 %';
3877		}));
3878
3879		var insertPanel = stylePanel.cloneNode(false);
3880		insertPanel.style.paddingLeft = '0px';
3881		var insertBtns = this.editorUi.toolbar.addItems(['link', 'image'], insertPanel, true);
3882
3883		var btns = [
3884		        this.editorUi.toolbar.addButton('geSprite-horizontalrule', mxResources.get('insertHorizontalRule'),
3885				function()
3886				{
3887					document.execCommand('inserthorizontalrule', false);
3888				}, insertPanel),
3889				this.editorUi.toolbar.addMenuFunctionInContainer(insertPanel, 'geSprite-table', mxResources.get('table'), false, mxUtils.bind(this, function(menu)
3890				{
3891					this.editorUi.menus.addInsertTableItem(menu, null, null, false);
3892				}))];
3893		this.styleButtons(insertBtns);
3894		this.styleButtons(btns);
3895
3896		var wrapper2 = this.createPanel();
3897		wrapper2.style.paddingTop = '10px';
3898		wrapper2.style.paddingBottom = '10px';
3899		wrapper2.appendChild(this.createTitle(mxResources.get('insert')));
3900		wrapper2.appendChild(insertPanel);
3901		container.appendChild(wrapper2);
3902
3903		var tablePanel = stylePanel.cloneNode(false);
3904		tablePanel.style.paddingLeft = '0px';
3905
3906		var btns = [
3907		        this.editorUi.toolbar.addButton('geSprite-insertcolumnbefore', mxResources.get('insertColumnBefore'),
3908	     		mxUtils.bind(this, function()
3909				{
3910					try
3911					{
3912				       	if (currentTable != null)
3913				       	{
3914				       		graph.insertColumn(currentTable, (tableCell != null) ? tableCell.cellIndex : 0);
3915				       	}
3916					}
3917					catch (e)
3918					{
3919						this.editorUi.handleError(e);
3920					}
3921				}), tablePanel),
3922				this.editorUi.toolbar.addButton('geSprite-insertcolumnafter', mxResources.get('insertColumnAfter'),
3923				mxUtils.bind(this, function()
3924				{
3925					try
3926					{
3927						if (currentTable != null)
3928				       	{
3929							graph.insertColumn(currentTable, (tableCell != null) ? tableCell.cellIndex + 1 : -1);
3930				       	}
3931					}
3932					catch (e)
3933					{
3934						this.editorUi.handleError(e);
3935					}
3936				}), tablePanel),
3937				this.editorUi.toolbar.addButton('geSprite-deletecolumn', mxResources.get('deleteColumn'),
3938				mxUtils.bind(this, function()
3939				{
3940					try
3941					{
3942						if (currentTable != null && tableCell != null)
3943						{
3944							graph.deleteColumn(currentTable, tableCell.cellIndex);
3945						}
3946					}
3947					catch (e)
3948					{
3949						this.editorUi.handleError(e);
3950					}
3951				}), tablePanel),
3952				this.editorUi.toolbar.addButton('geSprite-insertrowbefore', mxResources.get('insertRowBefore'),
3953				mxUtils.bind(this, function()
3954				{
3955					try
3956					{
3957						if (currentTable != null && tableRow != null)
3958						{
3959							graph.insertRow(currentTable, tableRow.sectionRowIndex);
3960						}
3961					}
3962					catch (e)
3963					{
3964						this.editorUi.handleError(e);
3965					}
3966				}), tablePanel),
3967				this.editorUi.toolbar.addButton('geSprite-insertrowafter', mxResources.get('insertRowAfter'),
3968				mxUtils.bind(this, function()
3969				{
3970					try
3971					{
3972						if (currentTable != null && tableRow != null)
3973						{
3974							graph.insertRow(currentTable, tableRow.sectionRowIndex + 1);
3975						}
3976					}
3977					catch (e)
3978					{
3979						this.editorUi.handleError(e);
3980					}
3981				}), tablePanel),
3982				this.editorUi.toolbar.addButton('geSprite-deleterow', mxResources.get('deleteRow'),
3983				mxUtils.bind(this, function()
3984				{
3985					try
3986					{
3987						if (currentTable != null && tableRow != null)
3988						{
3989							graph.deleteRow(currentTable, tableRow.sectionRowIndex);
3990						}
3991					}
3992					catch (e)
3993					{
3994						this.editorUi.handleError(e);
3995					}
3996				}), tablePanel)];
3997		this.styleButtons(btns);
3998		btns[2].style.marginRight = '10px';
3999
4000		var wrapper3 = this.createPanel();
4001		wrapper3.style.paddingTop = '10px';
4002		wrapper3.style.paddingBottom = '10px';
4003		wrapper3.appendChild(this.createTitle(mxResources.get('table')));
4004		wrapper3.appendChild(tablePanel);
4005
4006		var tablePanel2 = stylePanel.cloneNode(false);
4007		tablePanel2.style.paddingLeft = '0px';
4008
4009		var btns = [
4010		        this.editorUi.toolbar.addButton('geSprite-strokecolor', mxResources.get('borderColor'),
4011				mxUtils.bind(this, function(evt)
4012				{
4013					if (currentTable != null)
4014					{
4015						// Converts rgb(r,g,b) values
4016						var color = currentTable.style.borderColor.replace(
4017							    /\brgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g,
4018							    function($0, $1, $2, $3) {
4019							        return "#" + ("0"+Number($1).toString(16)).substr(-2) + ("0"+Number($2).toString(16)).substr(-2) + ("0"+Number($3).toString(16)).substr(-2);
4020							    });
4021						this.editorUi.pickColor(color, function(newColor)
4022						{
4023							var targetElt = (tableCell != null && (evt == null || !mxEvent.isShiftDown(evt))) ? tableCell : currentTable;
4024
4025							graph.processElements(targetElt, function(elt)
4026							{
4027								elt.style.border = null;
4028							});
4029
4030							if (newColor == null || newColor == mxConstants.NONE)
4031							{
4032								targetElt.removeAttribute('border');
4033								targetElt.style.border = '';
4034								targetElt.style.borderCollapse = '';
4035							}
4036							else
4037							{
4038								targetElt.setAttribute('border', '1');
4039								targetElt.style.border = '1px solid ' + newColor;
4040								targetElt.style.borderCollapse = 'collapse';
4041							}
4042						});
4043					}
4044				}), tablePanel2),
4045				this.editorUi.toolbar.addButton('geSprite-fillcolor', mxResources.get('backgroundColor'),
4046				mxUtils.bind(this, function(evt)
4047				{
4048					// Converts rgb(r,g,b) values
4049					if (currentTable != null)
4050					{
4051						var color = currentTable.style.backgroundColor.replace(
4052							    /\brgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g,
4053							    function($0, $1, $2, $3) {
4054							        return "#" + ("0"+Number($1).toString(16)).substr(-2) + ("0"+Number($2).toString(16)).substr(-2) + ("0"+Number($3).toString(16)).substr(-2);
4055							    });
4056						this.editorUi.pickColor(color, function(newColor)
4057						{
4058							var targetElt = (tableCell != null && (evt == null || !mxEvent.isShiftDown(evt))) ? tableCell : currentTable;
4059
4060							graph.processElements(targetElt, function(elt)
4061							{
4062								elt.style.backgroundColor = null;
4063							});
4064
4065							if (newColor == null || newColor == mxConstants.NONE)
4066							{
4067								targetElt.style.backgroundColor = '';
4068							}
4069							else
4070							{
4071								targetElt.style.backgroundColor = newColor;
4072							}
4073						});
4074					}
4075				}), tablePanel2),
4076				this.editorUi.toolbar.addButton('geSprite-fit', mxResources.get('spacing'),
4077				function()
4078				{
4079					if (currentTable != null)
4080					{
4081						var value = currentTable.getAttribute('cellPadding') || 0;
4082
4083						var dlg = new FilenameDialog(ui, value, mxResources.get('apply'), mxUtils.bind(this, function(newValue)
4084						{
4085							if (newValue != null && newValue.length > 0)
4086							{
4087								currentTable.setAttribute('cellPadding', newValue);
4088							}
4089							else
4090							{
4091								currentTable.removeAttribute('cellPadding');
4092							}
4093						}), mxResources.get('spacing'));
4094						ui.showDialog(dlg.container, 300, 80, true, true);
4095						dlg.init();
4096					}
4097				}, tablePanel2),
4098				this.editorUi.toolbar.addButton('geSprite-left', mxResources.get('left'),
4099				function()
4100				{
4101					if (currentTable != null)
4102					{
4103						currentTable.setAttribute('align', 'left');
4104					}
4105				}, tablePanel2),
4106				this.editorUi.toolbar.addButton('geSprite-center', mxResources.get('center'),
4107				function()
4108				{
4109					if (currentTable != null)
4110					{
4111						currentTable.setAttribute('align', 'center');
4112					}
4113				}, tablePanel2),
4114				this.editorUi.toolbar.addButton('geSprite-right', mxResources.get('right'),
4115				function()
4116				{
4117					if (currentTable != null)
4118					{
4119						currentTable.setAttribute('align', 'right');
4120					}
4121				}, tablePanel2)];
4122		this.styleButtons(btns);
4123		btns[2].style.marginRight = '10px';
4124
4125		wrapper3.appendChild(tablePanel2);
4126		container.appendChild(wrapper3);
4127
4128		tableWrapper = wrapper3;
4129	}
4130
4131	function setSelected(elt, selected)
4132	{
4133		elt.style.backgroundImage = (selected) ? (Editor.isDarkMode() ?
4134			'linear-gradient(rgb(0 161 241) 0px, rgb(0, 97, 146) 100%)':
4135			'linear-gradient(#c5ecff 0px,#87d4fb 100%)') : '';
4136	};
4137
4138	var listener = mxUtils.bind(this, function(sender, evt, force)
4139	{
4140		ss = this.format.getSelectionState();
4141		var fontStyle = mxUtils.getValue(ss.style, mxConstants.STYLE_FONTSTYLE, 0);
4142		setSelected(fontStyleItems[0], (fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD);
4143		setSelected(fontStyleItems[1], (fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC);
4144		setSelected(fontStyleItems[2], (fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE);
4145		fontMenu.firstChild.nodeValue = mxUtils.getValue(ss.style, mxConstants.STYLE_FONTFAMILY, Menus.prototype.defaultFont);
4146
4147		setSelected(verticalItem, mxUtils.getValue(ss.style, mxConstants.STYLE_HORIZONTAL, '1') == '0');
4148
4149		if (force || document.activeElement != input)
4150		{
4151			var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_FONTSIZE, Menus.prototype.defaultFontSize));
4152			input.value = (isNaN(tmp)) ? '' : tmp  + ' pt';
4153		}
4154
4155		var align = mxUtils.getValue(ss.style, mxConstants.STYLE_ALIGN, mxConstants.ALIGN_CENTER);
4156		setSelected(left, align == mxConstants.ALIGN_LEFT);
4157		setSelected(center, align == mxConstants.ALIGN_CENTER);
4158		setSelected(right, align == mxConstants.ALIGN_RIGHT);
4159
4160		var valign = mxUtils.getValue(ss.style, mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_MIDDLE);
4161		setSelected(top, valign == mxConstants.ALIGN_TOP);
4162		setSelected(middle, valign == mxConstants.ALIGN_MIDDLE);
4163		setSelected(bottom, valign == mxConstants.ALIGN_BOTTOM);
4164
4165		var pos = mxUtils.getValue(ss.style, mxConstants.STYLE_LABEL_POSITION, mxConstants.ALIGN_CENTER);
4166		var vpos = mxUtils.getValue(ss.style, mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.ALIGN_MIDDLE);
4167
4168		if (pos == mxConstants.ALIGN_LEFT && vpos == mxConstants.ALIGN_TOP)
4169		{
4170			positionSelect.value = 'topLeft';
4171		}
4172		else if (pos == mxConstants.ALIGN_CENTER && vpos == mxConstants.ALIGN_TOP)
4173		{
4174			positionSelect.value = 'top';
4175		}
4176		else if (pos == mxConstants.ALIGN_RIGHT && vpos == mxConstants.ALIGN_TOP)
4177		{
4178			positionSelect.value = 'topRight';
4179		}
4180		else if (pos == mxConstants.ALIGN_LEFT && vpos == mxConstants.ALIGN_BOTTOM)
4181		{
4182			positionSelect.value = 'bottomLeft';
4183		}
4184		else if (pos == mxConstants.ALIGN_CENTER && vpos == mxConstants.ALIGN_BOTTOM)
4185		{
4186			positionSelect.value = 'bottom';
4187		}
4188		else if (pos == mxConstants.ALIGN_RIGHT && vpos == mxConstants.ALIGN_BOTTOM)
4189		{
4190			positionSelect.value = 'bottomRight';
4191		}
4192		else if (pos == mxConstants.ALIGN_LEFT)
4193		{
4194			positionSelect.value = 'left';
4195		}
4196		else if (pos == mxConstants.ALIGN_RIGHT)
4197		{
4198			positionSelect.value = 'right';
4199		}
4200		else
4201		{
4202			positionSelect.value = 'center';
4203		}
4204
4205		var dir = mxUtils.getValue(ss.style, mxConstants.STYLE_TEXT_DIRECTION, mxConstants.DEFAULT_TEXT_DIRECTION);
4206
4207		if (dir == mxConstants.TEXT_DIRECTION_RTL)
4208		{
4209			dirSelect.value = 'rightToLeft';
4210		}
4211		else if (dir == mxConstants.TEXT_DIRECTION_LTR)
4212		{
4213			dirSelect.value = 'leftToRight';
4214		}
4215		else if (dir == mxConstants.TEXT_DIRECTION_AUTO)
4216		{
4217			dirSelect.value = 'automatic';
4218		}
4219
4220		if (force || document.activeElement != globalSpacing)
4221		{
4222			var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_SPACING, 2));
4223			globalSpacing.value = (isNaN(tmp)) ? '' : tmp  + ' pt';
4224		}
4225
4226		if (force || document.activeElement != topSpacing)
4227		{
4228			var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_SPACING_TOP, 0));
4229			topSpacing.value = (isNaN(tmp)) ? '' : tmp  + ' pt';
4230		}
4231
4232		if (force || document.activeElement != rightSpacing)
4233		{
4234			var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_SPACING_RIGHT, 0));
4235			rightSpacing.value = (isNaN(tmp)) ? '' : tmp  + ' pt';
4236		}
4237
4238		if (force || document.activeElement != bottomSpacing)
4239		{
4240			var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_SPACING_BOTTOM, 0));
4241			bottomSpacing.value = (isNaN(tmp)) ? '' : tmp  + ' pt';
4242		}
4243
4244		if (force || document.activeElement != leftSpacing)
4245		{
4246			var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_SPACING_LEFT, 0));
4247			leftSpacing.value = (isNaN(tmp)) ? '' : tmp  + ' pt';
4248		}
4249	});
4250
4251	globalUpdate = this.installInputHandler(globalSpacing, mxConstants.STYLE_SPACING, 2, -999, 999, ' pt');
4252	topUpdate = this.installInputHandler(topSpacing, mxConstants.STYLE_SPACING_TOP, 0, -999, 999, ' pt');
4253	rightUpdate = this.installInputHandler(rightSpacing, mxConstants.STYLE_SPACING_RIGHT, 0, -999, 999, ' pt');
4254	bottomUpdate = this.installInputHandler(bottomSpacing, mxConstants.STYLE_SPACING_BOTTOM, 0, -999, 999, ' pt');
4255	leftUpdate = this.installInputHandler(leftSpacing, mxConstants.STYLE_SPACING_LEFT, 0, -999, 999, ' pt');
4256
4257	this.addKeyHandler(input, listener);
4258	this.addKeyHandler(globalSpacing, listener);
4259	this.addKeyHandler(topSpacing, listener);
4260	this.addKeyHandler(rightSpacing, listener);
4261	this.addKeyHandler(bottomSpacing, listener);
4262	this.addKeyHandler(leftSpacing, listener);
4263
4264	graph.getModel().addListener(mxEvent.CHANGE, listener);
4265	this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }});
4266	listener();
4267
4268	if (graph.cellEditor.isContentEditing())
4269	{
4270		var updating = false;
4271
4272		var updateCssHandler = function()
4273		{
4274			if (!updating)
4275			{
4276				updating = true;
4277
4278				window.setTimeout(function()
4279				{
4280					var node = graph.getSelectedEditingElement();
4281
4282					if (node != null)
4283					{
4284						function getRelativeLineHeight(fontSize, css, elt)
4285						{
4286							if (elt.style != null && css != null)
4287							{
4288								var lineHeight = css.lineHeight
4289
4290								if (elt.style.lineHeight != null && elt.style.lineHeight.substring(elt.style.lineHeight.length - 1) == '%')
4291								{
4292									return parseInt(elt.style.lineHeight) / 100;
4293								}
4294								else
4295								{
4296									return (lineHeight.substring(lineHeight.length - 2) == 'px') ?
4297											parseFloat(lineHeight) / fontSize : parseInt(lineHeight);
4298								}
4299							}
4300							else
4301							{
4302								return '';
4303							}
4304						};
4305
4306						function getAbsoluteFontSize(css)
4307						{
4308							var fontSize = (css != null) ? css.fontSize : null;
4309
4310							if (fontSize != null && fontSize.substring(fontSize.length - 2) == 'px')
4311							{
4312								return parseFloat(fontSize);
4313							}
4314							else
4315							{
4316								return mxConstants.DEFAULT_FONTSIZE;
4317							}
4318						};
4319
4320						var css = mxUtils.getCurrentStyle(node);
4321						var fontSize = getAbsoluteFontSize(css);
4322						var lineHeight = getRelativeLineHeight(fontSize, css, node);
4323
4324						// Finds common font size
4325						var elts = node.getElementsByTagName('*');
4326
4327						// IE does not support containsNode
4328						if (elts.length > 0 && window.getSelection && !mxClient.IS_IE && !mxClient.IS_IE11)
4329						{
4330							var selection = window.getSelection();
4331
4332							for (var i = 0; i < elts.length; i++)
4333							{
4334								if (selection.containsNode(elts[i], true))
4335								{
4336									temp = mxUtils.getCurrentStyle(elts[i]);
4337									fontSize = Math.max(getAbsoluteFontSize(temp), fontSize);
4338									var lh = getRelativeLineHeight(fontSize, temp, elts[i]);
4339
4340									if (lh != lineHeight || isNaN(lh))
4341									{
4342										lineHeight = '';
4343									}
4344								}
4345							}
4346						}
4347
4348						function hasParentOrOnlyChild(name)
4349						{
4350							if (graph.getParentByName(node, name, graph.cellEditor.textarea) != null)
4351							{
4352								return true;
4353							}
4354							else
4355							{
4356								var child = node;
4357
4358								while (child != null && child.childNodes.length == 1)
4359								{
4360									child = child.childNodes[0];
4361
4362									if (child.nodeName == name)
4363									{
4364										return true;
4365									}
4366								}
4367							}
4368
4369							return false;
4370						};
4371
4372						function isEqualOrPrefixed(str, value)
4373						{
4374							if (str != null && value != null)
4375							{
4376								if (str == value)
4377								{
4378									return true;
4379								}
4380								else if (str.length > value.length + 1)
4381								{
4382									return str.substring(str.length - value.length - 1, str.length) == '-' + value;
4383								}
4384							}
4385
4386							return false;
4387						};
4388
4389						if (css != null)
4390						{
4391							setSelected(fontStyleItems[0], css.fontWeight == 'bold' ||
4392								css.fontWeight > 400 || hasParentOrOnlyChild('B') ||
4393								hasParentOrOnlyChild('STRONG'));
4394							setSelected(fontStyleItems[1], css.fontStyle == 'italic' ||
4395								hasParentOrOnlyChild('I') || hasParentOrOnlyChild('EM'));
4396							setSelected(fontStyleItems[2], hasParentOrOnlyChild('U'));
4397							setSelected(sup, hasParentOrOnlyChild('SUP'));
4398							setSelected(sub, hasParentOrOnlyChild('SUB'));
4399
4400							if (!graph.cellEditor.isTableSelected())
4401							{
4402								var align = graph.cellEditor.align || mxUtils.getValue(ss.style, mxConstants.STYLE_ALIGN, mxConstants.ALIGN_CENTER);
4403
4404								if (isEqualOrPrefixed(css.textAlign, 'justify'))
4405								{
4406									setSelected(full, isEqualOrPrefixed(css.textAlign, 'justify'));
4407									setSelected(left, false);
4408									setSelected(center, false);
4409									setSelected(right, false);
4410								}
4411								else
4412								{
4413									setSelected(full, false);
4414									setSelected(left, align == mxConstants.ALIGN_LEFT);
4415									setSelected(center, align == mxConstants.ALIGN_CENTER);
4416									setSelected(right, align == mxConstants.ALIGN_RIGHT);
4417								}
4418							}
4419							else
4420							{
4421								setSelected(full, isEqualOrPrefixed(css.textAlign, 'justify'));
4422								setSelected(left, isEqualOrPrefixed(css.textAlign, 'left'));
4423								setSelected(center, isEqualOrPrefixed(css.textAlign, 'center'));
4424								setSelected(right, isEqualOrPrefixed(css.textAlign, 'right'));
4425							}
4426
4427							currentTable = graph.getParentByName(node, 'TABLE', graph.cellEditor.textarea);
4428							tableRow = (currentTable == null) ? null : graph.getParentByName(node, 'TR', currentTable);
4429							tableCell = (currentTable == null) ? null : graph.getParentByNames(node, ['TD', 'TH'], currentTable);
4430							tableWrapper.style.display = (currentTable != null) ? '' : 'none';
4431
4432							if (document.activeElement != input)
4433							{
4434								if (node.nodeName == 'FONT' && node.getAttribute('size') == '4' &&
4435									pendingFontSize != null)
4436								{
4437									node.removeAttribute('size');
4438									node.style.fontSize = pendingFontSize + ' pt';
4439									pendingFontSize = null;
4440								}
4441								else
4442								{
4443									input.value = (isNaN(fontSize)) ? '' : fontSize + ' pt';
4444								}
4445
4446								var lh = parseFloat(lineHeight);
4447
4448								if (!isNaN(lh))
4449								{
4450									lineHeightInput.value = Math.round(lh * 100) + ' %';
4451								}
4452								else
4453								{
4454									lineHeightInput.value = '100 %';
4455								}
4456							}
4457
4458							// Converts rgb(r,g,b) values
4459							var color = css.color.replace(
4460									/\brgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*(\d+)\s*)?\)/g,
4461									function($0, $1, $2, $3, $4)
4462									{
4463										return '#' + ('0' + Number($1).toString(16)).substr(-2) +
4464											('0' + Number($2).toString(16)).substr(-2) +
4465											('0' + Number($3).toString(16)).substr(-2) + (($4 != null) ?
4466											('0' + Number($4).toString(16)).substr(-2) : '');
4467								    });
4468							var color2 = css.backgroundColor.replace(
4469								/\brgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*(\d+)\s*)?\)/g,
4470								function($0, $1, $2, $3, $4)
4471								{
4472									return '#' + ('0' + Number($1).toString(16)).substr(-2) +
4473										('0' + Number($2).toString(16)).substr(-2) +
4474										('0' + Number($3).toString(16)).substr(-2) + (($4 != null) ?
4475										('0' + Number($4).toString(16)).substr(-2) : '');
4476								});
4477
4478							// Updates the color picker for the current font
4479							if (fontColorApply != null)
4480							{
4481								if (color.charAt(0) == '#')
4482								{
4483									currentFontColor = color;
4484								}
4485								else
4486								{
4487									currentFontColor = '#000000';
4488								}
4489
4490								fontColorApply(currentFontColor, true);
4491							}
4492
4493							if (bgColorApply != null)
4494							{
4495								if (color2.charAt(0) == '#')
4496								{
4497									currentBgColor = color2;
4498								}
4499								else
4500								{
4501									currentBgColor = null;
4502								}
4503
4504								bgColorApply(currentBgColor, true);
4505							}
4506
4507							// Workaround for firstChild is null or not an object
4508							// in the log which seems to be IE8- only / 29.01.15
4509							if (fontMenu.firstChild != null)
4510							{
4511								fontMenu.firstChild.nodeValue = Graph.stripQuotes(css.fontFamily);
4512							}
4513						}
4514					}
4515
4516					updating = false;
4517				}, 0);
4518			}
4519		};
4520
4521		if (mxClient.IS_FF || mxClient.IS_EDGE || mxClient.IS_IE || mxClient.IS_IE11)
4522		{
4523			mxEvent.addListener(graph.cellEditor.textarea, 'DOMSubtreeModified', updateCssHandler);
4524		}
4525
4526		mxEvent.addListener(graph.cellEditor.textarea, 'input', updateCssHandler);
4527		mxEvent.addListener(graph.cellEditor.textarea, 'touchend', updateCssHandler);
4528		mxEvent.addListener(graph.cellEditor.textarea, 'mouseup', updateCssHandler);
4529		mxEvent.addListener(graph.cellEditor.textarea, 'keyup', updateCssHandler);
4530		this.listeners.push({destroy: function()
4531		{
4532			// No need to remove listener since textarea is destroyed after edit
4533		}});
4534		updateCssHandler();
4535	}
4536
4537	return container;
4538};
4539
4540/**
4541 * Adds the label menu items to the given menu and parent.
4542 */
4543StyleFormatPanel = function(format, editorUi, container)
4544{
4545	BaseFormatPanel.call(this, format, editorUi, container);
4546	this.init();
4547};
4548
4549mxUtils.extend(StyleFormatPanel, BaseFormatPanel);
4550
4551/**
4552 *
4553 */
4554StyleFormatPanel.prototype.defaultStrokeColor = 'black';
4555
4556/**
4557 * Adds the label menu items to the given menu and parent.
4558 */
4559StyleFormatPanel.prototype.init = function()
4560{
4561	var ui = this.editorUi;
4562	var editor = ui.editor;
4563	var graph = editor.graph;
4564	var ss = this.format.getSelectionState();
4565
4566	if (!ss.containsLabel && ss.cells.length > 0)
4567	{
4568		if (ss.containsImage && ss.vertices.length == 1 && ss.style.shape == 'image' &&
4569			ss.style.image != null && ss.style.image.substring(0, 19) == 'data:image/svg+xml;')
4570		{
4571			this.container.appendChild(this.addSvgStyles(this.createPanel()));
4572		}
4573
4574		if (!ss.containsImage || ss.style.shape == 'image')
4575		{
4576			this.container.appendChild(this.addFill(this.createPanel()));
4577		}
4578
4579		this.container.appendChild(this.addStroke(this.createPanel()));
4580		this.container.appendChild(this.addLineJumps(this.createPanel()));
4581		var opacityPanel = this.createRelativeOption(mxResources.get('opacity'), mxConstants.STYLE_OPACITY);
4582		opacityPanel.style.paddingTop = '8px';
4583		opacityPanel.style.paddingBottom = '8px';
4584		this.container.appendChild(opacityPanel);
4585		this.container.appendChild(this.addEffects(this.createPanel()));
4586	}
4587
4588	var opsPanel = this.addEditOps(this.createPanel());
4589
4590	if (opsPanel.firstChild != null)
4591	{
4592		mxUtils.br(opsPanel);
4593	}
4594
4595	this.container.appendChild(this.addStyleOps(opsPanel));
4596};
4597
4598/**
4599 * Use browser for parsing CSS.
4600 */
4601StyleFormatPanel.prototype.getCssRules = function(css)
4602{
4603	var doc = document.implementation.createHTMLDocument('');
4604	var styleElement = document.createElement('style');
4605
4606	mxUtils.setTextContent(styleElement, css);
4607	doc.body.appendChild(styleElement);
4608
4609	return styleElement.sheet.cssRules;
4610};
4611
4612/**
4613 * Adds the label menu items to the given menu and parent.
4614 */
4615StyleFormatPanel.prototype.addSvgStyles = function(container)
4616{
4617	var ui = this.editorUi;
4618	var graph = ui.editor.graph;
4619	var ss = this.format.getSelectionState();
4620	container.style.paddingTop = '6px';
4621	container.style.paddingBottom = '6px';
4622	container.style.fontWeight = 'bold';
4623	container.style.display = 'none';
4624
4625	try
4626	{
4627		var exp = ss.style.editableCssRules;
4628
4629		if (exp != null)
4630		{
4631			var regex = new RegExp(exp);
4632
4633			var data = ss.style.image.substring(ss.style.image.indexOf(',') + 1);
4634			var xml = (window.atob) ? atob(data) : Base64.decode(data, true);
4635			var svg = mxUtils.parseXml(xml);
4636
4637			if (svg != null)
4638			{
4639				var styles = svg.getElementsByTagName('style');
4640
4641				for (var i = 0; i < styles.length; i++)
4642				{
4643					var rules = this.getCssRules(mxUtils.getTextContent(styles[i]));
4644
4645					for (var j = 0; j < rules.length; j++)
4646					{
4647						this.addSvgRule(container, rules[j], svg, styles[i], rules, j, regex);
4648					}
4649				}
4650			}
4651		}
4652	}
4653	catch (e)
4654	{
4655		// ignore
4656	}
4657
4658	return container;
4659};
4660
4661/**
4662 * Adds the label menu items to the given menu and parent.
4663 */
4664StyleFormatPanel.prototype.addSvgRule = function(container, rule, svg, styleElem, rules, ruleIndex, regex)
4665{
4666	var ui = this.editorUi;
4667	var graph = ui.editor.graph;
4668
4669	if (regex.test(rule.selectorText))
4670	{
4671		function rgb2hex(rgb)
4672		{
4673			 rgb = rgb.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i);
4674
4675			 return (rgb && rgb.length === 4) ? "#" +
4676			  ("0" + parseInt(rgb[1],10).toString(16)).slice(-2) +
4677			  ("0" + parseInt(rgb[2],10).toString(16)).slice(-2) +
4678			  ("0" + parseInt(rgb[3],10).toString(16)).slice(-2) : '';
4679		};
4680
4681		var addStyleRule = mxUtils.bind(this, function(rule, key, label)
4682		{
4683			var value = mxUtils.trim(rule.style[key]);
4684
4685			if (value != '' && value.substring(0, 4) != 'url(')
4686			{
4687				var option = this.createColorOption(label + ' ' + rule.selectorText, function()
4688				{
4689					return rgb2hex(value);
4690				}, mxUtils.bind(this, function(color)
4691				{
4692					rules[ruleIndex].style[key] = color;
4693					var cssTxt = '';
4694
4695					for (var i = 0; i < rules.length; i++)
4696					{
4697						cssTxt += rules[i].cssText + ' ';
4698					}
4699
4700					styleElem.textContent = cssTxt;
4701					var xml = mxUtils.getXml(svg.documentElement);
4702
4703					graph.setCellStyles(mxConstants.STYLE_IMAGE, 'data:image/svg+xml,' +
4704						((window.btoa) ? btoa(xml) : Base64.encode(xml, true)),
4705						this.format.getSelectionState().cells);
4706				}), '#ffffff',
4707				{
4708					install: function(apply)
4709					{
4710						// ignore
4711					},
4712					destroy: function()
4713					{
4714						// ignore
4715					}
4716				});
4717
4718				container.appendChild(option);
4719
4720				// Shows container if rules are added
4721				container.style.display = '';
4722			}
4723		});
4724
4725		addStyleRule(rule, 'fill', mxResources.get('fill'));
4726		addStyleRule(rule, 'stroke', mxResources.get('line'));
4727		addStyleRule(rule, 'stop-color', mxResources.get('gradient'));
4728	}
4729};
4730
4731/**
4732 * Adds the label menu items to the given menu and parent.
4733 */
4734StyleFormatPanel.prototype.addEditOps = function(div)
4735{
4736	var ss = this.format.getSelectionState();
4737	var btn = null;
4738
4739	if (ss.cells.length == 1)
4740	{
4741		btn = mxUtils.button(mxResources.get('editStyle'), mxUtils.bind(this, function(evt)
4742		{
4743			this.editorUi.actions.get('editStyle').funct();
4744		}));
4745
4746		btn.setAttribute('title', mxResources.get('editStyle') + ' (' + this.editorUi.actions.get('editStyle').shortcut + ')');
4747		btn.style.width = '210px';
4748		btn.style.marginBottom = '2px';
4749
4750		div.appendChild(btn);
4751	}
4752
4753	if (ss.image && ss.cells.length > 0)
4754	{
4755		var btn2 = mxUtils.button(mxResources.get('editImage'), mxUtils.bind(this, function(evt)
4756		{
4757			this.editorUi.actions.get('image').funct();
4758		}));
4759
4760		btn2.setAttribute('title', mxResources.get('editImage'));
4761		btn2.style.marginBottom = '2px';
4762
4763		if (btn == null)
4764		{
4765			btn2.style.width = '210px';
4766		}
4767		else
4768		{
4769			btn.style.width = '104px';
4770			btn2.style.width = '104px';
4771			btn2.style.marginLeft = '2px';
4772		}
4773
4774		div.appendChild(btn2);
4775	}
4776
4777	return div;
4778};
4779
4780/**
4781 * Adds the label menu items to the given menu and parent.
4782 */
4783StyleFormatPanel.prototype.addFill = function(container)
4784{
4785	var ui = this.editorUi;
4786	var graph = ui.editor.graph;
4787	var ss = this.format.getSelectionState();
4788	container.style.paddingTop = '6px';
4789	container.style.paddingBottom = '6px';
4790
4791	// Adds gradient direction option
4792	var gradientSelect = document.createElement('select');
4793	gradientSelect.style.position = 'absolute';
4794	gradientSelect.style.left = '104px';
4795	gradientSelect.style.width = '70px';
4796	gradientSelect.style.height = '22px';
4797	gradientSelect.style.padding = '0px';
4798	gradientSelect.style.marginTop = '-3px';
4799	gradientSelect.style.borderRadius = '4px';
4800	gradientSelect.style.border = '1px solid rgb(160, 160, 160)';
4801	gradientSelect.style.boxSizing = 'border-box';
4802
4803	var fillStyleSelect = gradientSelect.cloneNode(false);
4804
4805	// Stops events from bubbling to color option event handler
4806	mxEvent.addListener(gradientSelect, 'click', function(evt)
4807	{
4808		mxEvent.consume(evt);
4809	});
4810	mxEvent.addListener(fillStyleSelect, 'click', function(evt)
4811	{
4812		mxEvent.consume(evt);
4813	});
4814
4815	var defs = (ss.vertices.length >= 1) ? graph.stylesheet.getDefaultVertexStyle() : graph.stylesheet.getDefaultEdgeStyle();
4816	var gradientPanel = this.createCellColorOption(mxResources.get('gradient'), mxConstants.STYLE_GRADIENTCOLOR,
4817		(defs[mxConstants.STYLE_GRADIENTCOLOR] != null) ? defs[mxConstants.STYLE_GRADIENTCOLOR] : '#ffffff', function(color)
4818	{
4819		if (color == null || color == mxConstants.NONE)
4820		{
4821			gradientSelect.style.display = 'none';
4822		}
4823		else
4824		{
4825			gradientSelect.style.display = '';
4826		}
4827	}, function(color)
4828	{
4829		graph.updateCellStyles(mxConstants.STYLE_GRADIENTCOLOR, color, graph.getSelectionCells());
4830	});
4831
4832	var fillKey = (ss.style.shape == 'image') ? mxConstants.STYLE_IMAGE_BACKGROUND : mxConstants.STYLE_FILLCOLOR;
4833	var label = (ss.style.shape == 'image') ? mxResources.get('background') : mxResources.get('fill');
4834
4835	var fillPanel = this.createCellColorOption(label, fillKey, 'default', null, mxUtils.bind(this, function(color)
4836	{
4837		graph.setCellStyles(fillKey, color, ss.cells);
4838	}), graph.shapeBackgroundColor);
4839
4840	fillPanel.style.fontWeight = 'bold';
4841	var tmpColor = mxUtils.getValue(ss.style, fillKey, null);
4842	gradientPanel.style.display = (tmpColor != null && tmpColor != mxConstants.NONE &&
4843		ss.fill && ss.style.shape != 'image') ? '' : 'none';
4844
4845	var directions = [mxConstants.DIRECTION_NORTH, mxConstants.DIRECTION_EAST,
4846	                  mxConstants.DIRECTION_SOUTH, mxConstants.DIRECTION_WEST,
4847					  mxConstants.DIRECTION_RADIAL];
4848
4849	for (var i = 0; i < directions.length; i++)
4850	{
4851		var gradientOption = document.createElement('option');
4852		gradientOption.setAttribute('value', directions[i]);
4853		mxUtils.write(gradientOption, mxResources.get(directions[i]));
4854		gradientSelect.appendChild(gradientOption);
4855	}
4856
4857	gradientPanel.appendChild(gradientSelect);
4858
4859	for (var i = 0; i < Editor.roughFillStyles.length; i++)
4860	{
4861		var fillStyleOption = document.createElement('option');
4862		fillStyleOption.setAttribute('value', Editor.roughFillStyles[i].val);
4863		mxUtils.write(fillStyleOption, Editor.roughFillStyles[i].dispName);
4864		fillStyleSelect.appendChild(fillStyleOption);
4865	}
4866
4867	fillPanel.appendChild(fillStyleSelect);
4868
4869	var listener = mxUtils.bind(this, function()
4870	{
4871		ss = this.format.getSelectionState();
4872		var value = mxUtils.getValue(ss.style, mxConstants.STYLE_GRADIENT_DIRECTION, mxConstants.DIRECTION_SOUTH);
4873		var fillStyle = mxUtils.getValue(ss.style, 'fillStyle', 'auto');
4874
4875		// Handles empty string which is not allowed as a value
4876		if (value == '')
4877		{
4878			value = mxConstants.DIRECTION_SOUTH;
4879		}
4880
4881		gradientSelect.value = value;
4882		fillStyleSelect.value = fillStyle;
4883		container.style.display = (ss.fill) ? '' : 'none';
4884
4885		var fillColor = mxUtils.getValue(ss.style, mxConstants.STYLE_FILLCOLOR, null);
4886
4887		if (!ss.fill || ss.containsImage || fillColor == null || fillColor == mxConstants.NONE || ss.style.shape == 'filledEdge')
4888		{
4889			fillStyleSelect.style.display = 'none';
4890			gradientPanel.style.display = 'none';
4891		}
4892		else
4893		{
4894			fillStyleSelect.style.display = (ss.style.sketch == '1') ? '' : 'none';
4895			gradientPanel.style.display = (ss.style.sketch != '1' || fillStyle == 'solid' || fillStyle == 'auto') ? '' : 'none';
4896		}
4897	});
4898
4899	graph.getModel().addListener(mxEvent.CHANGE, listener);
4900	this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }});
4901	listener();
4902
4903	mxEvent.addListener(gradientSelect, 'change', function(evt)
4904	{
4905		graph.setCellStyles(mxConstants.STYLE_GRADIENT_DIRECTION, gradientSelect.value, ss.cells);
4906		ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_GRADIENT_DIRECTION],
4907			'values', [gradientSelect.value], 'cells', ss.cells));
4908		mxEvent.consume(evt);
4909	});
4910
4911	mxEvent.addListener(fillStyleSelect, 'change', function(evt)
4912	{
4913		graph.setCellStyles('fillStyle', fillStyleSelect.value, ss.cells);
4914		ui.fireEvent(new mxEventObject('styleChanged', 'keys', ['fillStyle'],
4915			'values', [fillStyleSelect.value], 'cells', ss.cells));
4916		mxEvent.consume(evt);
4917	});
4918
4919	container.appendChild(fillPanel);
4920	container.appendChild(gradientPanel);
4921
4922	// Adds custom colors
4923	var custom = this.getCustomColors();
4924
4925	for (var i = 0; i < custom.length; i++)
4926	{
4927		container.appendChild(this.createCellColorOption(custom[i].title, custom[i].key, custom[i].defaultValue));
4928	}
4929
4930	return container;
4931};
4932
4933/**
4934 * Adds the label menu items to the given menu and parent.
4935 */
4936StyleFormatPanel.prototype.getCustomColors = function()
4937{
4938	var ss = this.format.getSelectionState();
4939	var result = [];
4940
4941	if (ss.style.shape == 'swimlane' || ss.style.shape == 'table')
4942	{
4943		result.push({title: mxResources.get('laneColor'), key: 'swimlaneFillColor', defaultValue: 'default'});
4944	}
4945
4946	return result;
4947};
4948
4949/**
4950 * Adds the label menu items to the given menu and parent.
4951 */
4952StyleFormatPanel.prototype.addStroke = function(container)
4953{
4954	var ui = this.editorUi;
4955	var graph = ui.editor.graph;
4956	var ss = this.format.getSelectionState();
4957
4958	container.style.paddingTop = '6px';
4959	container.style.paddingBottom = '4px';
4960	container.style.whiteSpace = 'normal';
4961
4962	var colorPanel = document.createElement('div');
4963	colorPanel.style.fontWeight = 'bold';
4964
4965	if (!ss.stroke)
4966	{
4967		colorPanel.style.display = 'none';
4968	}
4969
4970	// Adds gradient direction option
4971	var styleSelect = document.createElement('select');
4972	styleSelect.style.position = 'absolute';
4973	styleSelect.style.height = '22px';
4974	styleSelect.style.padding = '0px';
4975	styleSelect.style.marginTop = '-3px';
4976	styleSelect.style.boxSizing = 'border-box';
4977	styleSelect.style.left = '94px';
4978	styleSelect.style.width = '80px';
4979	styleSelect.style.border = '1px solid rgb(160, 160, 160)';
4980	styleSelect.style.borderRadius = '4px';
4981
4982	var styles = ['sharp', 'rounded', 'curved'];
4983
4984	for (var i = 0; i < styles.length; i++)
4985	{
4986		var styleOption = document.createElement('option');
4987		styleOption.setAttribute('value', styles[i]);
4988		mxUtils.write(styleOption, mxResources.get(styles[i]));
4989		styleSelect.appendChild(styleOption);
4990	}
4991
4992	mxEvent.addListener(styleSelect, 'change', function(evt)
4993	{
4994		graph.getModel().beginUpdate();
4995		try
4996		{
4997			var keys = [mxConstants.STYLE_ROUNDED, mxConstants.STYLE_CURVED];
4998			// Default for rounded is 1
4999			var values = ['0', null];
5000
5001			if (styleSelect.value == 'rounded')
5002			{
5003				values = ['1', null];
5004			}
5005			else if (styleSelect.value == 'curved')
5006			{
5007				values = [null, '1'];
5008			}
5009
5010			for (var i = 0; i < keys.length; i++)
5011			{
5012				graph.setCellStyles(keys[i], values[i], ss.cells);
5013			}
5014
5015			ui.fireEvent(new mxEventObject('styleChanged', 'keys', keys,
5016				'values', values, 'cells', ss.cells));
5017		}
5018		finally
5019		{
5020			graph.getModel().endUpdate();
5021		}
5022
5023		mxEvent.consume(evt);
5024	});
5025
5026	// Stops events from bubbling to color option event handler
5027	mxEvent.addListener(styleSelect, 'click', function(evt)
5028	{
5029		mxEvent.consume(evt);
5030	});
5031
5032	var strokeKey = (ss.style.shape == 'image') ? mxConstants.STYLE_IMAGE_BORDER : mxConstants.STYLE_STROKECOLOR;
5033	var label = (ss.style.shape == 'image') ? mxResources.get('border') : mxResources.get('line');
5034
5035	var lineColor = this.createCellColorOption(label, strokeKey, 'default', null, mxUtils.bind(this, function(color)
5036	{
5037		graph.setCellStyles(strokeKey, color, ss.cells);
5038	}), graph.shapeForegroundColor);
5039
5040	lineColor.appendChild(styleSelect);
5041	colorPanel.appendChild(lineColor);
5042
5043	// Used if only edges selected
5044	var stylePanel = colorPanel.cloneNode(false);
5045	stylePanel.style.fontWeight = 'normal';
5046	stylePanel.style.whiteSpace = 'nowrap';
5047	stylePanel.style.position = 'relative';
5048	stylePanel.style.paddingLeft = '0px';
5049	stylePanel.style.marginBottom = '2px';
5050	stylePanel.style.overflow = 'hidden';
5051	stylePanel.style.marginTop = '2px';
5052	stylePanel.style.width = '220px';
5053	stylePanel.className = 'geToolbarContainer';
5054
5055	var addItem = mxUtils.bind(this, function(menu, width, cssName, keys, values)
5056	{
5057		var item = this.editorUi.menus.styleChange(menu, '', keys, values, 'geIcon', null);
5058
5059		var pat = document.createElement('div');
5060		pat.style.width = width + 'px';
5061		pat.style.height = '1px';
5062		pat.style.borderBottom = '1px ' + cssName + ' ' + this.defaultStrokeColor;
5063		pat.style.paddingTop = '6px';
5064
5065		item.firstChild.firstChild.style.padding = '0px 4px 0px 4px';
5066		item.firstChild.firstChild.style.width = width + 'px';
5067		item.firstChild.firstChild.appendChild(pat);
5068
5069		return item;
5070	});
5071
5072	var pattern = this.editorUi.toolbar.addMenuFunctionInContainer(stylePanel, 'geSprite-orthogonal', mxResources.get('pattern'), false, mxUtils.bind(this, function(menu)
5073	{
5074		addItem(menu, 75, 'solid', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], [null, null]).setAttribute('title', mxResources.get('solid'));
5075		addItem(menu, 75, 'dashed', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', null]).setAttribute('title', mxResources.get('dashed'));
5076		addItem(menu, 75, 'dotted', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', '1 1']).setAttribute('title', mxResources.get('dotted') + ' (1)');
5077		addItem(menu, 75, 'dotted', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', '1 2']).setAttribute('title', mxResources.get('dotted') + ' (2)');
5078		addItem(menu, 75, 'dotted', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', '1 4']).setAttribute('title', mxResources.get('dotted') + ' (3)');
5079	}));
5080
5081	// Used for mixed selection (vertices and edges)
5082	var altStylePanel = stylePanel.cloneNode(false);
5083
5084	var edgeShape = this.editorUi.toolbar.addMenuFunctionInContainer(altStylePanel, 'geSprite-connection', mxResources.get('connection'), false, mxUtils.bind(this, function(menu)
5085	{
5086		this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_SHAPE, mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE, 'width'], [null, null, null, null], 'geIcon geSprite geSprite-connection', null, true).setAttribute('title', mxResources.get('line'));
5087		this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_SHAPE, mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE, 'width'], ['link', null, null, null], 'geIcon geSprite geSprite-linkedge', null, true).setAttribute('title', mxResources.get('link'));
5088		this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_SHAPE, mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE, 'width'], ['flexArrow', null, null, null], 'geIcon geSprite geSprite-arrow', null, true).setAttribute('title', mxResources.get('arrow'));
5089		this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_SHAPE, mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE, 'width'], ['arrow', null, null, null], 'geIcon geSprite geSprite-simplearrow', null, true).setAttribute('title', mxResources.get('simpleArrow'));
5090	}));
5091
5092	var altPattern = this.editorUi.toolbar.addMenuFunctionInContainer(altStylePanel, 'geSprite-orthogonal', mxResources.get('pattern'), false, mxUtils.bind(this, function(menu)
5093	{
5094		addItem(menu, 33, 'solid', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], [null, null]).setAttribute('title', mxResources.get('solid'));
5095		addItem(menu, 33, 'dashed', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', null]).setAttribute('title', mxResources.get('dashed'));
5096		addItem(menu, 33, 'dotted', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', '1 1']).setAttribute('title', mxResources.get('dotted') + ' (1)');
5097		addItem(menu, 33, 'dotted', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', '1 2']).setAttribute('title', mxResources.get('dotted') + ' (2)');
5098		addItem(menu, 33, 'dotted', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', '1 4']).setAttribute('title', mxResources.get('dotted') + ' (3)');
5099	}));
5100
5101	var stylePanel2 = stylePanel.cloneNode(false);
5102
5103	// Stroke width
5104	var input = document.createElement('input');
5105	input.style.position = 'absolute';
5106	input.style.textAlign = 'right';
5107	input.style.marginTop = '2px';
5108	input.style.width = '52px';
5109	input.style.height = '21px';
5110	input.style.left = '146px';
5111	input.style.border = '1px solid rgb(160, 160, 160)';
5112	input.style.borderRadius = '4px';
5113	input.style.boxSizing = 'border-box';
5114	input.setAttribute('title', mxResources.get('linewidth'));
5115
5116	stylePanel.appendChild(input);
5117
5118	var altInput = input.cloneNode(true);
5119	altStylePanel.appendChild(altInput);
5120
5121	function update(evt)
5122	{
5123		// Maximum stroke width is 999
5124		var value = parseFloat(input.value);
5125		value = Math.min(999, Math.max(0, (isNaN(value)) ? 1 : value));
5126
5127		if (value != mxUtils.getValue(ss.style, mxConstants.STYLE_STROKEWIDTH, 1))
5128		{
5129			graph.setCellStyles(mxConstants.STYLE_STROKEWIDTH, value, ss.cells);
5130			ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_STROKEWIDTH],
5131					'values', [value], 'cells', ss.cells));
5132		}
5133
5134		input.value = value + ' pt';
5135		mxEvent.consume(evt);
5136	};
5137
5138	function altUpdate(evt)
5139	{
5140		// Maximum stroke width is 999
5141		var value = parseFloat(altInput.value);
5142		value = Math.min(999, Math.max(0, (isNaN(value)) ? 1 : value));
5143
5144		if (value != mxUtils.getValue(ss.style, mxConstants.STYLE_STROKEWIDTH, 1))
5145		{
5146			graph.setCellStyles(mxConstants.STYLE_STROKEWIDTH, value, ss.cells);
5147			ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_STROKEWIDTH],
5148					'values', [value], 'cells', ss.cells));
5149		}
5150
5151		altInput.value = value + ' pt';
5152		mxEvent.consume(evt);
5153	};
5154
5155	var stepper = this.createStepper(input, update, 1, 9);
5156	stepper.style.display = input.style.display;
5157	stepper.style.marginTop = '2px';
5158	stepper.style.left = '198px';
5159	stylePanel.appendChild(stepper);
5160
5161	var altStepper = this.createStepper(altInput, altUpdate, 1, 9);
5162	altStepper.style.display = altInput.style.display;
5163	altStepper.style.marginTop = '2px';
5164	altInput.style.position = 'absolute';
5165	altStepper.style.left = '198px';
5166	altStylePanel.appendChild(altStepper);
5167
5168	mxEvent.addListener(input, 'blur', update);
5169	mxEvent.addListener(input, 'change', update);
5170
5171	mxEvent.addListener(altInput, 'blur', altUpdate);
5172	mxEvent.addListener(altInput, 'change', altUpdate);
5173
5174	var edgeStyle = this.editorUi.toolbar.addMenuFunctionInContainer(stylePanel2, 'geSprite-orthogonal', mxResources.get('waypoints'), false, mxUtils.bind(this, function(menu)
5175	{
5176		if (ss.style.shape != 'arrow')
5177		{
5178			this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], [null, null, null], 'geIcon geSprite geSprite-straight', null, true).setAttribute('title', mxResources.get('straight'));
5179			this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['orthogonalEdgeStyle', null, null], 'geIcon geSprite geSprite-orthogonal', null, true).setAttribute('title', mxResources.get('orthogonal'));
5180			this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['elbowEdgeStyle', null, null, null], 'geIcon geSprite geSprite-horizontalelbow', null, true).setAttribute('title', mxResources.get('simple'));
5181			this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['elbowEdgeStyle', 'vertical', null, null], 'geIcon geSprite geSprite-verticalelbow', null, true).setAttribute('title', mxResources.get('simple'));
5182			this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['isometricEdgeStyle', null, null, null], 'geIcon geSprite geSprite-horizontalisometric', null, true).setAttribute('title', mxResources.get('isometric'));
5183			this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['isometricEdgeStyle', 'vertical', null, null], 'geIcon geSprite geSprite-verticalisometric', null, true).setAttribute('title', mxResources.get('isometric'));
5184
5185			if (ss.style.shape == 'connector')
5186			{
5187				this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['orthogonalEdgeStyle', '1', null], 'geIcon geSprite geSprite-curved', null, true).setAttribute('title', mxResources.get('curved'));
5188			}
5189
5190			this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['entityRelationEdgeStyle', null, null], 'geIcon geSprite geSprite-entity', null, true).setAttribute('title', mxResources.get('entityRelation'));
5191		}
5192	}));
5193
5194	var lineStart = this.editorUi.toolbar.addMenuFunctionInContainer(stylePanel2, 'geSprite-startclassic', mxResources.get('linestart'), false, mxUtils.bind(this, function(menu)
5195	{
5196		if (ss.style.shape == 'connector' || ss.style.shape == 'flexArrow' || ss.style.shape == 'filledEdge')
5197		{
5198			var item = this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.NONE, 0], 'geIcon', null, false);
5199			item.setAttribute('title', mxResources.get('none'));
5200
5201			var font = document.createElement('font');
5202			font.style.fontSize = '10px';
5203			mxUtils.write(font, mxResources.get('none'));
5204			item.firstChild.firstChild.appendChild(font);
5205
5206			if (ss.style.shape == 'connector' || ss.style.shape == 'filledEdge')
5207			{
5208				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_CLASSIC, 1], null, null, false, Format.classicFilledMarkerImage.src));
5209				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_CLASSIC_THIN, 1], null, null, false, Format.classicThinFilledMarkerImage.src));
5210				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_OPEN, 0], null, null, false, Format.openFilledMarkerImage.src));
5211				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_OPEN_THIN, 0], null, null, false, Format.openThinFilledMarkerImage.src));
5212				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['openAsync', 0], null, null, false, Format.openAsyncFilledMarkerImage.src));
5213				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_BLOCK, 1], null, null, false, Format.blockFilledMarkerImage.src));
5214				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_BLOCK_THIN, 1], null, null, false, Format.blockThinFilledMarkerImage.src));
5215				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['async', 1], null, null, false, Format.asyncFilledMarkerImage.src));
5216				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_OVAL, 1], null, null, false, Format.ovalFilledMarkerImage.src));
5217				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_DIAMOND, 1], null, null, false, Format.diamondFilledMarkerImage.src));
5218				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_DIAMOND_THIN, 1], null, null, false, Format.diamondThinFilledMarkerImage.src));
5219				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_CLASSIC, 0], null, null, false, Format.classicMarkerImage.src));
5220				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_CLASSIC_THIN, 0], null, null, false, Format.classicThinMarkerImage.src));
5221				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_BLOCK, 0], null, null, false, Format.blockMarkerImage.src));
5222				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_BLOCK_THIN, 0], null, null, false, Format.blockThinMarkerImage.src));
5223				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['async', 0], null, null, false, Format.asyncMarkerImage.src));
5224				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_OVAL, 0], null, null, false, Format.ovalMarkerImage.src));
5225				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_DIAMOND, 0], null, null, false, Format.diamondMarkerImage.src));
5226				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_DIAMOND_THIN, 0], null, null, false, Format.diamondThinMarkerImage.src));
5227				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['box', 0], null, null, false, Format.boxMarkerImage.src));
5228				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['halfCircle', 0], null, null, false, Format.halfCircleMarkerImage.src));
5229				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['dash', 0], null, null, false, Format.dashMarkerImage.src));
5230				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['cross', 0], null, null, false, Format.crossMarkerImage.src));
5231				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['circlePlus', 0], null, null, false, Format.circlePlusMarkerImage.src));
5232				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['circle', 1], null, null, false, Format.circleMarkerImage.src));
5233				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['baseDash', 0], null, null, false, Format.baseDashMarkerImage.src));
5234				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['ERone', 0], null, null, false, Format.EROneMarkerImage.src));
5235				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['ERmandOne', 0], null, null, false, Format.ERmandOneMarkerImage.src));
5236				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['ERmany', 0], null, null, false, Format.ERmanyMarkerImage.src));
5237				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['ERoneToMany', 0], null, null, false, Format.ERoneToManyMarkerImage.src));
5238				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['ERzeroToOne', 0], null, null, false, Format.ERzeroToOneMarkerImage.src));
5239				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['ERzeroToMany', 0], null, null, false, Format.ERzeroToManyMarkerImage.src));
5240				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['doubleBlock', 0], null, null, false, Format.doubleBlockMarkerImage.src));
5241				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['doubleBlock', 1], null, null, false, Format.doubleBlockFilledMarkerImage.src));
5242			}
5243			else
5244			{
5245				this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW], [mxConstants.ARROW_BLOCK], 'geIcon geSprite geSprite-startblocktrans', null, false).setAttribute('title', mxResources.get('block'));
5246			}
5247
5248			menu.div.style.width = '40px';
5249		}
5250	}));
5251
5252	var lineEnd = this.editorUi.toolbar.addMenuFunctionInContainer(stylePanel2, 'geSprite-endclassic', mxResources.get('lineend'), false, mxUtils.bind(this, function(menu)
5253	{
5254		if (ss.style.shape == 'connector' || ss.style.shape == 'flexArrow' || ss.style.shape == 'filledEdge')
5255		{
5256			var item = this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.NONE, 0], 'geIcon', null, false);
5257			item.setAttribute('title', mxResources.get('none'));
5258
5259			var font = document.createElement('font');
5260			font.style.fontSize = '10px';
5261			mxUtils.write(font, mxResources.get('none'));
5262			item.firstChild.firstChild.appendChild(font);
5263
5264			if (ss.style.shape == 'connector' || ss.style.shape == 'filledEdge')
5265			{
5266				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_CLASSIC, 1], null, null, false, Format.classicFilledMarkerImage.src), 'scaleX(-1)');
5267				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_CLASSIC_THIN, 1], null, null, false, Format.classicThinFilledMarkerImage.src), 'scaleX(-1)');
5268				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_OPEN, 0], null, null, false, Format.openFilledMarkerImage.src), 'scaleX(-1)');
5269				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_OPEN_THIN, 0], null, null, false, Format.openThinFilledMarkerImage.src), 'scaleX(-1)');
5270				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['openAsync', 0], null, null, false, Format.openAsyncFilledMarkerImage.src), 'scaleX(-1)');
5271				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_BLOCK, 1], null, null, false, Format.blockFilledMarkerImage.src), 'scaleX(-1)');
5272				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_BLOCK_THIN, 1], null, null, false, Format.blockThinFilledMarkerImage.src), 'scaleX(-1)');
5273				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['async', 1], null, null, false, Format.asyncFilledMarkerImage.src), 'scaleX(-1)');
5274				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_OVAL, 1], null, null, false, Format.ovalFilledMarkerImage.src), 'scaleX(-1)');
5275				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_DIAMOND, 1], null, null, false, Format.diamondFilledMarkerImage.src), 'scaleX(-1)');
5276				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_DIAMOND_THIN, 1], null, null, false, Format.diamondThinFilledMarkerImage.src), 'scaleX(-1)');
5277				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_CLASSIC, 0], null, null, false, Format.classicMarkerImage.src), 'scaleX(-1)');
5278				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_CLASSIC_THIN, 0], null, null, false, Format.classicThinMarkerImage.src), 'scaleX(-1)');
5279				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_BLOCK, 0], null, null, false, Format.blockMarkerImage.src), 'scaleX(-1)');
5280				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_BLOCK_THIN, 0], null, null, false, Format.blockThinMarkerImage.src), 'scaleX(-1)');
5281				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['async', 0], null, null, false, Format.asyncMarkerImage.src), 'scaleX(-1)');
5282				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_OVAL, 0], null, null, false, Format.ovalMarkerImage.src), 'scaleX(-1)');
5283				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_DIAMOND, 0], null, null, false, Format.diamondMarkerImage.src), 'scaleX(-1)');
5284				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_DIAMOND_THIN, 0], null, null, false, Format.diamondThinMarkerImage.src), 'scaleX(-1)');
5285				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['box', 0], null, null, false, Format.boxMarkerImage.src), 'scaleX(-1)');
5286				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['halfCircle', 0], null, null, false, Format.halfCircleMarkerImage.src), 'scaleX(-1)');
5287				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['dash', 0], null, null, false, Format.dashMarkerImage.src), 'scaleX(-1)');
5288				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['cross', 0], null, null, false, Format.crossMarkerImage.src), 'scaleX(-1)');
5289				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['circlePlus', 0], null, null, false, Format.circlePlusMarkerImage.src), 'scaleX(-1)');
5290				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['circle', 0], null, null, false, Format.circleMarkerImage.src), 'scaleX(-1)');
5291				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['baseDash', 0], null, null, false, Format.baseDashMarkerImage.src), 'scaleX(-1)');
5292				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['ERone', 0], null, null, false, Format.EROneMarkerImage.src), 'scaleX(-1)');
5293				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['ERmandOne', 0], null, null, false, Format.ERmandOneMarkerImage.src), 'scaleX(-1)');
5294				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['ERmany', 0], null, null, false, Format.ERmanyMarkerImage.src), 'scaleX(-1)');
5295				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['ERoneToMany', 0], null, null, false, Format.ERoneToManyMarkerImage.src), 'scaleX(-1)');
5296				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['ERzeroToOne', 0], null, null, false, Format.ERzeroToOneMarkerImage.src), 'scaleX(-1)');
5297				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['ERzeroToMany', 0], null, null, false, Format.ERzeroToManyMarkerImage.src), 'scaleX(-1)');
5298				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['doubleBlock', 0], null, null, false, Format.doubleBlockMarkerImage.src), 'scaleX(-1)');
5299				Format.processMenuIcon(this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['doubleBlock', 1], null, null, false, Format.doubleBlockFilledMarkerImage.src), 'scaleX(-1)');
5300			}
5301			else
5302			{
5303				this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW], [mxConstants.ARROW_BLOCK], 'geIcon geSprite geSprite-endblocktrans', null, false).setAttribute('title', mxResources.get('block'));
5304			}
5305
5306			menu.div.style.width = '40px';
5307		}
5308	}));
5309
5310	var elt = this.addArrow(edgeShape, 8);
5311	elt.nextSibling.style.position = 'relative';
5312	elt.nextSibling.style.top = '-2px';
5313	elt = this.addArrow(edgeStyle, 10);
5314	elt.nextSibling.style.position = 'relative';
5315	elt.nextSibling.style.top = '-3px';
5316	edgeStyle.getElementsByTagName('img')[0].style.top = '-1px';
5317	this.addArrow(lineStart);
5318	this.addArrow(lineEnd);
5319
5320	var symbol = this.addArrow(pattern, 9);
5321	symbol.className = 'geIcon';
5322	symbol.style.width = 'auto';
5323
5324	var altSymbol = this.addArrow(altPattern, 9);
5325	altSymbol.className = 'geIcon';
5326	altSymbol.style.width = '22px';
5327
5328	var solid = document.createElement('div');
5329	solid.style.width = '84px';
5330	solid.style.height = '1px';
5331	solid.style.borderBottom = '1px solid ' + this.defaultStrokeColor;
5332	solid.style.marginBottom = '7px';
5333	symbol.appendChild(solid);
5334
5335	var altSolid = document.createElement('div');
5336	altSolid.style.width = '23px';
5337	altSolid.style.height = '1px';
5338	altSolid.style.borderBottom = '1px solid ' + this.defaultStrokeColor;
5339	altSolid.style.marginBottom = '7px';
5340	altSymbol.appendChild(altSolid);
5341
5342	pattern.style.height = '15px';
5343	pattern.style.marginLeft = '16px';
5344	altPattern.style.height = '15px';
5345	altPattern.style.marginLeft = '3px';
5346	edgeShape.style.marginLeft = '10px';
5347	edgeShape.style.height = '15px';
5348	edgeStyle.style.marginLeft = '10px';
5349	edgeStyle.style.height = '17px';
5350	lineStart.style.marginLeft = '3px';
5351	lineStart.style.height = '17px';
5352	lineEnd.style.marginLeft = '3px';
5353	lineEnd.style.height = '17px';
5354
5355	container.appendChild(colorPanel);
5356	container.appendChild(altStylePanel);
5357	container.appendChild(stylePanel);
5358
5359	var arrowPanel = stylePanel.cloneNode(false);
5360	arrowPanel.style.padding = '5px 4px 6px 0px';
5361	arrowPanel.style.fontWeight = 'normal';
5362
5363	var span = document.createElement('div');
5364	span.style.position = 'absolute';
5365	span.style.marginLeft = '0px';
5366	span.style.marginBottom = '12px';
5367	span.style.marginTop = '2px';
5368	span.style.fontWeight = 'normal';
5369	span.style.width = '76px';
5370
5371	mxUtils.write(span, mxResources.get('lineend'));
5372	arrowPanel.appendChild(span);
5373
5374	var endSpacingUpdate, endSizeUpdate;
5375	var endSpacing = this.addUnitInput(arrowPanel, 'pt', 98, 52, function()
5376	{
5377		endSpacingUpdate.apply(this, arguments);
5378	});
5379	var endSize = this.addUnitInput(arrowPanel, 'pt', 30, 52, function()
5380	{
5381		endSizeUpdate.apply(this, arguments);
5382	});
5383
5384	mxUtils.br(arrowPanel);
5385
5386	var spacer = document.createElement('div');
5387	spacer.style.height = '8px';
5388	arrowPanel.appendChild(spacer);
5389
5390	span = span.cloneNode(false);
5391	mxUtils.write(span, mxResources.get('linestart'));
5392	arrowPanel.appendChild(span);
5393
5394	var startSpacingUpdate, startSizeUpdate;
5395	var startSpacing = this.addUnitInput(arrowPanel, 'pt', 98, 52, function()
5396	{
5397		startSpacingUpdate.apply(this, arguments);
5398	});
5399	var startSize = this.addUnitInput(arrowPanel, 'pt', 30, 52, function()
5400	{
5401		startSizeUpdate.apply(this, arguments);
5402	});
5403
5404	mxUtils.br(arrowPanel);
5405	this.addLabel(arrowPanel, mxResources.get('spacing'), 98, 52);
5406	this.addLabel(arrowPanel, mxResources.get('size'), 30, 52);
5407	mxUtils.br(arrowPanel);
5408
5409	var perimeterPanel = colorPanel.cloneNode(false);
5410	perimeterPanel.style.fontWeight = 'normal';
5411	perimeterPanel.style.position = 'relative';
5412	perimeterPanel.style.paddingLeft = '16px'
5413	perimeterPanel.style.marginBottom = '2px';
5414	perimeterPanel.style.marginTop = '6px';
5415	perimeterPanel.style.borderWidth = '0px';
5416	perimeterPanel.style.paddingBottom = '18px';
5417
5418	var span = document.createElement('div');
5419	span.style.position = 'absolute';
5420	span.style.marginLeft = '3px';
5421	span.style.marginBottom = '12px';
5422	span.style.marginTop = '1px';
5423	span.style.fontWeight = 'normal';
5424	span.style.width = '120px';
5425	mxUtils.write(span, mxResources.get('perimeter'));
5426	perimeterPanel.appendChild(span);
5427
5428	var perimeterUpdate;
5429	var perimeterSpacing = this.addUnitInput(perimeterPanel, 'pt', 30, 52, function()
5430	{
5431		perimeterUpdate.apply(this, arguments);
5432	});
5433
5434	if (ss.edges.length == ss.cells.length)
5435	{
5436		container.appendChild(stylePanel2);
5437		container.appendChild(arrowPanel);
5438	}
5439	else if (ss.vertices.length == ss.cells.length)
5440	{
5441		container.appendChild(perimeterPanel);
5442	}
5443
5444	var listener = mxUtils.bind(this, function(sender, evt, force)
5445	{
5446		ss = this.format.getSelectionState();
5447		var color = mxUtils.getValue(ss.style, strokeKey, null);
5448
5449		if (force || document.activeElement != input)
5450		{
5451			var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_STROKEWIDTH, 1));
5452			input.value = (isNaN(tmp)) ? '' : tmp + ' pt';
5453		}
5454
5455		if (force || document.activeElement != altInput)
5456		{
5457			var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_STROKEWIDTH, 1));
5458			altInput.value = (isNaN(tmp)) ? '' : tmp + ' pt';
5459		}
5460
5461		styleSelect.style.visibility = (ss.style.shape == 'connector' || ss.style.shape == 'filledEdge') ? '' : 'hidden';
5462
5463		if (mxUtils.getValue(ss.style, mxConstants.STYLE_CURVED, null) == '1')
5464		{
5465			styleSelect.value = 'curved';
5466		}
5467		else if (mxUtils.getValue(ss.style, mxConstants.STYLE_ROUNDED, null) == '1')
5468		{
5469			styleSelect.value = 'rounded';
5470		}
5471
5472		if (mxUtils.getValue(ss.style, mxConstants.STYLE_DASHED, null) == '1')
5473		{
5474			if (mxUtils.getValue(ss.style, mxConstants.STYLE_DASH_PATTERN, null) == null)
5475			{
5476				solid.style.borderBottom = '1px dashed ' + this.defaultStrokeColor;
5477			}
5478			else
5479			{
5480				solid.style.borderBottom = '1px dotted ' + this.defaultStrokeColor;
5481			}
5482		}
5483		else
5484		{
5485			solid.style.borderBottom = '1px solid ' + this.defaultStrokeColor;
5486		}
5487
5488		altSolid.style.borderBottom = solid.style.borderBottom;
5489
5490		// Updates toolbar icon for edge style
5491		var edgeStyleDiv = edgeStyle.getElementsByTagName('div')[0];
5492
5493		if (edgeStyleDiv != null)
5494		{
5495			var es = mxUtils.getValue(ss.style, mxConstants.STYLE_EDGE, null);
5496
5497			if (mxUtils.getValue(ss.style, mxConstants.STYLE_NOEDGESTYLE, null) == '1')
5498			{
5499				es = null;
5500			}
5501
5502			if (es == 'orthogonalEdgeStyle' && mxUtils.getValue(ss.style, mxConstants.STYLE_CURVED, null) == '1')
5503			{
5504				edgeStyleDiv.className = 'geSprite geSprite-curved';
5505			}
5506			else if (es == 'straight' || es == 'none' || es == null)
5507			{
5508				edgeStyleDiv.className = 'geSprite geSprite-straight';
5509			}
5510			else if (es == 'entityRelationEdgeStyle')
5511			{
5512				edgeStyleDiv.className = 'geSprite geSprite-entity';
5513			}
5514			else if (es == 'elbowEdgeStyle')
5515			{
5516				edgeStyleDiv.className = 'geSprite ' + ((mxUtils.getValue(ss.style,
5517					mxConstants.STYLE_ELBOW, null) == 'vertical') ?
5518					'geSprite-verticalelbow' : 'geSprite-horizontalelbow');
5519			}
5520			else if (es == 'isometricEdgeStyle')
5521			{
5522				edgeStyleDiv.className = 'geSprite ' + ((mxUtils.getValue(ss.style,
5523					mxConstants.STYLE_ELBOW, null) == 'vertical') ?
5524					'geSprite-verticalisometric' : 'geSprite-horizontalisometric');
5525			}
5526			else
5527			{
5528				edgeStyleDiv.className = 'geSprite geSprite-orthogonal';
5529			}
5530		}
5531
5532		// Updates icon for edge shape
5533		var edgeShapeDiv = edgeShape.getElementsByTagName('div')[0];
5534
5535		if (edgeShapeDiv != null)
5536		{
5537			if (ss.style.shape == 'link')
5538			{
5539				edgeShapeDiv.className = 'geSprite geSprite-linkedge';
5540			}
5541			else if (ss.style.shape == 'flexArrow')
5542			{
5543				edgeShapeDiv.className = 'geSprite geSprite-arrow';
5544			}
5545			else if (ss.style.shape == 'arrow')
5546			{
5547				edgeShapeDiv.className = 'geSprite geSprite-simplearrow';
5548			}
5549			else
5550			{
5551				edgeShapeDiv.className = 'geSprite geSprite-connection';
5552			}
5553		}
5554
5555		if (ss.edges.length == ss.cells.length)
5556		{
5557			altStylePanel.style.display = '';
5558			stylePanel.style.display = 'none';
5559		}
5560		else
5561		{
5562			altStylePanel.style.display = 'none';
5563			stylePanel.style.display = '';
5564		}
5565
5566		if (Graph.lineJumpsEnabled && ss.edges.length > 0 &&
5567			ss.vertices.length == 0 && ss.lineJumps)
5568		{
5569			container.style.borderBottomStyle = 'none';
5570		}
5571
5572		function updateArrow(marker, fill, elt, prefix)
5573		{
5574			var markerDiv = elt.getElementsByTagName('div')[0];
5575
5576			if (markerDiv != null)
5577			{
5578				markerDiv.className = ui.getCssClassForMarker(prefix, ss.style.shape, marker, fill);
5579				markerDiv.nextSibling.style.marginLeft = '1px';
5580				markerDiv.nextSibling.style.paddingRight = '5px';
5581
5582				if (markerDiv.className == 'geSprite geSprite-noarrow')
5583				{
5584					markerDiv.innerHTML = mxUtils.htmlEntities(mxResources.get('none'));
5585					markerDiv.style.backgroundImage = 'none';
5586					markerDiv.style.verticalAlign = 'top';
5587					markerDiv.style.marginTop = '4px';
5588					markerDiv.style.fontSize = '10px';
5589					markerDiv.style.filter = 'none';
5590					markerDiv.style.color = this.defaultStrokeColor;
5591					markerDiv.nextSibling.style.marginTop = '0px';
5592				}
5593				else
5594				{
5595					markerDiv.nextSibling.style.position = 'relative';
5596					markerDiv.nextSibling.style.top = '-2px';
5597				}
5598			}
5599
5600			return markerDiv;
5601		};
5602
5603		var sourceDiv = updateArrow(mxUtils.getValue(ss.style, mxConstants.STYLE_STARTARROW, null),
5604				mxUtils.getValue(ss.style, 'startFill', '1'), lineStart, 'start');
5605		var targetDiv = updateArrow(mxUtils.getValue(ss.style, mxConstants.STYLE_ENDARROW, null),
5606				mxUtils.getValue(ss.style, 'endFill', '1'), lineEnd, 'end');
5607
5608		// Special cases for markers
5609		if (sourceDiv != null && targetDiv != null)
5610		{
5611			if (ss.style.shape == 'arrow')
5612			{
5613				sourceDiv.className = 'geSprite geSprite-noarrow';
5614				targetDiv.className = 'geSprite geSprite-endblocktrans';
5615			}
5616			else if (ss.style.shape == 'link')
5617			{
5618				sourceDiv.className = 'geSprite geSprite-noarrow';
5619				targetDiv.className = 'geSprite geSprite-noarrow';
5620			}
5621		}
5622
5623		mxUtils.setOpacity(edgeStyle, (ss.style.shape == 'arrow') ? 30 : 100);
5624
5625		if (ss.style.shape != 'connector' && ss.style.shape != 'flexArrow' && ss.style.shape != 'filledEdge')
5626		{
5627			mxUtils.setOpacity(lineStart, 30);
5628			mxUtils.setOpacity(lineEnd, 30);
5629		}
5630		else
5631		{
5632			mxUtils.setOpacity(lineStart, 100);
5633			mxUtils.setOpacity(lineEnd, 100);
5634		}
5635
5636		if (force || document.activeElement != startSize)
5637		{
5638			var tmp = parseInt(mxUtils.getValue(ss.style, mxConstants.STYLE_STARTSIZE, mxConstants.DEFAULT_MARKERSIZE));
5639			startSize.value = (isNaN(tmp)) ? '' : tmp  + ' pt';
5640		}
5641
5642		if (force || document.activeElement != startSpacing)
5643		{
5644			var tmp = parseInt(mxUtils.getValue(ss.style, mxConstants.STYLE_SOURCE_PERIMETER_SPACING, 0));
5645			startSpacing.value = (isNaN(tmp)) ? '' : tmp  + ' pt';
5646		}
5647
5648		if (force || document.activeElement != endSize)
5649		{
5650			var tmp = parseInt(mxUtils.getValue(ss.style, mxConstants.STYLE_ENDSIZE, mxConstants.DEFAULT_MARKERSIZE));
5651			endSize.value = (isNaN(tmp)) ? '' : tmp  + ' pt';
5652		}
5653
5654		if (force || document.activeElement != startSpacing)
5655		{
5656			var tmp = parseInt(mxUtils.getValue(ss.style, mxConstants.STYLE_TARGET_PERIMETER_SPACING, 0));
5657			endSpacing.value = (isNaN(tmp)) ? '' : tmp  + ' pt';
5658		}
5659
5660		if (force || document.activeElement != perimeterSpacing)
5661		{
5662			var tmp = parseInt(mxUtils.getValue(ss.style, mxConstants.STYLE_PERIMETER_SPACING, 0));
5663			perimeterSpacing.value = (isNaN(tmp)) ? '' : tmp  + ' pt';
5664		}
5665	});
5666
5667	startSizeUpdate = this.installInputHandler(startSize, mxConstants.STYLE_STARTSIZE, mxConstants.DEFAULT_MARKERSIZE, 0, 999, ' pt');
5668	startSpacingUpdate = this.installInputHandler(startSpacing, mxConstants.STYLE_SOURCE_PERIMETER_SPACING, 0, -999, 999, ' pt');
5669	endSizeUpdate = this.installInputHandler(endSize, mxConstants.STYLE_ENDSIZE, mxConstants.DEFAULT_MARKERSIZE, 0, 999, ' pt');
5670	endSpacingUpdate = this.installInputHandler(endSpacing, mxConstants.STYLE_TARGET_PERIMETER_SPACING, 0, -999, 999, ' pt');
5671	perimeterUpdate = this.installInputHandler(perimeterSpacing, mxConstants.STYLE_PERIMETER_SPACING, 0, 0, 999, ' pt');
5672
5673	this.addKeyHandler(input, listener);
5674	this.addKeyHandler(startSize, listener);
5675	this.addKeyHandler(startSpacing, listener);
5676	this.addKeyHandler(endSize, listener);
5677	this.addKeyHandler(endSpacing, listener);
5678	this.addKeyHandler(perimeterSpacing, listener);
5679
5680	graph.getModel().addListener(mxEvent.CHANGE, listener);
5681	this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }});
5682	listener();
5683
5684	return container;
5685};
5686
5687/**
5688 * Adds UI for configuring line jumps.
5689 */
5690StyleFormatPanel.prototype.addLineJumps = function(container)
5691{
5692	var ss = this.format.getSelectionState();
5693
5694	if (Graph.lineJumpsEnabled && ss.edges.length > 0 &&
5695		ss.vertices.length == 0 && ss.lineJumps)
5696	{
5697		container.style.padding = '2px 0px 24px 14px';
5698
5699		var ui = this.editorUi;
5700		var editor = ui.editor;
5701		var graph = editor.graph;
5702
5703		var span = document.createElement('div');
5704		span.style.position = 'absolute';
5705		span.style.maxWidth = '82px';
5706		span.style.overflow = 'hidden';
5707		span.style.textOverflow = 'ellipsis';
5708
5709		mxUtils.write(span, mxResources.get('lineJumps'));
5710		container.appendChild(span);
5711
5712		var styleSelect = document.createElement('select');
5713		styleSelect.style.position = 'absolute';
5714		styleSelect.style.height = '21px';
5715		styleSelect.style.padding = '0px';
5716		styleSelect.style.marginTop = '-2px';
5717		styleSelect.style.boxSizing = 'border-box';
5718		styleSelect.style.right = '76px';
5719		styleSelect.style.width = '54px';
5720		styleSelect.style.border = '1px solid rgb(160, 160, 160)';
5721		styleSelect.style.borderRadius = '4px';
5722
5723		var styles = ['none', 'arc', 'gap', 'sharp', 'line'];
5724
5725		for (var i = 0; i < styles.length; i++)
5726		{
5727			var styleOption = document.createElement('option');
5728			styleOption.setAttribute('value', styles[i]);
5729			mxUtils.write(styleOption, mxResources.get(styles[i]));
5730			styleSelect.appendChild(styleOption);
5731		}
5732
5733		mxEvent.addListener(styleSelect, 'change', function(evt)
5734		{
5735			graph.getModel().beginUpdate();
5736			try
5737			{
5738				graph.setCellStyles('jumpStyle', styleSelect.value, ss.cells);
5739				ui.fireEvent(new mxEventObject('styleChanged', 'keys', ['jumpStyle'],
5740					'values', [styleSelect.value], 'cells', ss.cells));
5741			}
5742			finally
5743			{
5744				graph.getModel().endUpdate();
5745			}
5746
5747			mxEvent.consume(evt);
5748		});
5749
5750		// Stops events from bubbling to color option event handler
5751		mxEvent.addListener(styleSelect, 'click', function(evt)
5752		{
5753			mxEvent.consume(evt);
5754		});
5755
5756		container.appendChild(styleSelect);
5757
5758		var jumpSizeUpdate;
5759
5760		var jumpSize = this.addUnitInput(container, 'pt', 16, 42, function()
5761		{
5762			jumpSizeUpdate.apply(this, arguments);
5763		});
5764
5765		jumpSizeUpdate = this.installInputHandler(jumpSize, 'jumpSize',
5766			Graph.defaultJumpSize, 0, 999, ' pt');
5767
5768		var listener = mxUtils.bind(this, function(sender, evt, force)
5769		{
5770			ss = this.format.getSelectionState();
5771			styleSelect.value = mxUtils.getValue(ss.style, 'jumpStyle', 'none');
5772
5773			if (force || document.activeElement != jumpSize)
5774			{
5775				var tmp = parseInt(mxUtils.getValue(ss.style, 'jumpSize', Graph.defaultJumpSize));
5776				jumpSize.value = (isNaN(tmp)) ? '' : tmp  + ' pt';
5777			}
5778		});
5779
5780		this.addKeyHandler(jumpSize, listener);
5781
5782		graph.getModel().addListener(mxEvent.CHANGE, listener);
5783		this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }});
5784		listener();
5785	}
5786	else
5787	{
5788		container.style.display = 'none';
5789	}
5790
5791	return container;
5792};
5793
5794/**
5795 * Adds the label menu items to the given menu and parent.
5796 */
5797StyleFormatPanel.prototype.addEffects = function(div)
5798{
5799	var ui = this.editorUi;
5800	var editor = ui.editor;
5801	var graph = editor.graph;
5802	var ss = this.format.getSelectionState();
5803
5804	div.style.paddingTop = '4px';
5805	div.style.paddingBottom = '0px';
5806
5807	var table = document.createElement('table');
5808
5809	table.style.width = '210px';
5810	table.style.fontWeight = 'bold';
5811	table.style.tableLayout = 'fixed';
5812	var tbody = document.createElement('tbody');
5813	var row = document.createElement('tr');
5814	row.style.padding = '0px';
5815	var left = document.createElement('td');
5816	left.style.padding = '0px';
5817	left.style.width = '50%';
5818	left.setAttribute('valign', 'top');
5819
5820	var right = left.cloneNode(true);
5821	right.style.paddingLeft = '8px';
5822	row.appendChild(left);
5823	row.appendChild(right);
5824	tbody.appendChild(row);
5825	table.appendChild(tbody);
5826	div.appendChild(table);
5827
5828	var current = left;
5829	var count = 0;
5830
5831	var addOption = mxUtils.bind(this, function(label, key, defaultValue)
5832	{
5833		var opt = this.createCellOption(label, key, defaultValue);
5834		opt.style.width = '100%';
5835		current.appendChild(opt);
5836		current = (current == left) ? right : left;
5837		count++;
5838	});
5839
5840	var listener = mxUtils.bind(this, function(sender, evt, force)
5841	{
5842		ss = this.format.getSelectionState();
5843
5844		left.innerHTML = '';
5845		right.innerHTML = '';
5846		current = left;
5847
5848		if (ss.rounded)
5849		{
5850			addOption(mxResources.get('rounded'), mxConstants.STYLE_ROUNDED, 0);
5851		}
5852
5853		if (ss.style.shape == 'swimlane')
5854		{
5855			addOption(mxResources.get('divider'), 'swimlaneLine', 1);
5856		}
5857
5858		if (!ss.containsImage)
5859		{
5860			addOption(mxResources.get('shadow'), mxConstants.STYLE_SHADOW, 0);
5861		}
5862
5863		if (ss.glass)
5864		{
5865			addOption(mxResources.get('glass'), mxConstants.STYLE_GLASS, 0);
5866		}
5867
5868		addOption(mxResources.get('sketch'), 'sketch', 0);
5869	});
5870
5871	graph.getModel().addListener(mxEvent.CHANGE, listener);
5872	this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }});
5873	listener();
5874
5875	return div;
5876}
5877
5878/**
5879 * Adds the label menu items to the given menu and parent.
5880 */
5881StyleFormatPanel.prototype.addStyleOps = function(div)
5882{
5883	div.style.paddingTop = '10px';
5884	div.style.paddingBottom = '10px';
5885
5886	var btn = mxUtils.button(mxResources.get('setAsDefaultStyle'), mxUtils.bind(this, function(evt)
5887	{
5888		this.editorUi.actions.get('setAsDefaultStyle').funct();
5889	}));
5890
5891	btn.setAttribute('title', mxResources.get('setAsDefaultStyle') + ' (' + this.editorUi.actions.get('setAsDefaultStyle').shortcut + ')');
5892	btn.style.width = '210px';
5893	div.appendChild(btn);
5894
5895	return div;
5896};
5897
5898/**
5899 * Adds the label menu items to the given menu and parent.
5900 */
5901DiagramStylePanel = function(format, editorUi, container)
5902{
5903	BaseFormatPanel.call(this, format, editorUi, container);
5904	this.init();
5905};
5906
5907mxUtils.extend(DiagramStylePanel, BaseFormatPanel);
5908
5909/**
5910 * Adds the label menu items to the given menu and parent.
5911 */
5912DiagramStylePanel.prototype.init = function()
5913{
5914	var ui = this.editorUi;
5915	var editor = ui.editor;
5916	var graph = editor.graph;
5917
5918	this.darkModeChangedListener = mxUtils.bind(this, function()
5919	{
5920		this.format.cachedStyleEntries = [];
5921	});
5922
5923	ui.addListener('darkModeChanged', this.darkModeChangedListener);
5924	this.container.appendChild(this.addView(this.createPanel()));
5925};
5926
5927/**
5928 * Adds the label menu items to the given menu and parent.
5929 */
5930DiagramStylePanel.prototype.addView = function(div)
5931{
5932	var ui = this.editorUi;
5933	var editor = ui.editor;
5934	var graph = editor.graph;
5935	var model = graph.getModel();
5936
5937	div.style.whiteSpace = 'normal';
5938
5939	var sketch = graph.currentVertexStyle['sketch'] == '1' && graph.currentEdgeStyle['sketch'] == '1';
5940	var rounded = graph.currentVertexStyle['rounded'] == '1';
5941	var curved = graph.currentEdgeStyle['curved'] == '1';
5942
5943	var opts = document.createElement('div');
5944	opts.style.paddingBottom = '12px';
5945	opts.style.marginRight = '16px';
5946	div.style.paddingTop = '8px';
5947
5948	var table = document.createElement('table');
5949
5950	table.style.width = '210px';
5951	table.style.fontWeight = 'bold';
5952
5953	var tbody = document.createElement('tbody');
5954	var row = document.createElement('tr');
5955	row.style.padding = '0px';
5956
5957	var left = document.createElement('td');
5958	left.style.padding = '0px';
5959	left.style.width = '50%';
5960	left.setAttribute('valign', 'middle');
5961
5962	var right = left.cloneNode(true);
5963	right.style.paddingLeft = '8px';
5964	row.appendChild(left);
5965	row.appendChild(right);
5966	tbody.appendChild(row);
5967	table.appendChild(tbody);
5968
5969	// Sketch
5970	left.appendChild(this.createOption(mxResources.get('sketch'), function()
5971	{
5972		return sketch;
5973	}, function(checked)
5974	{
5975		sketch = checked;
5976
5977		if (checked)
5978		{
5979			graph.currentEdgeStyle['sketch'] = '1';
5980			graph.currentVertexStyle['sketch'] = '1';
5981		}
5982		else
5983		{
5984			delete graph.currentEdgeStyle['sketch'];
5985			delete graph.currentVertexStyle['sketch'];
5986		}
5987
5988		graph.updateCellStyles('sketch', (checked) ? '1' : null, graph.getVerticesAndEdges());
5989	}, null, function(div)
5990	{
5991		div.style.width = 'auto';
5992	}));
5993
5994	// Rounded
5995	right.appendChild(this.createOption(mxResources.get('rounded'), function()
5996	{
5997		return rounded;
5998	}, function(checked)
5999	{
6000		rounded = checked;
6001
6002		if (checked)
6003		{
6004			graph.currentEdgeStyle['rounded'] = '1';
6005			graph.currentVertexStyle['rounded'] = '1';
6006		}
6007		else
6008		{
6009			delete graph.currentEdgeStyle['rounded'];
6010			delete graph.currentVertexStyle['rounded'];
6011		}
6012
6013		graph.updateCellStyles('rounded', (checked) ? '1' : '0', graph.getVerticesAndEdges());
6014	}, null, function(div)
6015	{
6016		div.style.width = 'auto';
6017	}));
6018
6019	// Curved
6020	left = left.cloneNode(false);
6021	right = right.cloneNode(false);
6022	row = row.cloneNode(false);
6023	row.appendChild(left);
6024	row.appendChild(right);
6025	tbody.appendChild(row);
6026
6027	left.appendChild(this.createOption(mxResources.get('curved'), function()
6028	{
6029		return curved;
6030	}, function(checked)
6031	{
6032		curved = checked;
6033
6034		if (checked)
6035		{
6036			graph.currentEdgeStyle['curved'] = '1';
6037		}
6038		else
6039		{
6040			delete graph.currentEdgeStyle['curved'];
6041		}
6042
6043		graph.updateCellStyles('curved', (checked) ? '1' : null, graph.getVerticesAndEdges(false, true));
6044	}, null, function(div)
6045	{
6046		div.style.width = 'auto';
6047	}));
6048
6049	opts.appendChild(table);
6050	div.appendChild(opts);
6051
6052	var defaultStyles = ['fillColor', 'strokeColor', 'fontColor', 'gradientColor'];
6053
6054	var updateCells = mxUtils.bind(this, function(styles, graphStyle)
6055	{
6056		var cells = graph.getVerticesAndEdges();
6057
6058		model.beginUpdate();
6059		try
6060		{
6061			for (var i = 0; i < cells.length; i++)
6062			{
6063				var style = graph.getCellStyle(cells[i]);
6064
6065				// Handles special label background color
6066				if (style['labelBackgroundColor'] != null)
6067				{
6068					graph.updateCellStyles('labelBackgroundColor', (graphStyle != null) ?
6069						graphStyle.background : null, [cells[i]]);
6070				}
6071
6072				var edge = model.isEdge(cells[i]);
6073				var newStyle = model.getStyle(cells[i]);
6074				var current = (edge) ? graph.currentEdgeStyle : graph.currentVertexStyle;
6075
6076				for (var j = 0; j < styles.length; j++)
6077				{
6078					if ((style[styles[j]] != null && style[styles[j]] != mxConstants.NONE) ||
6079						(styles[j] != mxConstants.STYLE_FILLCOLOR &&
6080						styles[j] != mxConstants.STYLE_STROKECOLOR))
6081					{
6082						newStyle = mxUtils.setStyle(newStyle, styles[j], current[styles[j]]);
6083					}
6084				}
6085
6086				model.setStyle(cells[i], newStyle);
6087			}
6088		}
6089		finally
6090		{
6091			model.endUpdate();
6092		}
6093	});
6094
6095	var removeStyles = mxUtils.bind(this, function(style, styles, defaultStyle)
6096	{
6097		if (style != null)
6098		{
6099			for (var j = 0; j < styles.length; j++)
6100			{
6101				if (((style[styles[j]] != null &&
6102					style[styles[j]] != mxConstants.NONE) ||
6103					(styles[j] != mxConstants.STYLE_FILLCOLOR &&
6104					styles[j] != mxConstants.STYLE_STROKECOLOR)))
6105				{
6106					style[styles[j]] = defaultStyle[styles[j]];
6107				}
6108			}
6109		}
6110	});
6111
6112	var applyStyle = mxUtils.bind(this, function(style, result, cell, graphStyle, theGraph)
6113	{
6114		if (style != null)
6115		{
6116			if (cell != null)
6117			{
6118				// Handles special label background color
6119				if (result['labelBackgroundColor'] != null)
6120				{
6121					var bg = (graphStyle != null) ? graphStyle.background : null;
6122					theGraph = (theGraph != null) ? theGraph : graph;
6123
6124					if (bg == null)
6125					{
6126						bg = theGraph.background;
6127					}
6128
6129					if (bg == null)
6130					{
6131						bg = theGraph.defaultPageBackgroundColor;
6132					}
6133
6134					result['labelBackgroundColor'] = bg;
6135				}
6136			}
6137
6138			for (var key in style)
6139			{
6140				if (cell == null || ((result[key] != null &&
6141					result[key] != mxConstants.NONE) ||
6142					(key != mxConstants.STYLE_FILLCOLOR &&
6143					key != mxConstants.STYLE_STROKECOLOR)))
6144				{
6145					result[key] = style[key];
6146				}
6147			}
6148		}
6149	});
6150
6151	var btn = mxUtils.button(mxResources.get('reset'), mxUtils.bind(this, function(evt)
6152	{
6153		var all = graph.getVerticesAndEdges(true, true);
6154
6155		if (all.length > 0)
6156		{
6157			model.beginUpdate();
6158			try
6159			{
6160				graph.updateCellStyles('sketch', null, all);
6161				graph.updateCellStyles('rounded', null, all);
6162				graph.updateCellStyles('curved', null, graph.getVerticesAndEdges(false, true));
6163			}
6164			finally
6165			{
6166				model.endUpdate();
6167			}
6168		}
6169
6170		ui.clearDefaultStyle();
6171	}));
6172
6173	btn.setAttribute('title', mxResources.get('reset'));
6174	btn.style.textOverflow = 'ellipsis';
6175	btn.style.maxWidth = '90px';
6176	right.appendChild(btn);
6177
6178	var createPreview = mxUtils.bind(this, function(commonStyle, vertexStyle, edgeStyle, graphStyle, container)
6179	{
6180		// Wrapper needed to catch events
6181		var div = document.createElement('div');
6182		div.style.position = 'absolute';
6183		div.style.display = 'inline-block';
6184		div.style.overflow = 'hidden';
6185		div.style.pointerEvents = 'none';
6186		div.style.width = '100%';
6187		div.style.height = '100%';
6188		container.appendChild(div);
6189
6190		var graph2 = new Graph(div, null, null, graph.getStylesheet());
6191
6192		graph2.resetViewOnRootChange = false;
6193		graph2.foldingEnabled = false;
6194		graph2.gridEnabled = false;
6195		graph2.autoScroll = false;
6196		graph2.setTooltips(false);
6197		graph2.setConnectable(false);
6198		graph2.setPanning(false);
6199		graph2.setEnabled(false);
6200
6201		graph2.getCellStyle = function(cell)
6202		{
6203			var result = mxUtils.clone(Graph.prototype.getCellStyle.apply(this, arguments));
6204			var defaultStyle = graph.stylesheet.getDefaultVertexStyle();
6205			var appliedStyle = vertexStyle;
6206
6207			if (model.isEdge(cell))
6208			{
6209				defaultStyle = graph.stylesheet.getDefaultEdgeStyle();
6210				appliedStyle = edgeStyle;
6211			}
6212
6213			removeStyles(result, defaultStyles, defaultStyle);
6214			applyStyle(commonStyle, result, cell, graphStyle, graph2);
6215			applyStyle(appliedStyle, result, cell, graphStyle, graph2);
6216
6217			if (result != null)
6218			{
6219				result = this.postProcessCellStyle(result);
6220			}
6221
6222			return result;
6223		};
6224
6225		// Avoid HTML labels to capture events in bubble phase
6226		graph2.model.beginUpdate();
6227		try
6228		{
6229			var v1 = graph2.insertVertex(graph2.getDefaultParent(), null, 'Shape', 14, 8, 70, 40, 'strokeWidth=2;');
6230			var e1 = graph2.insertEdge(graph2.getDefaultParent(), null, 'Connector', v1, v1,
6231				'edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;endSize=5;strokeWidth=2;')
6232			e1.geometry.points = [new mxPoint(32, 70)];
6233			e1.geometry.offset = new mxPoint(0, 8);
6234		}
6235		finally
6236		{
6237			graph2.model.endUpdate();
6238		}
6239	});
6240
6241	// Entries
6242	var entries = document.createElement('div');
6243	entries.style.position = 'relative';
6244	div.appendChild(entries);
6245
6246	// Cached entries
6247	if (this.format.cachedStyleEntries == null)
6248	{
6249		this.format.cachedStyleEntries = [];
6250	}
6251
6252	var addEntry = mxUtils.bind(this, function(commonStyle, vertexStyle, edgeStyle, graphStyle, index)
6253	{
6254		var panel = this.format.cachedStyleEntries[index];
6255
6256		if (panel == null)
6257		{
6258			panel = document.createElement('div');
6259			panel.style.display = 'inline-block';
6260			panel.style.position = 'relative';
6261			panel.style.width = '96px';
6262			panel.style.height = '90px';
6263			panel.style.cursor = 'pointer';
6264			panel.style.border = '1px solid gray';
6265			panel.style.borderRadius = '8px';
6266			panel.style.margin = '2px';
6267			panel.style.overflow = 'hidden';
6268
6269			if (graphStyle != null && graphStyle.background != null)
6270			{
6271				panel.style.backgroundColor = graphStyle.background;
6272			}
6273
6274			createPreview(commonStyle, vertexStyle, edgeStyle, graphStyle, panel);
6275
6276			mxEvent.addGestureListeners(panel, mxUtils.bind(this, function(evt)
6277			{
6278				panel.style.opacity = 0.5;
6279			}), null, mxUtils.bind(this, function(evt)
6280			{
6281				panel.style.opacity = 1;
6282				graph.currentVertexStyle = mxUtils.clone(graph.defaultVertexStyle);
6283				graph.currentEdgeStyle = mxUtils.clone(graph.defaultEdgeStyle);
6284
6285				applyStyle(commonStyle, graph.currentVertexStyle);
6286				applyStyle(commonStyle, graph.currentEdgeStyle);
6287				applyStyle(vertexStyle, graph.currentVertexStyle);
6288				applyStyle(edgeStyle, graph.currentEdgeStyle);
6289
6290				if (sketch)
6291				{
6292					graph.currentEdgeStyle['sketch'] = '1';
6293					graph.currentVertexStyle['sketch'] = '1';
6294				}
6295				else
6296				{
6297					graph.currentEdgeStyle['sketch'] = '0';
6298					graph.currentVertexStyle['sketch'] = '0';
6299				}
6300
6301				if (rounded)
6302				{
6303					graph.currentVertexStyle['rounded'] = '1';
6304					graph.currentEdgeStyle['rounded'] = '1';
6305				}
6306				else
6307				{
6308					graph.currentVertexStyle['rounded'] = '0';
6309					graph.currentEdgeStyle['rounded'] = '1';
6310				}
6311
6312				if (curved)
6313				{
6314					graph.currentEdgeStyle['curved'] = '1';
6315				}
6316				else
6317				{
6318					graph.currentEdgeStyle['curved'] = '0';
6319				}
6320
6321				model.beginUpdate();
6322				try
6323				{
6324					updateCells(defaultStyles, graphStyle);
6325
6326					var change = new ChangePageSetup(ui, (graphStyle != null) ? graphStyle.background : null);
6327					change.ignoreImage = true;
6328					model.execute(change);
6329
6330					model.execute(new ChangeGridColor(ui, (graphStyle != null && graphStyle.gridColor != null) ?
6331						graphStyle.gridColor : graph.view.defaultGridColor));
6332				}
6333				finally
6334				{
6335					model.endUpdate();
6336				}
6337			}));
6338
6339			mxEvent.addListener(panel, 'mouseenter', mxUtils.bind(this, function(evt)
6340			{
6341				var prev = graph.getCellStyle;
6342				var prevBg = graph.background;
6343				var prevGrid = graph.view.gridColor;
6344
6345				graph.background = (graphStyle != null) ? graphStyle.background : null;
6346				graph.view.gridColor = (graphStyle != null && graphStyle.gridColor != null) ?
6347					graphStyle.gridColor : graph.view.defaultGridColor;
6348
6349				graph.getCellStyle = function(cell)
6350				{
6351					var result = mxUtils.clone(prev.apply(this, arguments));
6352
6353					var defaultStyle = graph.stylesheet.getDefaultVertexStyle();
6354					var appliedStyle = vertexStyle;
6355
6356					if (model.isEdge(cell))
6357					{
6358						defaultStyle = graph.stylesheet.getDefaultEdgeStyle();
6359						appliedStyle = edgeStyle;
6360					}
6361
6362					removeStyles(result, defaultStyles, defaultStyle);
6363					applyStyle(commonStyle, result, cell, graphStyle);
6364					applyStyle(appliedStyle, result, cell, graphStyle);
6365
6366					if (result != null)
6367					{
6368						result = this.postProcessCellStyle(result);
6369					}
6370
6371					return result;
6372				};
6373
6374				graph.refresh();
6375				graph.getCellStyle = prev;
6376				graph.background = prevBg;
6377				graph.view.gridColor = prevGrid;
6378			}));
6379
6380			mxEvent.addListener(panel, 'mouseleave', mxUtils.bind(this, function(evt)
6381			{
6382				graph.refresh();
6383			}));
6384
6385			// Workaround for broken cache in IE11
6386			if (!mxClient.IS_IE && !mxClient.IS_IE11)
6387			{
6388				this.format.cachedStyleEntries[index] = panel;
6389			}
6390		}
6391
6392		entries.appendChild(panel);
6393	});
6394
6395	// Maximum palettes to switch the switcher
6396	var maxEntries = 10;
6397	var pageCount = Math.ceil(Editor.styles.length / maxEntries);
6398	this.format.currentStylePage = (this.format.currentStylePage != null) ? this.format.currentStylePage : 0;
6399	var dots = [];
6400
6401	var addEntries = mxUtils.bind(this, function()
6402	{
6403		if (dots.length > 0)
6404		{
6405			dots[this.format.currentStylePage].style.background = '#84d7ff';
6406		}
6407
6408		for (var i = this.format.currentStylePage * maxEntries;
6409			i < Math.min((this.format.currentStylePage + 1) * maxEntries,
6410			Editor.styles.length); i++)
6411		{
6412			var s = Editor.styles[i];
6413			addEntry(s.commonStyle, s.vertexStyle, s.edgeStyle, s.graph, i);
6414		}
6415	});
6416
6417	var selectPage = mxUtils.bind(this, function(index)
6418	{
6419		if (index >= 0 && index < pageCount)
6420		{
6421			dots[this.format.currentStylePage].style.background = 'transparent';
6422			entries.innerHTML = '';
6423			this.format.currentStylePage = index;
6424			addEntries();
6425		}
6426	});
6427
6428	if (pageCount > 1)
6429	{
6430		// Selector
6431		var switcher = document.createElement('div');
6432		switcher.style.whiteSpace = 'nowrap';
6433		switcher.style.position = 'relative';
6434		switcher.style.textAlign = 'center';
6435		switcher.style.paddingTop = '4px';
6436		switcher.style.width = '210px';
6437
6438		div.style.paddingBottom = '8px';
6439
6440		for (var i = 0; i < pageCount; i++)
6441		{
6442			var dot = document.createElement('div');
6443			dot.style.display = 'inline-block';
6444			dot.style.width = '6px';
6445			dot.style.height = '6px';
6446			dot.style.marginLeft = '4px';
6447			dot.style.marginRight = '3px';
6448			dot.style.borderRadius = '3px';
6449			dot.style.cursor = 'pointer';
6450			dot.style.background = 'transparent';
6451			dot.style.border = '1px solid #b5b6b7';
6452
6453			(mxUtils.bind(this, function(index, elt)
6454			{
6455				mxEvent.addListener(dot, 'click', mxUtils.bind(this, function()
6456				{
6457					selectPage(index);
6458				}));
6459			}))(i, dot);
6460
6461			switcher.appendChild(dot);
6462			dots.push(dot);
6463		}
6464
6465		div.appendChild(switcher);
6466		addEntries();
6467
6468		if (pageCount < 15)
6469		{
6470			var left = document.createElement('div');
6471			left.style.position = 'absolute';
6472			left.style.left = '0px';
6473			left.style.top = '0px';
6474			left.style.bottom = '0px';
6475			left.style.width = '24px';
6476			left.style.height = '24px';
6477			left.style.margin = '0px';
6478			left.style.cursor = 'pointer';
6479			left.style.opacity = '0.5';
6480			left.style.backgroundRepeat = 'no-repeat';
6481			left.style.backgroundPosition = 'center center';
6482			left.style.backgroundSize = '24px 24px';
6483			left.style.backgroundImage = 'url(' + Editor.previousImage + ')';
6484
6485			if (Editor.isDarkMode())
6486			{
6487				left.style.filter = 'invert(100%)';
6488			}
6489
6490			var right = left.cloneNode(false);
6491			right.style.backgroundImage = 'url(' + Editor.nextImage + ')';
6492			right.style.left = '';
6493			right.style.right = '2px';
6494
6495			switcher.appendChild(left);
6496			switcher.appendChild(right);
6497
6498			mxEvent.addListener(left, 'click', mxUtils.bind(this, function()
6499			{
6500				selectPage(mxUtils.mod(this.format.currentStylePage - 1, pageCount));
6501			}));
6502
6503			mxEvent.addListener(right, 'click', mxUtils.bind(this, function()
6504			{
6505				selectPage(mxUtils.mod(this.format.currentStylePage + 1, pageCount));
6506			}));
6507
6508			// Hover state
6509			function addHoverState(elt)
6510			{
6511				mxEvent.addListener(elt, 'mouseenter', function()
6512				{
6513					elt.style.opacity = '1';
6514				});
6515				mxEvent.addListener(elt, 'mouseleave', function()
6516				{
6517					elt.style.opacity = '0.5';
6518				});
6519			};
6520
6521			addHoverState(left);
6522			addHoverState(right);
6523		}
6524	}
6525	else
6526	{
6527		addEntries();
6528	}
6529
6530	return div;
6531};
6532
6533/**
6534 * Adds the label menu items to the given menu and parent.
6535 */
6536 DiagramStylePanel.prototype.destroy = function()
6537 {
6538	 BaseFormatPanel.prototype.destroy.apply(this, arguments);
6539
6540	 if (this.darkModeChangedListener)
6541	 {
6542		 this.editorUi.removeListener(this.darkModeChangedListener);
6543		 this.darkModeChangedListener = null;
6544	 }
6545 };
6546
6547/**
6548 * Adds the label menu items to the given menu and parent.
6549 */
6550DiagramFormatPanel = function(format, editorUi, container)
6551{
6552	BaseFormatPanel.call(this, format, editorUi, container);
6553	this.init();
6554};
6555
6556mxUtils.extend(DiagramFormatPanel, BaseFormatPanel);
6557
6558/**
6559 * Switch to disable page view.
6560 */
6561DiagramFormatPanel.showPageView = true;
6562
6563/**
6564 * Specifies if the background image option should be shown. Default is true.
6565 */
6566DiagramFormatPanel.prototype.showBackgroundImageOption = true;
6567
6568/**
6569 * Adds the label menu items to the given menu and parent.
6570 */
6571DiagramFormatPanel.prototype.init = function()
6572{
6573	var ui = this.editorUi;
6574	var editor = ui.editor;
6575	var graph = editor.graph;
6576
6577	this.container.appendChild(this.addView(this.createPanel()));
6578
6579	if (graph.isEnabled())
6580	{
6581		this.container.appendChild(this.addOptions(this.createPanel()));
6582		this.container.appendChild(this.addPaperSize(this.createPanel()));
6583		this.container.appendChild(this.addStyleOps(this.createPanel()));
6584	}
6585};
6586
6587/**
6588 * Adds the label menu items to the given menu and parent.
6589 */
6590DiagramFormatPanel.prototype.addView = function(div)
6591{
6592	var ui = this.editorUi;
6593	var editor = ui.editor;
6594	var graph = editor.graph;
6595
6596	div.appendChild(this.createTitle(mxResources.get('view')));
6597
6598	// Grid
6599	this.addGridOption(div);
6600
6601	// Page View
6602	if (DiagramFormatPanel.showPageView)
6603	{
6604		div.appendChild(this.createOption(mxResources.get('pageView'), function()
6605		{
6606			return graph.pageVisible;
6607		}, function(checked)
6608		{
6609			ui.actions.get('pageView').funct();
6610		},
6611		{
6612			install: function(apply)
6613			{
6614				this.listener = function()
6615				{
6616					apply(graph.pageVisible);
6617				};
6618
6619				ui.addListener('pageViewChanged', this.listener);
6620			},
6621			destroy: function()
6622			{
6623				ui.removeListener(this.listener);
6624			}
6625		}));
6626	}
6627
6628	if (graph.isEnabled())
6629	{
6630		// Background
6631		var bg = this.createColorOption(mxResources.get('background'), function()
6632		{
6633			return graph.background;
6634		}, function(color)
6635		{
6636			var change = new ChangePageSetup(ui, color);
6637			change.ignoreImage = true;
6638
6639			graph.model.execute(change);
6640		}, '#ffffff',
6641		{
6642			install: function(apply)
6643			{
6644				this.listener = function()
6645				{
6646					apply(graph.background);
6647				};
6648
6649				ui.addListener('backgroundColorChanged', this.listener);
6650			},
6651			destroy: function()
6652			{
6653				ui.removeListener(this.listener);
6654			}
6655		});
6656
6657		if (this.showBackgroundImageOption)
6658		{
6659			var label = bg.getElementsByTagName('span')[0];
6660			label.style.display = 'inline-block';
6661			label.style.textOverflow = 'ellipsis';
6662			label.style.overflow = 'hidden';
6663			label.style.maxWidth = '68px';
6664
6665			if (mxClient.IS_FF)
6666			{
6667				label.style.marginTop = '1px';
6668			}
6669
6670			var btn = mxUtils.button(mxResources.get('change'), function(evt)
6671			{
6672				ui.showBackgroundImageDialog(null, ui.editor.graph.backgroundImage);
6673				mxEvent.consume(evt);
6674			})
6675
6676			btn.className = 'geColorBtn';
6677			btn.style.position = 'absolute';
6678			btn.style.marginTop = '-3px';
6679			btn.style.height = '22px';
6680			btn.style.left = '118px';
6681			btn.style.width = '56px';
6682
6683			bg.appendChild(btn);
6684		}
6685
6686		div.appendChild(bg);
6687	}
6688
6689	return div;
6690};
6691
6692/**
6693 * Adds the label menu items to the given menu and parent.
6694 */
6695DiagramFormatPanel.prototype.addOptions = function(div)
6696{
6697	var ui = this.editorUi;
6698	var editor = ui.editor;
6699	var graph = editor.graph;
6700
6701	div.appendChild(this.createTitle(mxResources.get('options')));
6702
6703	if (graph.isEnabled())
6704	{
6705		// Connection arrows
6706		div.appendChild(this.createOption(mxResources.get('connectionArrows'), function()
6707		{
6708			return graph.connectionArrowsEnabled;
6709		}, function(checked)
6710		{
6711			ui.actions.get('connectionArrows').funct();
6712		},
6713		{
6714			install: function(apply)
6715			{
6716				this.listener = function()
6717				{
6718					apply(graph.connectionArrowsEnabled);
6719				};
6720
6721				ui.addListener('connectionArrowsChanged', this.listener);
6722			},
6723			destroy: function()
6724			{
6725				ui.removeListener(this.listener);
6726			}
6727		}));
6728
6729		// Connection points
6730		div.appendChild(this.createOption(mxResources.get('connectionPoints'), function()
6731		{
6732			return graph.connectionHandler.isEnabled();
6733		}, function(checked)
6734		{
6735			ui.actions.get('connectionPoints').funct();
6736		},
6737		{
6738			install: function(apply)
6739			{
6740				this.listener = function()
6741				{
6742					apply(graph.connectionHandler.isEnabled());
6743				};
6744
6745				ui.addListener('connectionPointsChanged', this.listener);
6746			},
6747			destroy: function()
6748			{
6749				ui.removeListener(this.listener);
6750			}
6751		}));
6752
6753		// Guides
6754		div.appendChild(this.createOption(mxResources.get('guides'), function()
6755		{
6756			return graph.graphHandler.guidesEnabled;
6757		}, function(checked)
6758		{
6759			ui.actions.get('guides').funct();
6760		},
6761		{
6762			install: function(apply)
6763			{
6764				this.listener = function()
6765				{
6766					apply(graph.graphHandler.guidesEnabled);
6767				};
6768
6769				ui.addListener('guidesEnabledChanged', this.listener);
6770			},
6771			destroy: function()
6772			{
6773				ui.removeListener(this.listener);
6774			}
6775		}));
6776	}
6777
6778	return div;
6779};
6780
6781/**
6782 *
6783 */
6784DiagramFormatPanel.prototype.addGridOption = function(container)
6785{
6786	var fPanel = this;
6787	var ui = this.editorUi;
6788	var graph = ui.editor.graph;
6789
6790	var input = document.createElement('input');
6791	input.style.position = 'absolute';
6792	input.style.textAlign = 'right';
6793	input.style.width = '48px';
6794	input.style.marginTop = '-2px';
6795	input.style.height = '21px';
6796	input.style.border = '1px solid rgb(160, 160, 160)';
6797	input.style.borderRadius = '4px';
6798	input.style.boxSizing = 'border-box';
6799	input.value = this.inUnit(graph.getGridSize()) + ' ' + this.getUnit();
6800
6801	var stepper = this.createStepper(input, update, this.getUnitStep(), null, null, null, this.isFloatUnit());
6802	input.style.display = (graph.isGridEnabled()) ? '' : 'none';
6803	stepper.style.display = input.style.display;
6804
6805	mxEvent.addListener(input, 'keydown', function(e)
6806	{
6807		if (e.keyCode == 13)
6808		{
6809			graph.container.focus();
6810			mxEvent.consume(e);
6811		}
6812		else if (e.keyCode == 27)
6813		{
6814			input.value = graph.getGridSize();
6815			graph.container.focus();
6816			mxEvent.consume(e);
6817		}
6818	});
6819
6820	function update(evt)
6821	{
6822		var value = fPanel.isFloatUnit()? parseFloat(input.value) : parseInt(input.value);
6823		value = fPanel.fromUnit(Math.max(fPanel.inUnit(1), (isNaN(value)) ? fPanel.inUnit(10) : value));
6824
6825		if (value != graph.getGridSize())
6826		{
6827			mxGraph.prototype.gridSize = value;
6828			graph.setGridSize(value)
6829		}
6830
6831		input.value = fPanel.inUnit(value) + ' ' + fPanel.getUnit();
6832		mxEvent.consume(evt);
6833	};
6834
6835	mxEvent.addListener(input, 'blur', update);
6836	mxEvent.addListener(input, 'change', update);
6837
6838	input.style.right = '78px';
6839	stepper.style.marginTop = '-17px';
6840	stepper.style.right = '66px';
6841
6842	var panel = this.createColorOption(mxResources.get('grid'), function()
6843	{
6844		var color = graph.view.gridColor;
6845
6846		return (graph.isGridEnabled()) ? color : null;
6847	}, function(color)
6848	{
6849		var enabled = graph.isGridEnabled();
6850
6851		if (color == mxConstants.NONE)
6852		{
6853			graph.setGridEnabled(false);
6854		}
6855		else
6856		{
6857			graph.setGridEnabled(true);
6858			ui.setGridColor(color);
6859		}
6860
6861		input.style.display = (graph.isGridEnabled()) ? '' : 'none';
6862		stepper.style.display = input.style.display;
6863
6864		if (enabled != graph.isGridEnabled())
6865		{
6866			graph.defaultGridEnabled = graph.isGridEnabled();
6867			ui.fireEvent(new mxEventObject('gridEnabledChanged'));
6868		}
6869	}, Editor.isDarkMode() ? graph.view.defaultDarkGridColor : graph.view.defaultGridColor,
6870	{
6871		install: function(apply)
6872		{
6873			this.listener = function()
6874			{
6875				apply((graph.isGridEnabled()) ? graph.view.gridColor : null);
6876			};
6877
6878			ui.addListener('gridColorChanged', this.listener);
6879			ui.addListener('gridEnabledChanged', this.listener);
6880		},
6881		destroy: function()
6882		{
6883			ui.removeListener(this.listener);
6884		}
6885	});
6886
6887	panel.appendChild(input);
6888	panel.appendChild(stepper);
6889	container.appendChild(panel);
6890};
6891
6892/**
6893 * Adds the label menu items to the given menu and parent.
6894 */
6895DiagramFormatPanel.prototype.addDocumentProperties = function(div)
6896{
6897	// Hook for subclassers
6898	var ui = this.editorUi;
6899	var editor = ui.editor;
6900	var graph = editor.graph;
6901
6902	div.appendChild(this.createTitle(mxResources.get('options')));
6903
6904	return div;
6905};
6906
6907/**
6908 * Adds the label menu items to the given menu and parent.
6909 */
6910DiagramFormatPanel.prototype.addPaperSize = function(div)
6911{
6912	var ui = this.editorUi;
6913	var editor = ui.editor;
6914	var graph = editor.graph;
6915
6916	div.appendChild(this.createTitle(mxResources.get('paperSize')));
6917
6918	var accessor = PageSetupDialog.addPageFormatPanel(div, 'formatpanel', graph.pageFormat, function(pageFormat)
6919	{
6920		if (graph.pageFormat == null || graph.pageFormat.width != pageFormat.width ||
6921			graph.pageFormat.height != pageFormat.height)
6922		{
6923			var change = new ChangePageSetup(ui, null, null, pageFormat);
6924			change.ignoreColor = true;
6925			change.ignoreImage = true;
6926
6927			graph.model.execute(change);
6928		}
6929	});
6930
6931	this.addKeyHandler(accessor.widthInput, function()
6932	{
6933		accessor.set(graph.pageFormat);
6934	});
6935	this.addKeyHandler(accessor.heightInput, function()
6936	{
6937		accessor.set(graph.pageFormat);
6938	});
6939
6940	var listener = function()
6941	{
6942		accessor.set(graph.pageFormat);
6943	};
6944
6945	ui.addListener('pageFormatChanged', listener);
6946	this.listeners.push({destroy: function() { ui.removeListener(listener); }});
6947
6948	graph.getModel().addListener(mxEvent.CHANGE, listener);
6949	this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }});
6950
6951	return div;
6952};
6953
6954/**
6955 * Adds the label menu items to the given menu and parent.
6956 */
6957DiagramFormatPanel.prototype.addStyleOps = function(div)
6958{
6959	var btn = mxUtils.button(mxResources.get('editData'), mxUtils.bind(this, function(evt)
6960	{
6961		this.editorUi.actions.get('editData').funct();
6962	}));
6963
6964	btn.setAttribute('title', mxResources.get('editData') + ' (' + this.editorUi.actions.get('editData').shortcut + ')');
6965	btn.style.width = '210px';
6966	btn.style.marginBottom = '2px';
6967	div.appendChild(btn);
6968
6969	mxUtils.br(div);
6970
6971	btn = mxUtils.button(mxResources.get('clearDefaultStyle'), mxUtils.bind(this, function(evt)
6972	{
6973		this.editorUi.actions.get('clearDefaultStyle').funct();
6974	}));
6975
6976	btn.setAttribute('title', mxResources.get('clearDefaultStyle') + ' (' + this.editorUi.actions.get('clearDefaultStyle').shortcut + ')');
6977	btn.style.width = '210px';
6978	div.appendChild(btn);
6979
6980	return div;
6981};
6982
6983/**
6984 * Adds the label menu items to the given menu and parent.
6985 */
6986DiagramFormatPanel.prototype.destroy = function()
6987{
6988	BaseFormatPanel.prototype.destroy.apply(this, arguments);
6989
6990	if (this.gridEnabledListener)
6991	{
6992		this.editorUi.removeListener(this.gridEnabledListener);
6993		this.gridEnabledListener = null;
6994	}
6995};
6996