1/**
2 * Copyright (c) 2006-2012, JGraph Ltd
3 */
4/**
5 * Construcs a new toolbar for the given editor.
6 */
7function Toolbar(editorUi, container)
8{
9	this.editorUi = editorUi;
10	this.container = container;
11	this.staticElements = [];
12	this.init();
13
14	// Global handler to hide the current menu
15	this.gestureHandler = mxUtils.bind(this, function(evt)
16	{
17		if (this.editorUi.currentMenu != null && mxEvent.getSource(evt) != this.editorUi.currentMenu.div)
18		{
19			this.hideMenu();
20		}
21	});
22
23	mxEvent.addGestureListeners(document, this.gestureHandler);
24};
25
26/**
27 * Image for the dropdown arrow.
28 */
29Toolbar.prototype.dropDownImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/dropdown.gif' : 'data:image/gif;base64,R0lGODlhDQANAIABAHt7e////yH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS4wLWMwNjAgNjEuMTM0Nzc3LCAyMDEwLzAyLzEyLTE3OjMyOjAwICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpCREM1NkJFMjE0NEMxMUU1ODk1Q0M5MjQ0MTA4QjNDMSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpCREM1NkJFMzE0NEMxMUU1ODk1Q0M5MjQ0MTA4QjNDMSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkQzOUMzMjZCMTQ0QjExRTU4OTVDQzkyNDQxMDhCM0MxIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkQzOUMzMjZDMTQ0QjExRTU4OTVDQzkyNDQxMDhCM0MxIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Af/+/fz7+vn49/b19PPy8fDv7u3s6+rp6Ofm5eTj4uHg397d3Nva2djX1tXU09LR0M/OzczLysnIx8bFxMPCwcC/vr28u7q5uLe2tbSzsrGwr66trKuqqainpqWko6KhoJ+enZybmpmYl5aVlJOSkZCPjo2Mi4qJiIeGhYSDgoGAf359fHt6eXh3dnV0c3JxcG9ubWxramloZ2ZlZGNiYWBfXl1cW1pZWFdWVVRTUlFQT05NTEtKSUhHRkVEQ0JBQD8+PTw7Ojk4NzY1NDMyMTAvLi0sKyopKCcmJSQjIiEgHx4dHBsaGRgXFhUUExIREA8ODQwLCgkIBwYFBAMCAQAAIfkEAQAAAQAsAAAAAA0ADQAAAhGMj6nL3QAjVHIu6azbvPtWAAA7';
30
31/**
32 * Defines the background for selected buttons.
33 */
34Toolbar.prototype.selectedBackground = '#d0d0d0';
35
36/**
37 * Defines the background for selected buttons.
38 */
39Toolbar.prototype.unselectedBackground = 'none';
40
41/**
42 * Array that contains the DOM nodes that should never be removed.
43 */
44Toolbar.prototype.staticElements = null;
45
46/**
47 * Adds the toolbar elements.
48 */
49Toolbar.prototype.init = function()
50{
51	var sw = screen.width;
52
53	// Takes into account initial compact mode
54	sw -= (screen.height > 740) ? 56 : 0;
55
56	if (sw >= 700)
57	{
58		var formatMenu = this.addMenu('', mxResources.get('view') + ' (' + mxResources.get('panTooltip') + ')', true, 'viewPanels', null, true);
59		this.addDropDownArrow(formatMenu, 'geSprite-formatpanel', 38, 50, -4, -3, 36, -8);
60		this.addSeparator();
61	}
62
63	var viewMenu = this.addMenu('', mxResources.get('zoom') + ' (Alt+Mousewheel)', true, 'viewZoom', null, true);
64	viewMenu.showDisabled = true;
65	viewMenu.style.whiteSpace = 'nowrap';
66	viewMenu.style.position = 'relative';
67	viewMenu.style.overflow = 'hidden';
68
69	if (EditorUi.compactUi)
70	{
71		viewMenu.style.width = '50px';
72	}
73	else
74	{
75		viewMenu.style.width = '36px';
76	}
77
78	if (sw >= 420)
79	{
80		this.addSeparator();
81		var elts = this.addItems(['zoomIn', 'zoomOut']);
82		elts[0].setAttribute('title', mxResources.get('zoomIn') + ' (' + this.editorUi.actions.get('zoomIn').shortcut + ')');
83		elts[1].setAttribute('title', mxResources.get('zoomOut') + ' (' + this.editorUi.actions.get('zoomOut').shortcut + ')');
84	}
85
86	// Updates the label if the scale changes
87	this.updateZoom = mxUtils.bind(this, function()
88	{
89		viewMenu.innerHTML = Math.round(this.editorUi.editor.graph.view.scale * 100) + '%';
90		this.appendDropDownImageHtml(viewMenu);
91
92		if (EditorUi.compactUi)
93		{
94			viewMenu.getElementsByTagName('img')[0].style.right = '1px';
95			viewMenu.getElementsByTagName('img')[0].style.top = '5px';
96		}
97	});
98
99	this.editorUi.editor.graph.view.addListener(mxEvent.EVENT_SCALE, this.updateZoom);
100	this.editorUi.editor.addListener('resetGraphView', this.updateZoom);
101
102	var elts = this.addItems(['-', 'undo', 'redo']);
103	elts[1].setAttribute('title', mxResources.get('undo') + ' (' + this.editorUi.actions.get('undo').shortcut + ')');
104	elts[2].setAttribute('title', mxResources.get('redo') + ' (' + this.editorUi.actions.get('redo').shortcut + ')');
105
106	if (sw >= 320)
107	{
108		var elts = this.addItems(['-', 'delete']);
109		elts[1].setAttribute('title', mxResources.get('delete') + ' (' + this.editorUi.actions.get('delete').shortcut + ')');
110	}
111
112	if (sw >= 550)
113	{
114		this.addItems(['-', 'toFront', 'toBack']);
115	}
116
117	if (sw >= 740)
118	{
119		this.addItems(['-', 'fillColor']);
120
121		if (sw >= 780)
122		{
123			this.addItems(['strokeColor']);
124
125			if (sw >= 820)
126			{
127				this.addItems(['shadow']);
128			}
129		}
130	}
131
132	if (sw >= 400)
133	{
134		this.addSeparator();
135
136		if (sw >= 440)
137		{
138			this.edgeShapeMenu = this.addMenuFunction('', mxResources.get('connection'), false, mxUtils.bind(this, function(menu)
139			{
140				this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_SHAPE, 'width'], [null, null], 'geIcon geSprite geSprite-connection', null, true).setAttribute('title', mxResources.get('line'));
141				this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_SHAPE, 'width'], ['link', null], 'geIcon geSprite geSprite-linkedge', null, true).setAttribute('title', mxResources.get('link'));
142				this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_SHAPE, 'width'], ['flexArrow', null], 'geIcon geSprite geSprite-arrow', null, true).setAttribute('title', mxResources.get('arrow'));
143				this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_SHAPE, 'width'], ['arrow', null], 'geIcon geSprite geSprite-simplearrow', null, true).setAttribute('title', mxResources.get('simpleArrow'));
144			}));
145
146			this.addDropDownArrow(this.edgeShapeMenu, 'geSprite-connection', 44, 50, 0, 0, 22, -4);
147		}
148
149		this.edgeStyleMenu = this.addMenuFunction('geSprite-orthogonal', mxResources.get('waypoints'), false, mxUtils.bind(this, function(menu)
150		{
151			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'));
152			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'));
153			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'));
154			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'));
155			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'));
156			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'));
157			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'));
158			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'));
159		}));
160
161		this.addDropDownArrow(this.edgeStyleMenu, 'geSprite-orthogonal', 44, 50, 0, 0, 22, -4);
162	}
163
164	this.addSeparator();
165	var insertMenu = this.addMenu('', mxResources.get('insert') + ' (' + mxResources.get('doubleClickTooltip') + ')', true, 'insert', null, true);
166	this.addDropDownArrow(insertMenu, 'geSprite-plus', 38, 48, -4, -3, 36, -8);
167	this.addSeparator();
168	this.addTableDropDown();
169};
170
171/**
172 * Adds the toolbar elements.
173 */
174Toolbar.prototype.appendDropDownImageHtml = function(elt)
175{
176	var img = document.createElement('img');
177	img.setAttribute('border', '0');
178	img.setAttribute('valign', 'middle');
179	img.setAttribute('src', Toolbar.prototype.dropDownImage);
180	elt.appendChild(img);
181
182	img.style.position = 'absolute';
183	img.style.right = '4px';
184	img.style.top = (!EditorUi.compactUi ? 8 : 6) + 'px';
185};
186
187/**
188 * Adds the toolbar elements.
189 */
190Toolbar.prototype.addTableDropDown = function()
191{
192	// KNOWN: All table stuff does not work with undo/redo
193	// KNOWN: Lost focus after click on submenu with text (not icon) in quirks and IE8. This is because the TD seems
194	// to catch the focus on click in these browsers. NOTE: Workaround in mxPopupMenu for icon items (without text).
195	var menuElt = this.addMenuFunction('geIcon geSprite geSprite-table', mxResources.get('table'), false, mxUtils.bind(this, function(menu)
196	{
197		this.editorUi.menus.addInsertTableCellItem(menu);
198	}));
199
200	menuElt.style.position = 'relative';
201	menuElt.style.whiteSpace = 'nowrap';
202	menuElt.style.overflow = 'hidden';
203	menuElt.style.width = '30px';
204	menuElt.innerHTML = '<div class="geSprite geSprite-table"></div>';
205
206	this.appendDropDownImageHtml(menuElt);
207
208	var div = menuElt.getElementsByTagName('div')[0];
209	div.style.marginLeft = '-2px';
210
211	// Fix for item size in kennedy theme
212	if (EditorUi.compactUi)
213	{
214		menuElt.getElementsByTagName('img')[0].style.left = '22px';
215		menuElt.getElementsByTagName('img')[0].style.top = '5px';
216	}
217
218	// Connects to insert menu enabled state
219	var menu = this.editorUi.menus.get('insert');
220
221	// Workaround for possible not a function
222	// when extending HTML objects
223	if (menu != null && typeof menuElt.setEnabled === 'function')
224	{
225		menu.addListener('stateChanged', function()
226		{
227			menuElt.setEnabled(menu.enabled);
228		});
229	}
230
231	return menuElt;
232};
233
234/**
235 * Adds the toolbar elements.
236 */
237Toolbar.prototype.addDropDownArrow = function(menu, sprite, width, atlasWidth, left, top, atlasDelta, atlasLeft)
238{
239	atlasDelta = (atlasDelta != null) ? atlasDelta : 32;
240	left = (EditorUi.compactUi) ? left : atlasLeft;
241
242	menu.style.whiteSpace = 'nowrap';
243	menu.style.overflow = 'hidden';
244	menu.style.position = 'relative';
245	menu.style.width = (atlasWidth - atlasDelta) + 'px';
246
247	menu.innerHTML = '<div class="geSprite ' + sprite + '"></div>';
248	this.appendDropDownImageHtml(menu);
249
250	var div = menu.getElementsByTagName('div')[0];
251	div.style.marginLeft = left + 'px';
252	div.style.marginTop = top + 'px';
253
254	// Fix for item size in kennedy theme
255	if (EditorUi.compactUi)
256	{
257		menu.getElementsByTagName('img')[0].style.left = '24px';
258		menu.getElementsByTagName('img')[0].style.top = '5px';
259		menu.style.width = (width - 10) + 'px';
260	}
261};
262
263/**
264 * Sets the current font name.
265 */
266Toolbar.prototype.setFontName = function(value)
267{
268	if (this.fontMenu != null)
269	{
270		this.fontMenu.innerHTML = '';
271		var div = document.createElement('div');
272		div.style.display = 'inline-block';
273		div.style.overflow = 'hidden';
274		div.style.textOverflow = 'ellipsis';
275		div.style.maxWidth = '66px';
276		mxUtils.write(div, value);
277		this.fontMenu.appendChild(div);
278
279		this.appendDropDownImageHtml(this.fontMenu);
280	}
281};
282
283/**
284 * Sets the current font name.
285 */
286Toolbar.prototype.setFontSize = function(value)
287{
288	if (this.sizeMenu != null)
289	{
290		this.sizeMenu.innerHTML = '';
291		var div = document.createElement('div');
292		div.style.display = 'inline-block';
293		div.style.overflow = 'hidden';
294		div.style.textOverflow = 'ellipsis';
295		div.style.maxWidth = '24px';
296		mxUtils.write(div, value);
297		this.sizeMenu.appendChild(div);
298
299		this.appendDropDownImageHtml(this.sizeMenu);
300	}
301};
302
303/**
304 * Hides the current menu.
305 */
306Toolbar.prototype.createTextToolbar = function()
307{
308	var ui = this.editorUi;
309	var graph = ui.editor.graph;
310
311	var styleElt = this.addMenu('', mxResources.get('style'), true, 'formatBlock');
312	styleElt.style.position = 'relative';
313	styleElt.style.whiteSpace = 'nowrap';
314	styleElt.style.overflow = 'hidden';
315	styleElt.innerHTML = mxResources.get('style');
316	this.appendDropDownImageHtml(styleElt);
317
318	if (EditorUi.compactUi)
319	{
320		styleElt.style.paddingRight = '18px';
321		styleElt.getElementsByTagName('img')[0].style.right = '1px';
322		styleElt.getElementsByTagName('img')[0].style.top = '5px';
323	}
324
325	this.addSeparator();
326
327	this.fontMenu = this.addMenu('', mxResources.get('fontFamily'), true, 'fontFamily');
328	this.fontMenu.style.position = 'relative';
329	this.fontMenu.style.whiteSpace = 'nowrap';
330	this.fontMenu.style.overflow = 'hidden';
331	this.fontMenu.style.width = '68px';
332
333	this.setFontName(Menus.prototype.defaultFont);
334
335	if (EditorUi.compactUi)
336	{
337		this.fontMenu.style.paddingRight = '18px';
338		this.fontMenu.getElementsByTagName('img')[0].style.right = '1px';
339		this.fontMenu.getElementsByTagName('img')[0].style.top = '5px';
340	}
341
342	this.addSeparator();
343
344	this.sizeMenu = this.addMenu(Menus.prototype.defaultFontSize, mxResources.get('fontSize'), true, 'fontSize');
345	this.sizeMenu.style.position = 'relative';
346	this.sizeMenu.style.whiteSpace = 'nowrap';
347	this.sizeMenu.style.overflow = 'hidden';
348	this.sizeMenu.style.width = '24px';
349
350	this.setFontSize(Menus.prototype.defaultFontSize);
351
352	if (EditorUi.compactUi)
353	{
354		this.sizeMenu.style.paddingRight = '18px';
355		this.sizeMenu.getElementsByTagName('img')[0].style.right = '1px';
356		this.sizeMenu.getElementsByTagName('img')[0].style.top = '5px';
357	}
358
359	var elts = this.addItems(['-', 'undo', 'redo','-', 'bold', 'italic', 'underline']);
360	elts[1].setAttribute('title', mxResources.get('undo') + ' (' + ui.actions.get('undo').shortcut + ')');
361	elts[2].setAttribute('title', mxResources.get('redo') + ' (' + ui.actions.get('redo').shortcut + ')');
362	elts[4].setAttribute('title', mxResources.get('bold') + ' (' + ui.actions.get('bold').shortcut + ')');
363	elts[5].setAttribute('title', mxResources.get('italic') + ' (' + ui.actions.get('italic').shortcut + ')');
364	elts[6].setAttribute('title', mxResources.get('underline') + ' (' + ui.actions.get('underline').shortcut + ')');
365
366	// KNOWN: Lost focus after click on submenu with text (not icon) in quirks and IE8. This is because the TD seems
367	// to catch the focus on click in these browsers. NOTE: Workaround in mxPopupMenu for icon items (without text).
368	var alignMenu = this.addMenuFunction('', mxResources.get('align'), false, mxUtils.bind(this, function(menu)
369	{
370		elt = menu.addItem('', null, mxUtils.bind(this, function(evt)
371		{
372			graph.cellEditor.alignText(mxConstants.ALIGN_LEFT, evt);
373			ui.fireEvent(new mxEventObject('styleChanged',
374				'keys', [mxConstants.STYLE_ALIGN],
375				'values', [mxConstants.ALIGN_LEFT],
376				'cells', [graph.cellEditor.getEditingCell()]));
377		}), null, 'geIcon geSprite geSprite-left');
378		elt.setAttribute('title', mxResources.get('left'));
379
380		elt = menu.addItem('', null, mxUtils.bind(this, function(evt)
381		{
382			graph.cellEditor.alignText(mxConstants.ALIGN_CENTER, evt);
383			ui.fireEvent(new mxEventObject('styleChanged',
384				'keys', [mxConstants.STYLE_ALIGN],
385				'values', [mxConstants.ALIGN_CENTER],
386				'cells', [graph.cellEditor.getEditingCell()]));
387		}), null, 'geIcon geSprite geSprite-center');
388		elt.setAttribute('title', mxResources.get('center'));
389
390		elt = menu.addItem('', null, mxUtils.bind(this, function(evt)
391		{
392			graph.cellEditor.alignText(mxConstants.ALIGN_RIGHT, evt);
393			ui.fireEvent(new mxEventObject('styleChanged',
394				'keys', [mxConstants.STYLE_ALIGN],
395				'values', [mxConstants.ALIGN_RIGHT],
396				'cells', [graph.cellEditor.getEditingCell()]));
397		}), null, 'geIcon geSprite geSprite-right');
398		elt.setAttribute('title', mxResources.get('right'));
399
400		elt = menu.addItem('', null, mxUtils.bind(this, function()
401		{
402			document.execCommand('justifyfull', false, null);
403		}), null, 'geIcon geSprite geSprite-justifyfull');
404		elt.setAttribute('title', mxResources.get('justifyfull'));
405
406		elt = menu.addItem('', null, mxUtils.bind(this, function()
407		{
408			document.execCommand('insertorderedlist', false, null);
409		}), null, 'geIcon geSprite geSprite-orderedlist');
410		elt.setAttribute('title', mxResources.get('numberedList'));
411
412		elt = menu.addItem('', null, mxUtils.bind(this, function()
413		{
414			document.execCommand('insertunorderedlist', false, null);
415		}), null, 'geIcon geSprite geSprite-unorderedlist');
416		elt.setAttribute('title', mxResources.get('bulletedList'));
417
418		elt = menu.addItem('', null, mxUtils.bind(this, function()
419		{
420			document.execCommand('outdent', false, null);
421		}), null, 'geIcon geSprite geSprite-outdent');
422		elt.setAttribute('title', mxResources.get('decreaseIndent'));
423
424		elt = menu.addItem('', null, mxUtils.bind(this, function()
425		{
426			document.execCommand('indent', false, null);
427		}), null, 'geIcon geSprite geSprite-indent');
428		elt.setAttribute('title', mxResources.get('increaseIndent'));
429	}));
430
431	alignMenu.style.position = 'relative';
432	alignMenu.style.whiteSpace = 'nowrap';
433	alignMenu.style.overflow = 'hidden';
434	alignMenu.style.width = '30px';
435	alignMenu.innerHTML = '';
436
437	var div = document.createElement('div');
438	div.className = 'geSprite geSprite-left';
439	div.style.marginLeft = '-2px';
440	alignMenu.appendChild(div);
441
442	this.appendDropDownImageHtml(alignMenu);
443
444	if (EditorUi.compactUi)
445	{
446		alignMenu.getElementsByTagName('img')[0].style.left = '22px';
447		alignMenu.getElementsByTagName('img')[0].style.top = '5px';
448	}
449
450	var formatMenu = this.addMenuFunction('', mxResources.get('format'), false, mxUtils.bind(this, function(menu)
451	{
452		elt = menu.addItem('', null, this.editorUi.actions.get('subscript').funct,
453			null, 'geIcon geSprite geSprite-subscript');
454		elt.setAttribute('title', mxResources.get('subscript') + ' (' + Editor.ctrlKey + '+,)');
455
456		elt = menu.addItem('', null, this.editorUi.actions.get('superscript').funct,
457			null, 'geIcon geSprite geSprite-superscript');
458		elt.setAttribute('title', mxResources.get('superscript') + ' (' + Editor.ctrlKey + '+.)');
459
460		// KNOWN: IE+FF don't return keyboard focus after color dialog (calling focus doesn't help)
461		elt = menu.addItem('', null, this.editorUi.actions.get('fontColor').funct,
462			null, 'geIcon geSprite geSprite-fontcolor');
463		elt.setAttribute('title', mxResources.get('fontColor'));
464
465		elt = menu.addItem('', null, this.editorUi.actions.get('backgroundColor').funct,
466			null, 'geIcon geSprite geSprite-fontbackground');
467		elt.setAttribute('title', mxResources.get('backgroundColor'));
468
469		elt = menu.addItem('', null, mxUtils.bind(this, function()
470		{
471			document.execCommand('removeformat', false, null);
472		}), null, 'geIcon geSprite geSprite-removeformat');
473		elt.setAttribute('title', mxResources.get('removeFormat'));
474	}));
475
476	formatMenu.style.position = 'relative';
477	formatMenu.style.whiteSpace = 'nowrap';
478	formatMenu.style.overflow = 'hidden';
479	formatMenu.style.width = '30px';
480	formatMenu.innerHTML = '';
481
482	var div = document.createElement('div');
483	div.className = 'geSprite geSprite-dots';
484	div.style.marginLeft = '-2px';
485	formatMenu.appendChild(div);
486
487	this.appendDropDownImageHtml(formatMenu);
488
489	if (EditorUi.compactUi)
490	{
491		formatMenu.getElementsByTagName('img')[0].style.left = '22px';
492		formatMenu.getElementsByTagName('img')[0].style.top = '5px';
493	}
494
495	this.addSeparator();
496
497	this.addButton('geIcon geSprite geSprite-code', mxResources.get('html'), function()
498	{
499		graph.cellEditor.toggleViewMode();
500
501		if (graph.cellEditor.textarea.innerHTML.length > 0 && (graph.cellEditor.textarea.innerHTML != '&nbsp;' || !graph.cellEditor.clearOnChange))
502		{
503			window.setTimeout(function()
504			{
505				document.execCommand('selectAll', false, null);
506			});
507		}
508	});
509
510	this.addSeparator();
511
512	var insertMenu = this.addMenuFunction('', mxResources.get('insert'), true, mxUtils.bind(this, function(menu)
513	{
514		menu.addItem(mxResources.get('insertLink'), null, mxUtils.bind(this, function()
515		{
516			this.editorUi.actions.get('link').funct();
517		}));
518
519		menu.addItem(mxResources.get('insertImage'), null, mxUtils.bind(this, function()
520		{
521			this.editorUi.actions.get('image').funct();
522		}));
523
524		menu.addItem(mxResources.get('insertHorizontalRule'), null, mxUtils.bind(this, function()
525		{
526			document.execCommand('inserthorizontalrule', false, null);
527		}));
528	}));
529
530	insertMenu.style.whiteSpace = 'nowrap';
531	insertMenu.style.overflow = 'hidden';
532	insertMenu.style.position = 'relative';
533	insertMenu.style.width = '16px';
534	insertMenu.innerHTML = '';
535
536	var div = document.createElement('div');
537	div.className = 'geSprite geSprite-plus';
538	div.style.marginLeft = '-4px';
539	div.style.marginTop = '-3px';
540	insertMenu.appendChild(div);
541
542	this.appendDropDownImageHtml(insertMenu);
543
544	// Fix for item size in kennedy theme
545	if (EditorUi.compactUi)
546	{
547		insertMenu.getElementsByTagName('img')[0].style.left = '24px';
548		insertMenu.getElementsByTagName('img')[0].style.top = '5px';
549		insertMenu.style.width = '30px';
550	}
551
552	this.addSeparator();
553
554	// KNOWN: All table stuff does not work with undo/redo
555	// KNOWN: Lost focus after click on submenu with text (not icon) in quirks and IE8. This is because the TD seems
556	// to catch the focus on click in these browsers. NOTE: Workaround in mxPopupMenu for icon items (without text).
557	var elt = this.addMenuFunction('geIcon geSprite geSprite-table', mxResources.get('table'), false, mxUtils.bind(this, function(menu)
558	{
559		var elt = graph.getSelectedElement();
560		var cell = graph.getParentByNames(elt, ['TD', 'TH'], graph.cellEditor.text2);
561		var row = graph.getParentByName(elt, 'TR', graph.cellEditor.text2);
562
563		if (row == null)
564    	{
565			function createTable(rows, cols)
566			{
567				var html = ['<table>'];
568
569				for (var i = 0; i < rows; i++)
570				{
571					html.push('<tr>');
572
573					for (var j = 0; j < cols; j++)
574					{
575						html.push('<td><br></td>');
576					}
577
578					html.push('</tr>');
579				}
580
581				html.push('</table>');
582
583				return html.join('');
584			};
585
586			this.editorUi.menus.addInsertTableItem(menu);
587    	}
588		else
589    	{
590			var table = graph.getParentByName(row, 'TABLE', graph.cellEditor.text2);
591
592			elt = menu.addItem('', null, mxUtils.bind(this, function()
593			{
594				try
595				{
596					graph.selectNode(graph.insertColumn(table, (cell != null) ? cell.cellIndex : 0));
597				}
598				catch (e)
599				{
600					this.editorUi.handleError(e);
601				}
602			}), null, 'geIcon geSprite geSprite-insertcolumnbefore');
603			elt.setAttribute('title', mxResources.get('insertColumnBefore'));
604
605			elt = menu.addItem('', null, mxUtils.bind(this, function()
606			{
607				try
608				{
609					graph.selectNode(graph.insertColumn(table, (cell != null) ? cell.cellIndex + 1 : -1));
610				}
611				catch (e)
612				{
613					this.editorUi.handleError(e);
614				}
615			}), null, 'geIcon geSprite geSprite-insertcolumnafter');
616			elt.setAttribute('title', mxResources.get('insertColumnAfter'));
617
618			elt = menu.addItem('Delete column', null, mxUtils.bind(this, function()
619			{
620				if (cell != null)
621				{
622					try
623					{
624						graph.deleteColumn(table, cell.cellIndex);
625					}
626					catch (e)
627					{
628						this.editorUi.handleError(e);
629					}
630				}
631			}), null, 'geIcon geSprite geSprite-deletecolumn');
632			elt.setAttribute('title', mxResources.get('deleteColumn'));
633
634			elt = menu.addItem('', null, mxUtils.bind(this, function()
635			{
636				try
637				{
638					graph.selectNode(graph.insertRow(table, row.sectionRowIndex));
639				}
640				catch (e)
641				{
642					this.editorUi.handleError(e);
643				}
644			}), null, 'geIcon geSprite geSprite-insertrowbefore');
645			elt.setAttribute('title', mxResources.get('insertRowBefore'));
646
647			elt = menu.addItem('', null, mxUtils.bind(this, function()
648			{
649				try
650				{
651					graph.selectNode(graph.insertRow(table, row.sectionRowIndex + 1));
652				}
653				catch (e)
654				{
655					this.editorUi.handleError(e);
656				}
657			}), null, 'geIcon geSprite geSprite-insertrowafter');
658			elt.setAttribute('title', mxResources.get('insertRowAfter'));
659
660			elt = menu.addItem('', null, mxUtils.bind(this, function()
661			{
662				try
663				{
664					graph.deleteRow(table, row.sectionRowIndex);
665				}
666				catch (e)
667				{
668					this.editorUi.handleError(e);
669				}
670			}), null, 'geIcon geSprite geSprite-deleterow');
671			elt.setAttribute('title', mxResources.get('deleteRow'));
672
673			elt = menu.addItem('', null, mxUtils.bind(this, function()
674			{
675				// Converts rgb(r,g,b) values
676				var color = table.style.borderColor.replace(
677					    /\brgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g,
678					    function($0, $1, $2, $3) {
679					        return "#" + ("0"+Number($1).toString(16)).substr(-2) + ("0"+Number($2).toString(16)).substr(-2) + ("0"+Number($3).toString(16)).substr(-2);
680					    });
681				this.editorUi.pickColor(color, function(newColor)
682				{
683					if (newColor == null || newColor == mxConstants.NONE)
684					{
685						table.removeAttribute('border');
686						table.style.border = '';
687						table.style.borderCollapse = '';
688					}
689					else
690					{
691						table.setAttribute('border', '1');
692						table.style.border = '1px solid ' + newColor;
693						table.style.borderCollapse = 'collapse';
694					}
695				});
696			}), null, 'geIcon geSprite geSprite-strokecolor');
697			elt.setAttribute('title', mxResources.get('borderColor'));
698
699			elt = menu.addItem('', null, mxUtils.bind(this, function()
700			{
701				// Converts rgb(r,g,b) values
702				var color = table.style.backgroundColor.replace(
703					    /\brgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g,
704					    function($0, $1, $2, $3) {
705					        return "#" + ("0"+Number($1).toString(16)).substr(-2) + ("0"+Number($2).toString(16)).substr(-2) + ("0"+Number($3).toString(16)).substr(-2);
706					    });
707				this.editorUi.pickColor(color, function(newColor)
708				{
709					if (newColor == null || newColor == mxConstants.NONE)
710					{
711						table.style.backgroundColor = '';
712					}
713					else
714					{
715						table.style.backgroundColor = newColor;
716					}
717				});
718			}), null, 'geIcon geSprite geSprite-fillcolor');
719			elt.setAttribute('title', mxResources.get('backgroundColor'));
720
721			elt = menu.addItem('', null, mxUtils.bind(this, function()
722			{
723				var value = table.getAttribute('cellPadding') || 0;
724
725				var dlg = new FilenameDialog(this.editorUi, value, mxResources.get('apply'), mxUtils.bind(this, function(newValue)
726				{
727					if (newValue != null && newValue.length > 0)
728					{
729						table.setAttribute('cellPadding', newValue);
730					}
731					else
732					{
733						table.removeAttribute('cellPadding');
734					}
735				}), mxResources.get('spacing'));
736				this.editorUi.showDialog(dlg.container, 300, 80, true, true);
737				dlg.init();
738			}), null, 'geIcon geSprite geSprite-fit');
739			elt.setAttribute('title', mxResources.get('spacing'));
740
741			elt = menu.addItem('', null, mxUtils.bind(this, function()
742			{
743				table.setAttribute('align', 'left');
744			}), null, 'geIcon geSprite geSprite-left');
745			elt.setAttribute('title', mxResources.get('left'));
746
747			elt = menu.addItem('', null, mxUtils.bind(this, function()
748			{
749				table.setAttribute('align', 'center');
750			}), null, 'geIcon geSprite geSprite-center');
751			elt.setAttribute('title', mxResources.get('center'));
752
753			elt = menu.addItem('', null, mxUtils.bind(this, function()
754			{
755				table.setAttribute('align', 'right');
756			}), null, 'geIcon geSprite geSprite-right');
757			elt.setAttribute('title', mxResources.get('right'));
758    	}
759	}));
760
761	elt.style.position = 'relative';
762	elt.style.whiteSpace = 'nowrap';
763	elt.style.overflow = 'hidden';
764	elt.style.width = '30px';
765	elt.innerHTML = '';
766
767	var div = document.createElement('div');
768	div.className = 'geSprite geSprite-table';
769	div.style.marginLeft = '-2px';
770	elt.appendChild(div);
771
772	this.appendDropDownImageHtml(elt);
773
774	// Fix for item size in kennedy theme
775	if (EditorUi.compactUi)
776	{
777		elt.getElementsByTagName('img')[0].style.left = '22px';
778		elt.getElementsByTagName('img')[0].style.top = '5px';
779	}
780};
781
782/**
783 * Hides the current menu.
784 */
785Toolbar.prototype.hideMenu = function()
786{
787	this.editorUi.hideCurrentMenu();
788};
789
790/**
791 * Adds a label to the toolbar.
792 */
793Toolbar.prototype.addMenu = function(label, tooltip, showLabels, name, c, showAll, ignoreState)
794{
795	var menu = this.editorUi.menus.get(name);
796	var elt = this.addMenuFunction(label, tooltip, showLabels, function()
797	{
798		menu.funct.apply(menu, arguments);
799	}, c, showAll);
800
801	// Workaround for possible not a function
802	// when extending HTML objects
803	if (!ignoreState && typeof elt.setEnabled === 'function')
804	{
805		menu.addListener('stateChanged', function()
806		{
807			elt.setEnabled(menu.enabled);
808		});
809	}
810
811	return elt;
812};
813
814/**
815 * Adds a label to the toolbar.
816 */
817Toolbar.prototype.addMenuFunction = function(label, tooltip, showLabels, funct, c, showAll)
818{
819	return this.addMenuFunctionInContainer((c != null) ? c : this.container, label, tooltip, showLabels, funct, showAll);
820};
821
822/**
823 * Adds a label to the toolbar.
824 */
825Toolbar.prototype.addMenuFunctionInContainer = function(container, label, tooltip, showLabels, funct, showAll)
826{
827	var elt = (showLabels) ? this.createLabel(label) : this.createButton(label);
828	this.initElement(elt, tooltip);
829	this.addMenuHandler(elt, showLabels, funct, showAll);
830	container.appendChild(elt);
831
832	return elt;
833};
834
835/**
836 * Adds a separator to the separator.
837 */
838Toolbar.prototype.addSeparator = function(c)
839{
840	c = (c != null) ? c : this.container;
841	var elt = document.createElement('div');
842	elt.className = 'geSeparator';
843	c.appendChild(elt);
844
845	return elt;
846};
847
848/**
849 * Adds given action item
850 */
851Toolbar.prototype.addItems = function(keys, c, ignoreDisabled)
852{
853	var items = [];
854
855	for (var i = 0; i < keys.length; i++)
856	{
857		var key = keys[i];
858
859		if (key == '-')
860		{
861			items.push(this.addSeparator(c));
862		}
863		else
864		{
865			items.push(this.addItem('geSprite-' + key.toLowerCase(), key, c, ignoreDisabled));
866		}
867	}
868
869	return items;
870};
871
872/**
873 * Adds given action item
874 */
875Toolbar.prototype.addItem = function(sprite, key, c, ignoreDisabled)
876{
877	var action = this.editorUi.actions.get(key);
878	var elt = null;
879
880	if (action != null)
881	{
882		var tooltip = action.label;
883
884		if (action.shortcut != null)
885		{
886			tooltip += ' (' + action.shortcut + ')';
887		}
888
889		elt = this.addButton(sprite, tooltip, action.funct, c);
890
891		// Workaround for possible not a function
892		// when extending HTML objects
893		if (!ignoreDisabled && typeof elt.setEnabled === 'function')
894		{
895			elt.setEnabled(action.enabled);
896
897			action.addListener('stateChanged', function()
898			{
899				elt.setEnabled(action.enabled);
900			});
901		}
902	}
903
904	return elt;
905};
906
907/**
908 * Adds a button to the toolbar.
909 */
910Toolbar.prototype.addButton = function(classname, tooltip, funct, c)
911{
912	var elt = this.createButton(classname);
913	c = (c != null) ? c : this.container;
914
915	this.initElement(elt, tooltip);
916	this.addClickHandler(elt, funct);
917	c.appendChild(elt);
918
919	return elt;
920};
921
922/**
923 * Initializes the given toolbar element.
924 */
925Toolbar.prototype.initElement = function(elt, tooltip)
926{
927	// Adds tooltip
928	if (tooltip != null)
929	{
930		elt.setAttribute('title', tooltip);
931	}
932
933	this.addEnabledState(elt);
934};
935
936/**
937 * Adds enabled state with setter to DOM node (avoids JS wrapper).
938 */
939Toolbar.prototype.addEnabledState = function(elt)
940{
941	var classname = elt.className;
942
943	elt.setEnabled = function(value)
944	{
945		elt.enabled = value;
946
947		if (value)
948		{
949			elt.className = classname;
950		}
951		else
952		{
953			elt.className = classname + ' mxDisabled';
954		}
955	};
956
957	elt.setEnabled(true);
958};
959
960/**
961 * Adds enabled state with setter to DOM node (avoids JS wrapper).
962 */
963Toolbar.prototype.addClickHandler = function(elt, funct)
964{
965	if (funct != null)
966	{
967		mxEvent.addListener(elt, 'click', function(evt)
968		{
969			if (elt.enabled)
970			{
971				funct(evt);
972			}
973
974			mxEvent.consume(evt);
975		});
976
977		// Prevents focus
978	    mxEvent.addListener(elt, (mxClient.IS_POINTER) ? 'pointerdown' : 'mousedown',
979        	mxUtils.bind(this, function(evt)
980    	{
981			evt.preventDefault();
982		}));
983	}
984};
985
986/**
987 * Creates and returns a new button.
988 */
989Toolbar.prototype.createButton = function(classname)
990{
991	var elt = document.createElement('a');
992	elt.className = 'geButton';
993
994	var inner = document.createElement('div');
995
996	if (classname != null)
997	{
998		inner.className = 'geSprite ' + classname;
999	}
1000
1001	elt.appendChild(inner);
1002
1003	return elt;
1004};
1005
1006/**
1007 * Creates and returns a new button.
1008 */
1009Toolbar.prototype.createLabel = function(label, tooltip)
1010{
1011	var elt = document.createElement('a');
1012	elt.className = 'geLabel';
1013	mxUtils.write(elt, label);
1014
1015	return elt;
1016};
1017
1018/**
1019 * Adds a handler for showing a menu in the given element.
1020 */
1021Toolbar.prototype.addMenuHandler = function(elt, showLabels, funct, showAll)
1022{
1023	if (funct != null)
1024	{
1025		var graph = this.editorUi.editor.graph;
1026		var menu = null;
1027		var show = true;
1028
1029		mxEvent.addListener(elt, 'click', mxUtils.bind(this, function(evt)
1030		{
1031			if (show && (elt.enabled == null || elt.enabled))
1032			{
1033				graph.popupMenuHandler.hideMenu();
1034				menu = new mxPopupMenu(funct);
1035				menu.div.className += ' geToolbarMenu';
1036				menu.showDisabled = showAll;
1037				menu.labels = showLabels;
1038				menu.autoExpand = true;
1039
1040				var offset = mxUtils.getOffset(elt);
1041				menu.popup(offset.x, offset.y + elt.offsetHeight, null, evt);
1042				this.editorUi.setCurrentMenu(menu, elt);
1043
1044				// Workaround for scrollbar hiding menu items
1045				if (!showLabels && menu.div.scrollHeight > menu.div.clientHeight)
1046				{
1047					menu.div.style.width = '40px';
1048				}
1049
1050				menu.hideMenu = mxUtils.bind(this, function()
1051				{
1052					mxPopupMenu.prototype.hideMenu.apply(menu, arguments);
1053					this.editorUi.resetCurrentMenu();
1054					menu.destroy();
1055				});
1056
1057				// Extends destroy to reset global state
1058				menu.addListener(mxEvent.EVENT_HIDE, mxUtils.bind(this, function()
1059				{
1060					this.currentElt = null;
1061				}));
1062			}
1063
1064			show = true;
1065			mxEvent.consume(evt);
1066		}));
1067
1068		// Hides menu if already showing and prevents focus
1069        mxEvent.addListener(elt, (mxClient.IS_POINTER) ? 'pointerdown' : 'mousedown',
1070        	mxUtils.bind(this, function(evt)
1071		{
1072			show = this.currentElt != elt;
1073			evt.preventDefault();
1074		}));
1075	}
1076};
1077
1078/**
1079 * Adds a handler for showing a menu in the given element.
1080 */
1081Toolbar.prototype.destroy = function()
1082{
1083	if (this.gestureHandler != null)
1084	{
1085		mxEvent.removeGestureListeners(document, this.gestureHandler);
1086		this.gestureHandler = null;
1087	}
1088};
1089