/** * $Id: Toolbar.js,v 1.10 2014/01/16 12:08:57 gaudenz Exp $ * Copyright (c) 2006-2012, JGraph Ltd */ /** * Construcs a new toolbar for the given editor. */ function Toolbar(editorUi, container) { this.editorUi = editorUi; this.container = container; this.init(); // Global handler to hide the current menu mxEvent.addGestureListeners(document, mxUtils.bind(this, function(evt) { this.hideMenu(); })); }; /** * Adds the toolbar elements. */ Toolbar.prototype.init = function() { this.addItems(['undo', 'redo', 'delete', '-', 'actualSize', 'zoomIn', 'zoomOut', '-']); var fontElt = this.addMenu('Helvetica', mxResources.get('fontFamily'), true, 'fontFamily'); fontElt.style.whiteSpace = 'nowrap'; fontElt.style.overflow = 'hidden'; fontElt.style.width = '56px'; this.addSeparator(); var sizeElt = this.addMenu('12', mxResources.get('fontSize'), true, 'fontSize'); sizeElt.style.whiteSpace = 'nowrap'; sizeElt.style.overflow = 'hidden'; sizeElt.style.width = '22px'; this.addItems(['-', 'bold', 'italic', 'underline']); var align = this.addMenuFunction('geSprite-left', mxResources.get('align'), false, mxUtils.bind(this, function(menu) { this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_ALIGN], [mxConstants.ALIGN_LEFT], 'geIcon geSprite geSprite-left', null, function() { document.execCommand('justifyleft'); }).setAttribute('title', mxResources.get('left')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_ALIGN], [mxConstants.ALIGN_CENTER], 'geIcon geSprite geSprite-center', null, function() { document.execCommand('justifycenter'); }).setAttribute('title', mxResources.get('center')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_ALIGN], [mxConstants.ALIGN_RIGHT], 'geIcon geSprite geSprite-right', null, function() { document.execCommand('justifyright'); }).setAttribute('title', mxResources.get('right')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_VERTICAL_ALIGN], [mxConstants.ALIGN_TOP], 'geIcon geSprite geSprite-top', null).setAttribute('title', mxResources.get('top')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_VERTICAL_ALIGN], [mxConstants.ALIGN_MIDDLE], 'geIcon geSprite geSprite-middle', null).setAttribute('title', mxResources.get('middle')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_VERTICAL_ALIGN], [mxConstants.ALIGN_BOTTOM], 'geIcon geSprite geSprite-bottom', null).setAttribute('title', mxResources.get('bottom')); })); this.addItems(['fontColor', '-']); var line = this.addMenuFunction('geSprite-straight', mxResources.get('line'), false, mxUtils.bind(this, function(menu) { this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_EDGE], [null], 'geIcon geSprite geSprite-straight', null).setAttribute('title', mxResources.get('straight')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_EDGE], ['entityRelationEdgeStyle'], 'geIcon geSprite geSprite-entity', null).setAttribute('title', mxResources.get('entityRelation')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW], ['elbowEdgeStyle', 'horizontal'], 'geIcon geSprite geSprite-helbow', null).setAttribute('title', mxResources.get('horizontal')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW], ['elbowEdgeStyle', 'vertical'], 'geIcon geSprite geSprite-velbow', null).setAttribute('title', mxResources.get('vertical')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_EDGE], ['segmentEdgeStyle'], 'geIcon geSprite geSprite-segment', null).setAttribute('title', mxResources.get('manual')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_EDGE], ['orthogonalEdgeStyle'], 'geIcon geSprite geSprite-orthogonal', null).setAttribute('title', mxResources.get('automatic')); })); var linestart = this.addMenuFunction('geSprite-startclassic', mxResources.get('lineend'), false, mxUtils.bind(this, function(menu) { this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.NONE, 0], 'geIcon geSprite geSprite-noarrow', null).setAttribute('title', mxResources.get('none')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_CLASSIC, 1], 'geIcon geSprite geSprite-startclassic', null).setAttribute('title', mxResources.get('classic')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_OPEN, 1], 'geIcon geSprite geSprite-startopen', null).setAttribute('title', mxResources.get('openArrow')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_BLOCK, 1], 'geIcon geSprite geSprite-startblock', null).setAttribute('title', mxResources.get('block')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_OVAL, 1], 'geIcon geSprite geSprite-startoval', null).setAttribute('title', mxResources.get('oval')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_DIAMOND, 1], 'geIcon geSprite geSprite-startdiamond', null).setAttribute('title', mxResources.get('diamond')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_DIAMOND_THIN, 1], 'geIcon geSprite geSprite-startthindiamond', null).setAttribute('title', mxResources.get('diamondThin')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_CLASSIC, 0], 'geIcon geSprite geSprite-startclassictrans', null).setAttribute('title', mxResources.get('classic')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_BLOCK, 0], 'geIcon geSprite geSprite-startblocktrans', null).setAttribute('title', mxResources.get('block')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_OVAL, 0], 'geIcon geSprite geSprite-startovaltrans', null).setAttribute('title', mxResources.get('oval')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_DIAMOND, 0], 'geIcon geSprite geSprite-startdiamondtrans', null).setAttribute('title', mxResources.get('diamond')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_DIAMOND_THIN, 0], 'geIcon geSprite geSprite-startthindiamondtrans', null).setAttribute('title', mxResources.get('diamondThin')); })); var lineend = this.addMenuFunction('geSprite-endclassic', mxResources.get('lineend'), false, mxUtils.bind(this, function(menu) { this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.NONE, 0], 'geIcon geSprite geSprite-noarrow', null).setAttribute('title', mxResources.get('none')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_CLASSIC, 1], 'geIcon geSprite geSprite-endclassic', null).setAttribute('title', mxResources.get('classic')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_OPEN, 1], 'geIcon geSprite geSprite-endopen', null).setAttribute('title', mxResources.get('openArrow')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_BLOCK, 1], 'geIcon geSprite geSprite-endblock', null).setAttribute('title', mxResources.get('block')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_OVAL, 1], 'geIcon geSprite geSprite-endoval', null).setAttribute('title', mxResources.get('oval')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_DIAMOND, 1], 'geIcon geSprite geSprite-enddiamond', null).setAttribute('title', mxResources.get('diamond')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_DIAMOND_THIN, 1], 'geIcon geSprite geSprite-endthindiamond', null).setAttribute('title', mxResources.get('diamondThin')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_CLASSIC, 0], 'geIcon geSprite geSprite-endclassictrans', null).setAttribute('title', mxResources.get('classic')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_BLOCK, 0], 'geIcon geSprite geSprite-endblocktrans', null).setAttribute('title', mxResources.get('block')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_OVAL, 0], 'geIcon geSprite geSprite-endovaltrans', null).setAttribute('title', mxResources.get('oval')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_DIAMOND, 0], 'geIcon geSprite geSprite-enddiamondtrans', null).setAttribute('title', mxResources.get('diamond')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_DIAMOND_THIN, 0], 'geIcon geSprite geSprite-endthindiamondtrans', null).setAttribute('title', mxResources.get('diamondThin')); })); this.addItems(['-', 'image', 'strokeColor', 'fillColor']); this.addItem('geSprite-gradientcolor', 'gradientColor').setAttribute('title', mxResources.get('gradient')); this.addItems(['shadow']); var items = this.addItems(['-', 'grid', 'guides']); var scolor = '#d0d0d0'; // Syncs grid & guides button states this.editorUi.addListener('gridEnabledChanged', mxUtils.bind(this, function() { items[1].style.background = (this.editorUi.actions.get('grid').selectedCallback()) ? scolor : 'none'; })); this.editorUi.addListener('guidesEnabledChanged', mxUtils.bind(this, function() { items[2].style.background = (this.editorUi.actions.get('guides').selectedCallback()) ? scolor : 'none'; })); this.editorUi.editor.addListener('updateGraphComponents', mxUtils.bind(this, function() { items[1].style.background = (this.editorUi.actions.get('grid').selectedCallback()) ? scolor : 'none'; items[2].style.background = (this.editorUi.actions.get('guides').selectedCallback()) ? scolor : 'none'; })); items[1].style.background = (this.editorUi.actions.get('grid').selectedCallback()) ? scolor : 'none'; items[2].style.background = (this.editorUi.actions.get('guides').selectedCallback()) ? scolor : 'none'; var graph = this.editorUi.editor.graph; // Update font size and font family labels var update = mxUtils.bind(this, function() { var ff = 'Helvetica'; var fs = '12'; var state = graph.getView().getState(graph.getSelectionCell()); if (state != null) { ff = state.style[mxConstants.STYLE_FONTFAMILY] || ff; fs = state.style[mxConstants.STYLE_FONTSIZE] || fs; if (ff.length > 10) { ff = ff.substring(0, 8) + '...'; } fontElt.innerHTML = ff; sizeElt.innerHTML = fs; } }); graph.getSelectionModel().addListener(mxEvent.CHANGE, update); graph.getModel().addListener(mxEvent.CHANGE, update); // Updates button states this.addEdgeSelectionHandler([line, linestart, lineend]); this.addSelectionHandler([align]); }; /** * Hides the current menu. */ Toolbar.prototype.createTextToolbar = function() { var graph = this.editorUi.editor.graph; this.addItems(['undo', 'redo', '-']); var fontElt = this.addMenu(mxResources.get('style'), mxResources.get('style'), true, 'formatBlock'); fontElt.style.whiteSpace = 'nowrap'; fontElt.style.overflow = 'hidden'; var fontElt = this.addMenu('Helvetica', mxResources.get('fontFamily'), true, 'fontFamily'); fontElt.style.whiteSpace = 'nowrap'; fontElt.style.overflow = 'hidden'; fontElt.style.width = '56px'; this.addSeparator(); var sizeElt = this.addMenu('12', mxResources.get('fontSize'), true, 'fontSize'); sizeElt.style.whiteSpace = 'nowrap'; sizeElt.style.overflow = 'hidden'; sizeElt.style.width = '22px'; this.addItems(['-', 'bold', 'italic', 'underline']); // KNOWN: Lost focus after click on submenu with text (not icon) in quirks and IE8. This is because the TD seems // to catch the focus on click in these browsers. NOTE: Workaround in mxPopupMenu for icon items (without text). this.addMenuFunction('geSprite-left', mxResources.get('align'), false, mxUtils.bind(this, function(menu) { this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_ALIGN], [mxConstants.ALIGN_LEFT], 'geIcon geSprite geSprite-left', null, function() { document.execCommand('justifyleft'); }).setAttribute('title', mxResources.get('left')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_ALIGN], [mxConstants.ALIGN_CENTER], 'geIcon geSprite geSprite-center', null, function() { document.execCommand('justifycenter'); }).setAttribute('title', mxResources.get('center')); this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_ALIGN], [mxConstants.ALIGN_RIGHT], 'geIcon geSprite geSprite-right', null, function() { document.execCommand('justifyright'); }).setAttribute('title', mxResources.get('right')); })); this.addMenuFunction('geSprite-fontcolor', mxResources.get('more') + '...', false, mxUtils.bind(this, function(menu) { // KNOWN: IE+FF don't return keyboard focus after color dialog (calling focus doesn't help) elt = menu.addItem('', null, this.editorUi.actions.get('fontColor').funct, null, 'geIcon geSprite geSprite-fontcolor'); elt.setAttribute('title', mxResources.get('fontColor')); elt = menu.addItem('', null, this.editorUi.actions.get('backgroundColor').funct, null, 'geIcon geSprite geSprite-fontbackground'); elt.setAttribute('title', mxResources.get('backgroundColor')); elt = menu.addItem('', null, mxUtils.bind(this, function() { document.execCommand('superscript'); }), null, 'geIcon geSprite geSprite-superscript'); elt.setAttribute('title', mxResources.get('superscript')); elt = menu.addItem('', null, mxUtils.bind(this, function() { document.execCommand('subscript'); }), null, 'geIcon geSprite geSprite-subscript'); elt.setAttribute('title', mxResources.get('subscript')); })); this.addSeparator(); this.addButton('geIcon geSprite geSprite-orderedlist', mxResources.get('numberedList'), function() { document.execCommand('insertorderedlist'); }); this.addButton('geIcon geSprite geSprite-unorderedlist', mxResources.get('bulletedList'), function() { document.execCommand('insertunorderedlist'); }); this.addButton('geIcon geSprite geSprite-outdent', mxResources.get('decreaseIndent'), function() { document.execCommand('outdent'); }); this.addButton('geIcon geSprite geSprite-indent', mxResources.get('increaseIndent'), function() { document.execCommand('indent'); }); this.addSeparator(); function getSelectedElement(name) { var node = null; if (window.getSelection) { var sel = window.getSelection(); if (sel.getRangeAt && sel.rangeCount) { var range = sel.getRangeAt(0); node = range.commonAncestorContainer; } } else if (document.selection) { node = document.selection.createRange().parentElement(); } while (node != null) { if (node.nodeName == name) { return node; } node = node.parentNode; } return node; }; function getParentElement(node, name) { var result = node; while (result != null) { if (result.nodeName == name) { break; } result = result.parentNode; } return result; }; function getSelectedCell() { return getSelectedElement('TD'); }; function getSelectedRow() { return getSelectedElement('TR'); }; function getParentTable(node) { return getParentElement(node, 'TABLE'); }; function selectNode(node) { var sel = null; // IE9 and non-IE if (window.getSelection) { sel = window.getSelection(); if (sel.getRangeAt && sel.rangeCount) { var range = document.createRange(); range.selectNode(node); sel.removeAllRanges(); sel.addRange(range); } } // IE < 9 else if ((sel = document.selection) && sel.type != 'Control') { var originalRange = sel.createRange(); originalRange.collapse(true); range = sel.createRange(); range.setEndPoint('StartToStart', originalRange); range.select(); } }; function pasteHtmlAtCaret(html) { var sel, range; // IE9 and non-IE if (window.getSelection) { sel = window.getSelection(); if (sel.getRangeAt && sel.rangeCount) { range = sel.getRangeAt(0); range.deleteContents(); // Range.createContextualFragment() would be useful here but is // only relatively recently standardized and is not supported in // some browsers (IE9, for one) var el = document.createElement("div"); el.innerHTML = html; var frag = document.createDocumentFragment(), node; while ((node = el.firstChild)) { lastNode = frag.appendChild(node); } range.insertNode(frag); } } // IE < 9 else if ((sel = document.selection) && sel.type != "Control") { // FIXME: Does not work if selection is empty sel.createRange().pasteHTML(html); } }; // TODO: Disable toolbar button for HTML code view this.addButton('geIcon geSprite geSprite-link', mxResources.get('insertLink'), mxUtils.bind(this, function() { if (graph.cellEditor.isContentEditing()) { var link = getSelectedElement('A'); var oldValue = ''; if (link != null) { oldValue = link.getAttribute('href'); } var selState = graph.cellEditor.saveSelection(); this.editorUi.showLinkDialog(oldValue, mxResources.get('apply'), mxUtils.bind(this, function(value) { graph.cellEditor.restoreSelection(selState); // 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.text2.getElementsByTagName('a'); var oldLinks = []; for (var i = 0; i < tmp.length; i++) { oldLinks.push(tmp[i]); } if (value != null && value.length > 0) { document.execCommand('createlink', false, mxUtils.trim(value)); // Adds target="_blank" for the new link var newLinks = graph.cellEditor.text2.getElementsByTagName('a'); if (newLinks.length == oldLinks.length + 1) { // Inverse order in favor of appended links for (var i = newLinks.length - 1; i >= 0; i--) { if (i == 0 || newLinks[i] != oldLinks[i - 1]) { newLinks[i].setAttribute('target', '_blank'); break; } } } } })); } })); // TODO: Disable toolbar button for HTML code view this.addItems(['image']); this.addButton('geIcon geSprite geSprite-horizontalrule', mxResources.get('insertHorizontalRule'), function() { document.execCommand('inserthorizontalrule'); }); // KNOWN: All table stuff does not work with undo/redo // KNOWN: Lost focus after click on submenu with text (not icon) in quirks and IE8. This is because the TD seems // to catch the focus on click in these browsers. NOTE: Workaround in mxPopupMenu for icon items (without text). var elt = this.addMenuFunction('geIcon geSprite geSprite-table', mxResources.get('table'), false, mxUtils.bind(this, function(menu) { var cell = getSelectedCell(); var row = getSelectedRow(); if (row == null) { function createTable(rows, cols) { var html = ['