text: /**
   * Copyright (c) 2006-2012, JGraph Ltd
   */
  /**
   * Constructs a new graph editor
   */
  Menus = function(editorUi)
  {
  	this.editorUi = editorUi;
  	this.menus = new Object();
  	this.init();
  	
  	// Pre-fetches checkmark image
  	if (!mxClient.IS_SVG)
  	{
  		new Image().src = this.checkmarkImage;
  	}
  };
  
  /**
   * Sets the default font family.
   */
  Menus.prototype.defaultFont = 'Helvetica';
  
  /**
   * Sets the default font size.
   */
  Menus.prototype.defaultFontSize = '12';
  
  /**
   * Sets the default font size.
   */
  Menus.prototype.defaultMenuItems = ['file', 'edit', 'view', 'arrange', 'extras'];
  
  /**
   * Adds the label menu items to the given menu and parent.
   */
  Menus.prototype.defaultFonts = ['Helvetica', 'Verdana', 'Times New Roman', 'Garamond', 'Comic Sans MS',
             		             'Courier New', 'Georgia', 'Lucida Console', 'Tahoma'];
  
  /**
   * Adds the label menu items to the given menu and parent.
   */
  Menus.prototype.init = function()
  {
  	var graph = this.editorUi.editor.graph;
  	var isGraphEnabled = mxUtils.bind(graph, graph.isEnabled);
  
  	this.customFonts = [];
  	this.customFontSizes = [];
  
  	this.put('fontFamily', new Menu(mxUtils.bind(this, function(menu, parent)
  	{
  		var addItem = mxUtils.bind(this, function(fontname)
  		{
  			var tr = this.styleChange(menu, fontname, [mxConstants.STYLE_FONTFAMILY], [fontname], null, parent, function()
  			{
  				document.execCommand('fontname', false, fontname);
  			}, function()
  			{
  				graph.updateLabelElements(graph.getSelectionCells(), function(elt)
  				{
  					elt.removeAttribute('face');
  					elt.style.fontFamily = null;
  					
  					if (elt.nodeName == 'PRE')
  					{
  						graph.replaceElement(elt, 'div');
  					}
  				});
  			});
  			tr.firstChild.nextSibling.style.fontFamily = fontname;
  		});
  		
  		for (var i = 0; i < this.defaultFonts.length; i++)
  		{
  			addItem(this.defaultFonts[i]);
  		}
  
  		menu.addSeparator(parent);
  		
  		if (this.customFonts.length > 0)
  		{
  			for (var i = 0; i < this.customFonts.length; i++)
  			{
  				addItem(this.customFonts[i]);
  			}
  			
  			menu.addSeparator(parent);
  			
  			menu.addItem(mxResources.get('reset'), null, mxUtils.bind(this, function()
  			{
  				this.customFonts = [];
  				this.editorUi.fireEvent(new mxEventObject('customFontsChanged'));
  			}), parent);
  			
  			menu.addSeparator(parent);
  		}
  		
  		this.promptChange(menu, mxResources.get('custom') + '...', '', mxConstants.DEFAULT_FONTFAMILY, mxConstants.STYLE_FONTFAMILY, parent, true, mxUtils.bind(this, function(newValue)
  		{
  			if (mxUtils.indexOf(this.customFonts, newValue) < 0)
  			{
  				this.customFonts.push(newValue);
  				this.editorUi.fireEvent(new mxEventObject('customFontsChanged'));
  			}
  		}));
  	})));
  	this.put('formatBlock', new Menu(mxUtils.bind(this, function(menu, parent)
  	{
  		function addItem(label, tag)
  		{
  			return menu.addItem(label, null, mxUtils.bind(this, function()
  			{
  				// TODO: Check if visible
  				if (graph.cellEditor.textarea != null)
  				{
  					graph.cellEditor.textarea.focus();
  		      		document.execCommand('formatBlock', false, '<' + tag + '>');
  				}
  			}), parent);
  		};
  		
  		addItem(mxResources.get('normal'), 'p');
  		
  		addItem('', 'h1').firstChild.nextSibling.innerHTML = '<h1 style="margin:0px;">' + mxResources.get('heading') + ' 1</h1>';
  		addItem('', 'h2').firstChild.nextSibling.innerHTML = '<h2 style="margin:0px;">' + mxResources.get('heading') + ' 2</h2>';
  		addItem('', 'h3').firstChild.nextSibling.innerHTML = '<h3 style="margin:0px;">' + mxResources.get('heading') + ' 3</h3>';
  		addItem('', 'h4').firstChild.nextSibling.innerHTML = '<h4 style="margin:0px;">' + mxResources.get('heading') + ' 4</h4>';
  		addItem('', 'h5').firstChild.nextSibling.innerHTML = '<h5 style="margin:0px;">' + mxResources.get('heading') + ' 5</h5>';
  		addItem('', 'h6').firstChild.nextSibling.innerHTML = '<h6 style="margin:0px;">' + mxResources.get('heading') + ' 6</h6>';
  		
  		addItem('', 'pre').firstChild.nextSibling.innerHTML = '<pre style="margin:0px;">' + mxResources.get('formatted') + '</pre>';
  		addItem('', 'blockquote').firstChild.nextSibling.innerHTML = '<blockquote style="margin-top:0px;margin-bottom:0px;">' + mxResources.get('blockquote') + '</blockquote>';
  	})));
  	this.put('fontSize', new Menu(mxUtils.bind(this, function(menu, parent)
  	{
  		var sizes = [6, 8, 9, 10, 11, 12, 14, 18, 24, 36, 48, 72];
  		
  		var addItem = mxUtils.bind(this, function(fontsize)
  		{
  			this.styleChange(menu, fontsize, [mxConstants.STYLE_FONTSIZE], [fontsize], null, parent, function()
  			{
  				if (graph.cellEditor.textarea != null)
  				{
  					// Creates an element with arbitrary size 3
  					document.execCommand('fontSize', false, '3');
  					
  					// Changes the css font size of the first font element inside the in-place editor with size 3
  					// hopefully the above element that we've just created. LATER: Check for new element using
  					// previous result of getElementsByTagName (see other actions)
  					var elts = graph.cellEditor.textarea.getElementsByTagName('font');
  					
  					for (var i = 0; i < elts.length; i++)
  					{
  						if (elts[i].getAttribute('size') == '3')
  						{
  							elts[i].removeAttribute('size');
  							elts[i].style.fontSize = fontsize + 'px';
  							
  							break;
  						}
  					}
  				}
  			});
  		});
  		
  		for (var i = 0; i < sizes.length; i++)
  		{
  			addItem(sizes[i]);
  		}
  
  		menu.addSeparator(parent);
  		
  		if (this.customFontSizes.length > 0)
  		{
  			for (var i = 0; i < this.customFontSizes.length; i++)
  			{
  				addItem(this.customFontSizes[i]);
  			}
  			
  			menu.addSeparator(parent);
  			
  			menu.addItem(mxResources.get('reset'), null, mxUtils.bind(this, function()
  			{
  				this.customFontSizes = [];
  			}), parent);
  			
  			menu.addSeparator(parent);
  		}
  		
  		this.promptChange(menu, mxResources.get('custom') + '...', '(pt)', '12', mxConstants.STYLE_FONTSIZE, parent, true, mxUtils.bind(this, function(newValue)
  		{
  			this.customFontSizes.push(newValue);
  		}));
  	})));
  	this.put('direction', new Menu(mxUtils.bind(this, function(menu, parent)
  	{
  		menu.addItem(mxResources.get('flipH'), null, function() { graph.toggleCellStyles(mxConstants.STYLE_FLIPH, false); }, parent);
  		menu.addItem(mxResources.get('flipV'), null, function() { graph.toggleCellStyles(mxConstants.STYLE_FLIPV, false); }, parent);
  		this.addMenuItems(menu, ['-', 'rotation'], parent);
  	})));
  	this.put('align', new Menu(mxUtils.bind(this, function(menu, parent)
  	{
  		menu.addItem(mxResources.get('leftAlign'), null, function() { graph.alignCells(mxConstants.ALIGN_LEFT); }, parent);
  		menu.addItem(mxResources.get('center'), null, function() { graph.alignCells(mxConstants.ALIGN_CENTER); }, parent);
  		menu.addItem(mxResources.get('rightAlign'), null, function() { graph.alignCells(mxConstants.ALIGN_RIGHT); }, parent);
  		menu.addSeparator(parent);
  		menu.addItem(mxResources.get('topAlign'), null, function() { graph.alignCells(mxConstants.ALIGN_TOP); }, parent);
  		menu.addItem(mxResources.get('middle'), null, function() { graph.alignCells(mxConstants.ALIGN_MIDDLE); }, parent);
  		menu.addItem(mxResources.get('bottomAlign'), null, function() { graph.alignCells(mxConstants.ALIGN_BOTTOM); }, parent);
  	})));
  	this.put('distribute', new Menu(mxUtils.bind(this, function(menu, parent)
  	{
  		menu.addItem(mxResources.get('horizontal'), null, function() { graph.distributeCells(true); }, parent);
  		menu.addItem(mxResources.get('vertical'), null, function() { graph.distributeCells(false); }, parent);
  	})));
  	this.put('layout', new Menu(mxUtils.bind(this, function(menu, parent)
  	{
  		var promptSpacing = mxUtils.bind(this, function(defaultValue, fn)
  		{
  			var dlg = new FilenameDialog(this.editorUi, defaultValue, mxResources.get('apply'), function(newValue)
  			{
  				fn(parseFloat(newValue));
  			}, mxResources.get('spacing'));
  			this.editorUi.showDialog(dlg.container, 300, 80, true, true);
  			dlg.init();
  		});
  		
  		menu.addItem(mxResources.get('horizontalFlow'), null, mxUtils.bind(this, function()
  		{
  			var layout = new mxHierarchicalLayout(graph, mxConstants.DIRECTION_WEST);
  			
      		this.editorUi.executeLayout(function()
      		{
      			var selectionCells = graph.getSelectionCells();
      			layout.execute(graph.getDefaultParent(), selectionCells.length == 0 ? null : selectionCells);
      		}, true);
  		}), parent);
  		menu.addItem(mxResources.get('verticalFlow'), null, mxUtils.bind(this, function()
  		{
  			var layout = new mxHierarchicalLayout(graph, mxConstants.DIRECTION_NORTH);
  			
      		this.editorUi.executeLayout(function()
      		{
      			var selectionCells = graph.getSelectionCells();
      			layout.execute(graph.getDefaultParent(), selectionCells.length == 0 ? null : selectionCells);
      		}, true);
  		}), parent);
  		menu.addSeparator(parent);
  		menu.addItem(mxResources.get('horizontalTree'), null, mxUtils.bind(this, function()
  		{
  			var tmp = graph.getSelectionCell();
  			var roots = null;
  			
  			if (tmp == null || graph.getModel().getChildCount(tmp) == 0)
  			{
  				if (graph.getModel().getEdgeCount(tmp) == 0)
  				{
  					roots = graph.findTreeRoots(graph.getDefaultParent());
  				}
  			}
  			else
  			{
  				roots = graph.findTreeRoots(tmp);
  			}
  
  			if (roots != null && roots.length > 0)
  			{
  				tmp = roots[0];
  			}
  			
  			if (tmp != null)
  			{
  				var layout = new mxCompactTreeLayout(graph, true);
  				layout.edgeRouting = false;
  				layout.levelDistance = 30;
  				
  				promptSpacing(layout.levelDistance, mxUtils.bind(this, function(newValue)
  				{
  					layout.levelDistance = newValue;
  					
  					this.editorUi.executeLayout(function()
  		    		{
  						layout.execute(graph.getDefaultParent(), tmp);
  		    		}, true);
  				}));
  			}
  		}), parent);
  		menu.addItem(mxResources.get('verticalTree'), null, mxUtils.bind(this, function()
  		{
  			var tmp = graph.getSelectionCell();
  			var roots = null;
  			
  			if (tmp == null || graph.getModel().getChildCount(tmp) == 0)
  			{
  				if (graph.getModel().getEdgeCount(tmp) == 0)
  				{
  					roots = graph.findTreeRoots(graph.getDefaultParent());
  				}
  			}
  			else
  			{
  				roots = graph.findTreeRoots(tmp);
  			}
  
  			if (roots != null && roots.length > 0)
  			{
  				tmp = roots[0];
  			}
  			
  			if (tmp != null)
  			{
  				var layout = new mxCompactTreeLayout(graph, false);
  				layout.edgeRouting = false;
  				layout.levelDistance = 30;
  				
  				promptSpacing(layout.levelDistance, mxUtils.bind(this, function(newValue)
  				{
  					layout.levelDistance = newValue;
  					
  					this.editorUi.executeLayout(function()
  		    		{
  						layout.execute(graph.getDefaultParent(), tmp);
  		    		}, true);
  				}));
  			}
  		}), parent);
  		menu.addItem(mxResources.get('radialTree'), null, mxUtils.bind(this, function()
  		{
  			var tmp = graph.getSelectionCell();
  			var roots = null;
  			
  			if (tmp == null || graph.getModel().getChildCount(tmp) == 0)
  			{
  				if (graph.getModel().getEdgeCount(tmp) == 0)
  				{
  					roots = graph.findTreeRoots(graph.getDefaultParent());
  				}
  			}
  			else
  			{
  				roots = graph.findTreeRoots(tmp);
  			}
  
  			if (roots != null && roots.length > 0)
  			{
  				tmp = roots[0];
  			}
  			
  			if (tmp != null)
  			{
  				var layout = new mxRadialTreeLayout(graph, false);
  				layout.levelDistance = 80;
  				layout.autoRadius = true;
  				
  				promptSpacing(layout.levelDistance, mxUtils.bind(this, function(newValue)
  				{
  					layout.levelDistance = newValue;
  					
  					this.editorUi.executeLayout(function()
  		    		{
  		    			layout.execute(graph.getDefaultParent(), tmp);
  		    			
  		    			if (!graph.isSelectionEmpty())
  		    			{
  			    			tmp = graph.getModel().getParent(tmp);
  			    			
  			    			if (graph.getModel().isVertex(tmp))
  			    			{
  			    				graph.updateGroupBounds([tmp], graph.gridSize * 2, true);
  			    			}
  		    			}
  		    		}, true);
  				}));
  			}
  		}), parent);
  		menu.addSeparator(parent);
  		menu.addItem(mxResources.get('organic'), null, mxUtils.bind(this, function()
  		{
  			var layout = new mxFastOrganicLayout(graph);
  			
  			promptSpacing(layout.forceConstant, mxUtils.bind(this, function(newValue)
  			{
  				layout.forceConstant = newValue;
  				
  	    		this.editorUi.executeLayout(function()
  	    		{
  	    			var tmp = graph.getSelectionCell();
  	    			
  	    			if (tmp == null || graph.getModel().getChildCount(tmp) == 0)
  	    			{
  	    				tmp = graph.getDefaultParent();
  	    			}
  	    			
  	    			layout.execute(tmp);
  	    			
  	    			if (graph.getModel().isVertex(tmp))
  	    			{
  	    				graph.updateGroupBounds([tmp], graph.gridSize * 2, true);
  	    			}
  	    		}, true);
  			}));
  		}), parent);
  		menu.addItem(mxResources.get('circle'), null, mxUtils.bind(this, function()
  		{
  			var layout = new mxCircleLayout(graph);
  			
      		this.editorUi.executeLayout(function()
      		{
      			var tmp = graph.getSelectionCell();
      			
      			if (tmp == null || graph.getModel().getChildCount(tmp) == 0)
      			{
      				tmp = graph.getDefaultParent();
      			}
      			
      			layout.execute(tmp);
      			
      			if (graph.getModel().isVertex(tmp))
      			{
      				graph.updateGroupBounds([tmp], graph.gridSize * 2, true);
      			}
      		}, true);
  		}), parent);
  	})));
  	this.put('navigation', new Menu(mxUtils.bind(this, function(menu, parent)
  	{
  		this.addMenuItems(menu, ['home', '-', 'exitGroup', 'enterGroup', '-', 'expand', 'collapse', '-', 'collapsible'], parent);
  	})));
  	this.put('arrange', new Menu(mxUtils.bind(this, function(menu, parent)
  	{
  		this.addMenuItems(menu, ['toFront', 'toBack', '-'], parent);
  		this.addSubmenu('direction', menu, parent);
  		this.addMenuItems(menu, ['turn', '-'], parent);
  		this.addSubmenu('align', menu, parent);
  		this.addSubmenu('distribute', menu, parent);
  		menu.addSeparator(parent);
  		this.addSubmenu('navigation', menu, parent);
  		this.addSubmenu('insert', menu, parent);
  		this.addSubmenu('layout', menu, parent);
  		this.addMenuItems(menu, ['-', 'group', 'ungroup', 'removeFromGroup', '-', 'clearWaypoints', 'autosize'], parent);
  	}))).isEnabled = isGraphEnabled;
  	this.put('insert', new Menu(mxUtils.bind(this, function(menu, parent)
  	{
  		this.addMenuItems(menu, ['insertLink', 'insertImage'], parent);
  	})));
  	this.put('view', new Menu(mxUtils.bind(this, function(menu, parent)
  	{
  		this.addMenuItems(menu, ((this.editorUi.format != null) ? ['formatPanel'] : []).
  			concat(['outline', 'layers', '-', 'pageView', 'pageScale', '-', 'scrollbars', 'tooltips', '-',
  			        'grid', 'guides', '-', 'connectionArrows', 'connectionPoints', '-',
  			        'resetView', 'zoomIn', 'zoomOut'], parent));
  	})));
  	// Two special dropdowns that are only used in the toolbar
  	this.put('viewPanels', new Menu(mxUtils.bind(this, function(menu, parent)
  	{
  		if (this.editorUi.format != null)
  		{
  			this.addMenuItems(menu, ['formatPanel'], parent);
  		}
  		
  		this.addMenuItems(menu, ['outline', 'layers'], parent);
  	})));
  	this.put('viewZoom', new Menu(mxUtils.bind(this, function(menu, parent)
  	{
  		this.addMenuItems(menu, ['resetView', '-'], parent);
  		var scales = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 3, 4];
  		
  		for (var i = 0; i < scales.length; i++)
  		{
  			(function(scale)
  			{
  				menu.addItem((scale * 100) + '%', null, function()
  				{
  					graph.zoomTo(scale);
  				}, parent);
  			})(scales[i]);
  		}
  
  		this.addMenuItems(menu, ['-', 'fitWindow', 'fitPageWidth', 'fitPage', 'fitTwoPages', '-', 'customZoom'], parent);
  	})));
  	this.put('file', new Menu(mxUtils.bind(this, function(menu, parent)
  	{
  		this.addMenuItems(menu, ['pageSetup'], parent);
  	})));
  	this.put('edit', new Menu(mxUtils.bind(this, function(menu, parent)
  	{
  		this.addMenuItems(menu, ['undo', 'redo', '-', 'cut', 'copy', 'paste', 'delete', '-', 'duplicate', '-',
  		                         'editData', 'editTooltip', 'editStyle', '-', 'edit', '-', 'editLink', 'openLink', '-',
  		                         'selectVertices', 'selectEdges', 'selectAll', 'selectNone', '-', 'lockUnlock']);
  	})));
  	this.put('extras', new Menu(mxUtils.bind(this, function(menu, parent)
  	{
  		this.addMenuItems(menu, ['copyConnect', 'collapseExpand', '-', 'editDiagram']);
  	})));
  };
  
  /**
   * Adds the label menu items to the given menu and parent.
   */
  Menus.prototype.put = function(name, menu)
  {
  	this.menus[name] = menu;
  	
  	return menu;
  };
  
  /**
   * Adds the label menu items to the given menu and parent.
   */
  Menus.prototype.get = function(name)
  {
  	return this.menus[name];
  };
  
  /**
   * Adds the given submenu.
   */
  Menus.prototype.addSubmenu = function(name, menu, parent, label)
  {
  	var entry = this.get(name);
  	
  	if (entry != null)
  	{
  		var enabled = entry.isEnabled();
  	
  		if (menu.showDisabled || enabled)
  		{
  			var submenu = menu.addItem(label || mxResources.get(name), null, null, parent, null, enabled);
  			this.addMenu(name, menu, submenu);
  		}
  	}
  };
  
  /**
   * Adds the label menu items to the given menu and parent.
   */
  Menus.prototype.addMenu = function(name, popupMenu, parent)
  {
  	var menu = this.get(name);
  	
  	if (menu != null && (popupMenu.showDisabled || menu.isEnabled()))
  	{
  		this.get(name).execute(popupMenu, parent);
  	}
  };
  
  /**
   * Adds a menu item to insert a table.
   */
  Menus.prototype.addInsertTableItem = function(menu)
  {
  	// KNOWN: Does not work in IE8 standards and quirks
  	var graph = this.editorUi.editor.graph;
  	
  	function createTable(rows, cols)
  	{
  		var html = ['<table>'];
  		
  		for (var i = 0; i < rows; i++)
  		{
  			html.push('<tr>');
  			
  			for (var j = 0; j < cols; j++)
  			{
  				html.push('<td><br></td>');
  			}
  			
  			html.push('</tr>');
  		}
  		
  		html.push('</table>');
  		
  		return html.join('');
  	};
  	
  	// Show table size dialog
  	var elt2 = menu.addItem('', null, mxUtils.bind(this, function(evt)
  	{
  		var td = graph.getParentByName(mxEvent.getSource(evt), 'TD');
  		
  		if (td != null && graph.cellEditor.textarea != null)
  		{
  			var row2 = graph.getParentByName(td, 'TR');
  			
  			// To find the new link, we create a list of all existing links first
      		// LATER: Refactor for reuse with code for finding inserted image below
  			var tmp = graph.cellEditor.textarea.getElementsByTagName('table');
  			var oldTables = [];
  			
  			for (var i = 0; i < tmp.length; i++)
  			{
  				oldTables.push(tmp[i]);
  			}
  			
  			// Finding the new table will work with insertHTML, but IE does not support that
  			graph.container.focus();
  			graph.pasteHtmlAtCaret(createTable(row2.sectionRowIndex + 1, td.cellIndex + 1));
  			
  			// Moves cursor to first table cell
  			var newTables = graph.cellEditor.textarea.getElementsByTagName('table');
  			
  			if (newTables.length == oldTables.length + 1)
  			{
  				// Inverse order in favor of appended tables
  				for (var i = newTables.length - 1; i >= 0; i--)
  				{
  					if (i == 0 || newTables[i] != oldTables[i - 1])
  					{
  						graph.selectNode(newTables[i].rows[0].cells[0]);
  						break;
  					}
  				}
  			}
  		}
  	}));
  	
  	// Quirks mode does not add cell padding if cell is empty, needs good old spacer solution
  	var quirksCellHtml = '<img src="' + mxClient.imageBasePath + '/transparent.gif' + '" width="16" height="16"/>';
  
  	function createPicker(rows, cols)
  	{
  		var table2 = document.createElement('table');
  		table2.setAttribute('border', '1');
  		table2.style.borderCollapse = 'collapse';
  
  		if (!mxClient.IS_QUIRKS)
  		{
  			table2.setAttribute('cellPadding', '8');
  		}
  		
  		for (var i = 0; i < rows; i++)
  		{
  			var row = table2.insertRow(i);
  			
  			for (var j = 0; j < cols; j++)
  			{
  				var cell = row.insertCell(-1);
  				
  				if (mxClient.IS_QUIRKS)
  				{
  					cell.innerHTML = quirksCellHtml;
  				}
  			}
  		}
  		
  		return table2;
  	};
  
  	function extendPicker(picker, rows, cols)
  	{
  		for (var i = picker.rows.length; i < rows; i++)
  		{
  			var row = picker.insertRow(i);
  			
  			for (var j = 0; j < picker.rows[0].cells.length; j++)
  			{
  				var cell = row.insertCell(-1);
  				
  				if (mxClient.IS_QUIRKS)
  				{
  					cell.innerHTML = quirksCellHtml;
  				}
  			}
  		}
  		
  		for (var i = 0; i < picker.rows.length; i++)
  		{
  			var row = picker.rows[i];
  			
  			for (var j = row.cells.length; j < cols; j++)
  			{
  				var cell = row.insertCell(-1);
  				
  				if (mxClient.IS_QUIRKS)
  				{
  					cell.innerHTML = quirksCellHtml;
  				}
  			}
  		}
  	};
  	
  	elt2.firstChild.innerHTML = '';
  	var picker = createPicker(5, 5);
  	elt2.firstChild.appendChild(picker);
  	
  	var label = document.createElement('div');
  	label.style.padding = '4px';
  	label.style.fontSize = Menus.prototype.defaultFontSize + 'px';
  	label.innerHTML = '1x1';
  	elt2.firstChild.appendChild(label);
  	
  	mxEvent.addListener(picker, 'mouseover', function(e)
  	{
  		var td = graph.getParentByName(mxEvent.getSource(e), 'TD');
  		
  		if (td != null)
  		{
  			var row2 = graph.getParentByName(td, 'TR');
  			extendPicker(picker, Math.min(20, row2.sectionRowIndex + 2), Math.min(20, td.cellIndex + 2));
  			label.innerHTML = (td.cellIndex + 1) + 'x' + (row2.sectionRowIndex + 1);
  			
  			for (var i = 0; i < picker.rows.length; i++)
  			{
  				var r = picker.rows[i];
  				
  				for (var j = 0; j < r.cells.length; j++)
  				{
  					var cell = r.cells[j];
  					
  					if (i <= row2.sectionRowIndex && j <= td.cellIndex)
  					{
  						cell.style.backgroundColor = 'blue';
  					}
  					else
  					{
  						cell.style.backgroundColor = 'white';
  					}
  				}
  			}
  			
  			mxEvent.consume(e);
  		}
  	});
  };
  
  /**
   * Adds a style change item to the given menu.
   */
  Menus.prototype.edgeStyleChange = function(menu, label, keys, values, sprite, parent, reset)
  {
  	return menu.addItem(label, null, mxUtils.bind(this, function()
  	{
  		var graph = this.editorUi.editor.graph;
  		graph.stopEditing(false);
  		
  		graph.getModel().beginUpdate();
  		try
  		{
  			var cells = graph.getSelectionCells();
  			var edges = [];
  			
  			for (var i = 0; i < cells.length; i++)
  			{
  				var cell = cells[i];
  				
  				if (graph.getModel().isEdge(cell))
  				{
  					if (reset)
  					{
  						var geo = graph.getCellGeometry(cell);
  			
  						// Resets all edge points
  						if (geo != null)
  						{
  							geo = geo.clone();
  							geo.points = null;
  							graph.getModel().setGeometry(cell, geo);
  						}
  					}
  					
  					for (var j = 0; j < keys.length; j++)
  					{
  						graph.setCellStyles(keys[j], values[j], [cell]);
  					}
  					
  					edges.push(cell);
  				}
  			}
  			
  			this.editorUi.fireEvent(new mxEventObject('styleChanged', 'keys', keys,
  				'values', values, 'cells', edges));
  		}
  		finally
  		{
  			graph.getModel().endUpdate();
  		}
  	}), parent, sprite);
  };
  
  /**
   * Adds a style change item to the given menu.
   */
  Menus.prototype.styleChange = function(menu, label, keys, values, sprite, parent, fn, post)
  {
  	var apply = this.createStyleChangeFunction(keys, values);
  	
  	return menu.addItem(label, null, mxUtils.bind(this, function()
  	{
  		var graph = this.editorUi.editor.graph;
  		
  		if (fn != null && graph.cellEditor.isContentEditing())
  		{
  			fn();
  		}
  		else
  		{
  			apply(post);
  		}
  	}), parent, sprite);
  };
  
  /**
   * 
   */
  Menus.prototype.createStyleChangeFunction = function(keys, values)
  {
  	return mxUtils.bind(this, function(post)
  	{
  		var graph = this.editorUi.editor.graph;
  		graph.stopEditing(false);
  		
  		graph.getModel().beginUpdate();
  		try
  		{
  			for (var i = 0; i < keys.length; i++)
  			{
  				graph.setCellStyles(keys[i], values[i]);
  
  				// Removes CSS alignment to produce consistent output
  				if (keys[i] == mxConstants.STYLE_ALIGN)
  				{
  					graph.updateLabelElements(graph.getSelectionCells(), function(elt)
  					{
  						elt.removeAttribute('align');
  						elt.style.textAlign = null;
  					});
  				}
  			}
  			
  			if (post != null)
  			{
  				post();
  			}
  			
  			this.editorUi.fireEvent(new mxEventObject('styleChanged', 'keys', keys, 'values', values,
  				'cells', graph.getSelectionCells()));
  		}
  		finally
  		{
  			graph.getModel().endUpdate();
  		}
  	});
  };
  
  /**
   * Adds a style change item with a prompt to the given menu.
   */
  Menus.prototype.promptChange = function(menu, label, hint, defaultValue, key, parent, enabled, fn, sprite)
  {
  	return menu.addItem(label, null, mxUtils.bind(this, function()
  	{
  		var graph = this.editorUi.editor.graph;
  		var value = defaultValue;
      	var state = graph.getView().getState(graph.getSelectionCell());
      	
      	if (state != null)
      	{
      		value = state.style[key] || value;
      	}
      	
  		var dlg = new FilenameDialog(this.editorUi, value, mxResources.get('apply'), mxUtils.bind(this, function(newValue)
  		{
  			if (newValue != null && newValue.length > 0)
  			{
  				graph.getModel().beginUpdate();
  				try
  				{
  					graph.stopEditing(false);
  					graph.setCellStyles(key, newValue);
  				}
  				finally
  				{
  					graph.getModel().endUpdate();
  				}
  				
  				if (fn != null)
  				{
  					fn(newValue);
  				}
  			}
  		}), mxResources.get('enterValue') + ((hint.length > 0) ? (' ' + hint) : ''));
  		this.editorUi.showDialog(dlg.container, 300, 80, true, true);
  		dlg.init();
  	}), parent, sprite, enabled);
  };
  
  /**
   * Adds a handler for showing a menu in the given element.
   */
  Menus.prototype.pickColor = function(key, cmd, defaultValue)
  {
  	var graph = this.editorUi.editor.graph;
  	var h = 226 + ((Math.ceil(ColorDialog.prototype.presetColors.length / 12) +
  			Math.ceil(ColorDialog.prototype.defaultColors.length / 12)) * 17);
  	
  	if (cmd != null && graph.cellEditor.isContentEditing())
  	{
  		// Saves and restores text selection for in-place editor
  		var selState = graph.cellEditor.saveSelection();
  		
  		var dlg = new ColorDialog(this.editorUi, defaultValue || '000000', mxUtils.bind(this, function(color)
  		{
  			graph.cellEditor.restoreSelection(selState);
  			document.execCommand(cmd, false, (color != mxConstants.NONE) ? color : 'transparent');
  		}), function()
  		{
  			graph.cellEditor.restoreSelection(selState);
  		});
  		this.editorUi.showDialog(dlg.container, 230, h, true, true);
  		dlg.init();
  	}
  	else
  	{
  		if (this.colorDialog == null)
  		{
  			this.colorDialog = new ColorDialog(this.editorUi);
  		}
  	
  		this.colorDialog.currentColorKey = key;
  		var state = graph.getView().getState(graph.getSelectionCell());
  		var color = 'none';
  		
  		if (state != null)
  		{
  			color = state.style[key] || color;
  		}
  		
  		if (color == 'none')
  		{
  			color = 'ffffff';
  			this.colorDialog.picker.fromString('ffffff');
  			this.colorDialog.colorInput.value = 'none';
  		}
  		else
  		{
  			this.colorDialog.picker.fromString(color);
  		}
  	
  		this.editorUi.showDialog(this.colorDialog.container, 230, h, true, true);
  		this.colorDialog.init();
  	}
  };
  
  /**
   * Adds a handler for showing a menu in the given element.
   */
  Menus.prototype.toggleStyle = function(key, defaultValue)
  {
  	var graph = this.editorUi.editor.graph;
  	var value = graph.toggleCellStyles(key, defaultValue);
  	this.editorUi.fireEvent(new mxEventObject('styleChanged', 'keys', [key], 'values', [value],
  			'cells', graph.getSelectionCells()));
  };
  
  /**
   * Creates the keyboard event handler for the current graph and history.
   */
  Menus.prototype.addMenuItem = function(menu, key, parent, trigger, sprite, label)
  {
  	var action = this.editorUi.actions.get(key);
  
  	if (action != null && (menu.showDisabled || action.isEnabled()) && action.visible)
  	{
  		var item = menu.addItem(label || action.label, null, function()
  		{
  			action.funct(trigger);
  		}, parent, sprite, action.isEnabled());
  		
  		// Adds checkmark image
  		if (action.toggleAction && action.isSelected())
  		{
  			menu.addCheckmark(item, Editor.checkmarkImage);
  		}
  
  		this.addShortcut(item, action);
  		
  		return item;
  	}
  	
  	return null;
  };
  
  /**
   * Adds a checkmark to the given menuitem.
   */
  Menus.prototype.addShortcut = function(item, action)
  {
  	if (action.shortcut != null)
  	{
  		var td = item.firstChild.nextSibling.nextSibling;
  		var span = document.createElement('span');
  		span.style.color = 'gray';
  		mxUtils.write(span, action.shortcut);
  		td.appendChild(span);
  	}
  };
  
  /**
   * Creates the keyboard event handler for the current graph and history.
   */
  Menus.prototype.addMenuItems = function(menu, keys, parent, trigger, sprites)
  {
  	for (var i = 0; i < keys.length; i++)
  	{
  		if (keys[i] == '-')
  		{
  			menu.addSeparator(parent);
  		}
  		else
  		{
  			this.addMenuItem(menu, keys[i], parent, trigger, (sprites != null) ? sprites[i] : null);
  		}
  	}
  };
  
  /**
   * Creates the keyboard event handler for the current graph and history.
   */
  Menus.prototype.createPopupMenu = function(menu, cell, evt)
  {
  	var graph = this.editorUi.editor.graph;
  	menu.smartSeparators = true;
  	
  	if (graph.isSelectionEmpty())
  	{
  		this.addMenuItems(menu, ['undo', 'redo', 'pasteHere'], null, evt);
  	}
  	else
  	{
  		this.addMenuItems(menu, ['delete', '-', 'cut', 'copy', '-', 'duplicate'], null, evt);
  	}
  
  	if (!graph.isSelectionEmpty())
  	{
  		if (graph.getSelectionCount() == 1)
  		{
  			this.addMenuItems(menu, ['setAsDefaultStyle'], null, evt);
  		}
  		
  		menu.addSeparator();
  		
  		cell = graph.getSelectionCell();
  		var state = graph.view.getState(cell);
  
  		if (state != null)
  		{
  			var hasWaypoints = false;
  			this.addMenuItems(menu, ['toFront', 'toBack', '-'], null, evt);
  
  			if (graph.getModel().isEdge(cell) && mxUtils.getValue(state.style, mxConstants.STYLE_EDGE, null) != 'entityRelationEdgeStyle' &&
  				mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null) != 'arrow')
  			{
  				var handler = graph.selectionCellsHandler.getHandler(cell);
  				var isWaypoint = false;
  				
  				if (handler instanceof mxEdgeHandler && handler.bends != null && handler.bends.length > 2)
  				{
  					var index = handler.getHandleForEvent(graph.updateMouseEvent(new mxMouseEvent(evt)));
  					
  					// Configures removeWaypoint action before execution
  					// Using trigger parameter is cleaner but have to find waypoint here anyway.
  					var rmWaypointAction = this.editorUi.actions.get('removeWaypoint');
  					rmWaypointAction.handler = handler;
  					rmWaypointAction.index = index;
  
  					isWaypoint = index > 0 && index < handler.bends.length - 1;
  				}
  				
  				menu.addSeparator();
  				this.addMenuItem(menu, 'turn', null, evt, null, mxResources.get('reverse'));
  				this.addMenuItems(menu, [(isWaypoint) ? 'removeWaypoint' : 'addWaypoint'], null, evt);
  				
  				// Adds reset waypoints option if waypoints exist
  				var geo = graph.getModel().getGeometry(cell);
  				hasWaypoints = geo != null && geo.points != null && geo.points.length > 0;
  			}
  
  			if (graph.getSelectionCount() == 1 && (hasWaypoints || (graph.getModel().isVertex(cell) &&
  				graph.getModel().getEdgeCount(cell) > 0)))
  			{
  				this.addMenuItems(menu, ['clearWaypoints'], null, evt);
  			}
  			
  			if (graph.getSelectionCount() > 1)	
  			{
  				menu.addSeparator();
  				this.addMenuItems(menu, ['group'], null, evt);
  			}
  			else if (graph.getSelectionCount() == 1 && !graph.getModel().isEdge(cell) && !graph.isSwimlane(cell) &&
  					graph.getModel().getChildCount(cell) > 0)
  			{
  				menu.addSeparator();
  				this.addMenuItems(menu, ['ungroup'], null, evt);
  			}
  			
  			if (graph.getSelectionCount() == 1)
  			{
  				menu.addSeparator();
  				this.addMenuItems(menu, ['editData', 'editLink'], null, evt);
  
  				// Shows edit image action if there is an image in the style
  				if (graph.getModel().isVertex(cell) && mxUtils.getValue(state.style, mxConstants.STYLE_IMAGE, null) != null)
  				{
  					menu.addSeparator();
  					this.addMenuItem(menu, 'image', null, evt).firstChild.nextSibling.innerHTML = mxResources.get('editImage') + '...';
  				}
  			}
  		}
  	}
  	else
  	{
  		this.addMenuItems(menu, ['-', 'selectVertices', 'selectEdges',
  			'selectAll', '-', 'clearDefaultStyle'], null, evt);
  	}
  };
  
  /**
   * Creates the keyboard event handler for the current graph and history.
   */
  Menus.prototype.createMenubar = function(container)
  {
  	var menubar = new Menubar(this.editorUi, container);
  	var menus = this.defaultMenuItems;
  	
  	for (var i = 0; i < menus.length; i++)
  	{
  		(mxUtils.bind(this, function(menu)
  		{
  			var elt = menubar.addMenu(mxResources.get(menus[i]), mxUtils.bind(this, function()
  			{
  				// Allows extensions of menu.funct
  				menu.funct.apply(this, arguments);
  			}));
  			
  			this.menuCreated(menu, elt);
  		}))(this.get(menus[i]));
  	}
  
  	return menubar;
  };
  
  /**
   * Creates the keyboard event handler for the current graph and history.
   */
  Menus.prototype.menuCreated = function(menu, elt, className)
  {
  	if (elt != null)
  	{
  		className = (className != null) ? className : 'geItem';
  		
  		menu.addListener('stateChanged', function()
  		{
  			elt.enabled = menu.enabled;
  			
  			if (!menu.enabled)
  			{
  				elt.className = className + ' mxDisabled';
  				
  				if (document.documentMode == 8)
  				{
  					elt.style.color = '#c3c3c3';
  				}
  			}
  			else
  			{
  				elt.className = className;
  				
  				if (document.documentMode == 8)
  				{
  					elt.style.color = '';
  				}
  			}
  		});
  	}
  };
  
  /**
   * Construcs a new menubar for the given editor.
   */
  function Menubar(editorUi, container)
  {
  	this.editorUi = editorUi;
  	this.container = container;
  };
  
  /**
   * Adds the menubar elements.
   */
  Menubar.prototype.hideMenu = function()
  {
  	this.editorUi.hideCurrentMenu();
  };
  
  /**
   * Adds a submenu to this menubar.
   */
  Menubar.prototype.addMenu = function(label, funct, before)
  {
  	var elt = document.createElement('a');
  	elt.className = 'geItem';
  	mxUtils.write(elt, label);
  	this.addMenuHandler(elt, funct);
  	
      if (before != null)
      {
      	this.container.insertBefore(elt, before);
      }
      else
      {
      	this.container.appendChild(elt);
      }
  	
  	return elt;
  };
  
  /**
   * Adds a handler for showing a menu in the given element.
   */
  Menubar.prototype.addMenuHandler = function(elt, funct)
  {
  	if (funct != null)
  	{
  		var show = true;
  		
  		var clickHandler = mxUtils.bind(this, function(evt)
  		{
  			if (show && elt.enabled == null || elt.enabled)
  			{
  				this.editorUi.editor.graph.popupMenuHandler.hideMenu();
  				var menu = new mxPopupMenu(funct);
  				menu.div.className += ' geMenubarMenu';
  				menu.smartSeparators = true;
  				menu.showDisabled = true;
  				menu.autoExpand = true;
  				
  				// Disables autoexpand and destroys menu when hidden
  				menu.hideMenu = mxUtils.bind(this, function()
  				{
  					mxPopupMenu.prototype.hideMenu.apply(menu, arguments);
  					this.editorUi.resetCurrentMenu();
  					menu.destroy();
  				});
  
  				var offset = mxUtils.getOffset(elt);
  				menu.table.style.marginRight = '15px'; // Always have a margin for the scrollbar
  				menu.popup(offset.x, offset.y + elt.offsetHeight, null, evt);
  				if (menu.div.offsetHeight >= menu.table.offsetHeight)
  				{
  					// If there is no scrollbar attempt to hide the margin.
  					// It's better to have extra margin rather than a scrollbar cutting off content.
  					menu.table.style.marginRight = '0px';
  				}
  				this.editorUi.setCurrentMenu(menu, elt);
  			}
  			
  			mxEvent.consume(evt);
  		});
  		
  		// Shows menu automatically while in expanded state
  		mxEvent.addListener(elt, 'mousemove', mxUtils.bind(this, function(evt)
  		{
  			if (this.editorUi.currentMenu != null && this.editorUi.currentMenuElt != elt)
  			{
  				this.editorUi.hideCurrentMenu();
  				clickHandler(evt);
  			}
  		}));
  		
  		// Hides menu if already showing and prevents focus
          mxEvent.addListener(elt, (mxClient.IS_POINTER) ? 'pointerdown' : 'mousedown',
          	mxUtils.bind(this, function(evt)
  		{
  			show = this.currentElt != elt;
  			evt.preventDefault();
  		}));
  
  		mxEvent.addListener(elt, 'click', mxUtils.bind(this, function(evt)
  		{
  			clickHandler(evt);
  			show = true;
  		}));
  	}
  };
  
  /**
   * Creates the keyboard event handler for the current graph and history.
   */
  Menubar.prototype.destroy = function()
  {
  	// do nothing
  };
  
  /**
   * Constructs a new action for the given parameters.
   */
  function Menu(funct, enabled)
  {
  	mxEventSource.call(this);
  	this.funct = funct;
  	this.enabled = (enabled != null) ? enabled : true;
  };
  
  // Menu inherits from mxEventSource
  mxUtils.extend(Menu, mxEventSource);
  
  /**
   * Sets the enabled state of the action and fires a stateChanged event.
   */
  Menu.prototype.isEnabled = function()
  {
  	return this.enabled;
  };
  
  /**
   * Sets the enabled state of the action and fires a stateChanged event.
   */
  Menu.prototype.setEnabled = function(value)
  {
  	if (this.enabled != value)
  	{
  		this.enabled = value;
  		this.fireEvent(new mxEventObject('stateChanged'));
  	}
  };
  
  /**
   * Sets the enabled state of the action and fires a stateChanged event.
   */
  Menu.prototype.execute = function(menu, parent)
  {
  	this.funct(menu, parent);
  };
  
  /**
   * "Installs" menus in EditorUi.
   */
  EditorUi.prototype.createMenus = function()
  {
  	return new Menus(this);
  };
  