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 != ' ' || !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