Toolbar.js 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955
  1. /**
  2. * Copyright (c) 2006-2012, JGraph Ltd
  3. */
  4. /**
  5. * Construcs a new toolbar for the given editor.
  6. */
  7. function Toolbar(editorUi, container)
  8. {
  9. this.editorUi = editorUi;
  10. this.container = container;
  11. this.staticElements = [];
  12. this.init();
  13. // Global handler to hide the current menu
  14. this.gestureHandler = mxUtils.bind(this, function(evt)
  15. {
  16. if (this.editorUi.currentMenu != null && mxEvent.getSource(evt) != this.editorUi.currentMenu.div)
  17. {
  18. this.hideMenu();
  19. }
  20. });
  21. mxEvent.addGestureListeners(document, this.gestureHandler);
  22. };
  23. /**
  24. * Image for the dropdown arrow.
  25. */
  26. Toolbar.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';
  27. /**
  28. * Image element for the dropdown arrow.
  29. */
  30. Toolbar.prototype.dropdownImageHtml = '<img border="0" style="position:absolute;right:4px;top:' +
  31. ((!EditorUi.compactUi) ? 8 : 6) + 'px;" src="' + Toolbar.prototype.dropdownImage + '" valign="middle"/>';
  32. /**
  33. * Defines the background for selected buttons.
  34. */
  35. Toolbar.prototype.selectedBackground = '#d0d0d0';
  36. /**
  37. * Defines the background for selected buttons.
  38. */
  39. Toolbar.prototype.unselectedBackground = 'none';
  40. /**
  41. * Array that contains the DOM nodes that should never be removed.
  42. */
  43. Toolbar.prototype.staticElements = null;
  44. /**
  45. * Adds the toolbar elements.
  46. */
  47. Toolbar.prototype.init = function()
  48. {
  49. var sw = screen.width;
  50. // Takes into account initial compact mode
  51. sw -= (screen.height > 740) ? 56 : 0;
  52. if (sw >= 700)
  53. {
  54. var formatMenu = this.addMenu('', mxResources.get('view') + ' (' + mxResources.get('panTooltip') + ')', true, 'viewPanels', null, true);
  55. this.addDropDownArrow(formatMenu, 'geSprite-formatpanel', 38, 50, -4, -3, 36, -8);
  56. this.addSeparator();
  57. }
  58. var viewMenu = this.addMenu('', mxResources.get('zoom') + ' (Alt+Mousewheel)', true, 'viewZoom', null, true);
  59. viewMenu.showDisabled = true;
  60. viewMenu.style.whiteSpace = 'nowrap';
  61. viewMenu.style.position = 'relative';
  62. viewMenu.style.overflow = 'hidden';
  63. if (EditorUi.compactUi)
  64. {
  65. viewMenu.style.width = (mxClient.IS_QUIRKS) ? '58px' : '50px';
  66. }
  67. else
  68. {
  69. viewMenu.style.width = (mxClient.IS_QUIRKS) ? '62px' : '36px';
  70. }
  71. if (sw >= 420)
  72. {
  73. this.addSeparator();
  74. var elts = this.addItems(['zoomIn', 'zoomOut']);
  75. elts[0].setAttribute('title', mxResources.get('zoomIn') + ' (' + this.editorUi.actions.get('zoomIn').shortcut + ')');
  76. elts[1].setAttribute('title', mxResources.get('zoomOut') + ' (' + this.editorUi.actions.get('zoomOut').shortcut + ')');
  77. }
  78. // Updates the label if the scale changes
  79. this.updateZoom = mxUtils.bind(this, function()
  80. {
  81. viewMenu.innerHTML = Math.round(this.editorUi.editor.graph.view.scale * 100) + '%' +
  82. this.dropdownImageHtml;
  83. if (EditorUi.compactUi)
  84. {
  85. viewMenu.getElementsByTagName('img')[0].style.right = '1px';
  86. viewMenu.getElementsByTagName('img')[0].style.top = '5px';
  87. }
  88. });
  89. this.editorUi.editor.graph.view.addListener(mxEvent.EVENT_SCALE, this.updateZoom);
  90. this.editorUi.editor.addListener('resetGraphView', this.updateZoom);
  91. var elts = this.addItems(['-', 'undo', 'redo']);
  92. elts[1].setAttribute('title', mxResources.get('undo') + ' (' + this.editorUi.actions.get('undo').shortcut + ')');
  93. elts[2].setAttribute('title', mxResources.get('redo') + ' (' + this.editorUi.actions.get('redo').shortcut + ')');
  94. if (sw >= 320)
  95. {
  96. var elts = this.addItems(['-', 'delete']);
  97. elts[1].setAttribute('title', mxResources.get('delete') + ' (' + this.editorUi.actions.get('delete').shortcut + ')');
  98. }
  99. if (sw >= 550)
  100. {
  101. this.addItems(['-', 'toFront', 'toBack']);
  102. }
  103. if (sw >= 740)
  104. {
  105. this.addItems(['-', 'fillColor']);
  106. if (sw >= 780)
  107. {
  108. this.addItems(['strokeColor']);
  109. if (sw >= 820)
  110. {
  111. this.addItems(['shadow']);
  112. }
  113. }
  114. }
  115. if (sw >= 400)
  116. {
  117. this.addSeparator();
  118. if (sw >= 440)
  119. {
  120. this.edgeShapeMenu = this.addMenuFunction('', mxResources.get('connection'), false, mxUtils.bind(this, function(menu)
  121. {
  122. this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_SHAPE, 'width'], [null, null], 'geIcon geSprite geSprite-connection', null, true).setAttribute('title', mxResources.get('line'));
  123. this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_SHAPE, 'width'], ['link', null], 'geIcon geSprite geSprite-linkedge', null, true).setAttribute('title', mxResources.get('link'));
  124. this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_SHAPE, 'width'], ['flexArrow', null], 'geIcon geSprite geSprite-arrow', null, true).setAttribute('title', mxResources.get('arrow'));
  125. this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_SHAPE, 'width'], ['arrow', null], 'geIcon geSprite geSprite-simplearrow', null, true).setAttribute('title', mxResources.get('simpleArrow'));
  126. }));
  127. this.addDropDownArrow(this.edgeShapeMenu, 'geSprite-connection', 44, 50, 0, 0, 22, -4);
  128. }
  129. this.edgeStyleMenu = this.addMenuFunction('geSprite-orthogonal', mxResources.get('waypoints'), false, mxUtils.bind(this, function(menu)
  130. {
  131. 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'));
  132. 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'));
  133. 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'));
  134. 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'));
  135. 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'));
  136. 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'));
  137. 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'));
  138. 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'));
  139. }));
  140. this.addDropDownArrow(this.edgeStyleMenu, 'geSprite-orthogonal', 44, 50, 0, 0, 22, -4);
  141. }
  142. this.addSeparator();
  143. var insertMenu = this.addMenu('', mxResources.get('insert') + ' (' + mxResources.get('doubleClickTooltip') + ')', true, 'insert', null, true);
  144. this.addDropDownArrow(insertMenu, 'geSprite-plus', 38, 48, -4, -3, 36, -8);
  145. };
  146. /**
  147. * Adds the toolbar elements.
  148. */
  149. Toolbar.prototype.addDropDownArrow = function(menu, sprite, width, atlasWidth, left, top, atlasDelta, atlasLeft)
  150. {
  151. atlasDelta = (atlasDelta != null) ? atlasDelta : 32;
  152. left = (EditorUi.compactUi) ? left : atlasLeft;
  153. menu.style.whiteSpace = 'nowrap';
  154. menu.style.overflow = 'hidden';
  155. menu.style.position = 'relative';
  156. menu.innerHTML = '<div class="geSprite ' + sprite + '" style="margin-left:' + left + 'px;margin-top:' + top + 'px;"></div>' +
  157. this.dropdownImageHtml;
  158. menu.style.width = (mxClient.IS_QUIRKS) ? atlasWidth + 'px' : (atlasWidth - atlasDelta) + 'px';
  159. if (mxClient.IS_QUIRKS)
  160. {
  161. menu.style.height = (EditorUi.compactUi) ? '24px' : '26px';
  162. }
  163. // Fix for item size in kennedy theme
  164. if (EditorUi.compactUi)
  165. {
  166. menu.getElementsByTagName('img')[0].style.left = '24px';
  167. menu.getElementsByTagName('img')[0].style.top = '5px';
  168. menu.style.width = (mxClient.IS_QUIRKS) ? width + 'px' : (width - 10) + 'px';
  169. }
  170. };
  171. /**
  172. * Sets the current font name.
  173. */
  174. Toolbar.prototype.setFontName = function(value)
  175. {
  176. if (this.fontMenu != null)
  177. {
  178. this.fontMenu.innerHTML = '<div style="width:60px;overflow:hidden;display:inline-block;">' +
  179. mxUtils.htmlEntities(value) + '</div>' + this.dropdownImageHtml;
  180. }
  181. };
  182. /**
  183. * Sets the current font name.
  184. */
  185. Toolbar.prototype.setFontSize = function(value)
  186. {
  187. if (this.sizeMenu != null)
  188. {
  189. this.sizeMenu.innerHTML = '<div style="width:24px;overflow:hidden;display:inline-block;">' +
  190. value + '</div>' + this.dropdownImageHtml;
  191. }
  192. };
  193. /**
  194. * Hides the current menu.
  195. */
  196. Toolbar.prototype.createTextToolbar = function()
  197. {
  198. var graph = this.editorUi.editor.graph;
  199. var styleElt = this.addMenu('', mxResources.get('style'), true, 'formatBlock');
  200. styleElt.style.position = 'relative';
  201. styleElt.style.whiteSpace = 'nowrap';
  202. styleElt.style.overflow = 'hidden';
  203. styleElt.innerHTML = mxResources.get('style') + this.dropdownImageHtml;
  204. if (EditorUi.compactUi)
  205. {
  206. styleElt.style.paddingRight = '18px';
  207. styleElt.getElementsByTagName('img')[0].style.right = '1px';
  208. styleElt.getElementsByTagName('img')[0].style.top = '5px';
  209. }
  210. this.addSeparator();
  211. this.fontMenu = this.addMenu('', mxResources.get('fontFamily'), true, 'fontFamily');
  212. this.fontMenu.style.position = 'relative';
  213. this.fontMenu.style.whiteSpace = 'nowrap';
  214. this.fontMenu.style.overflow = 'hidden';
  215. this.fontMenu.style.width = (mxClient.IS_QUIRKS) ? '80px' : '60px';
  216. this.setFontName(Menus.prototype.defaultFont);
  217. if (EditorUi.compactUi)
  218. {
  219. this.fontMenu.style.paddingRight = '18px';
  220. this.fontMenu.getElementsByTagName('img')[0].style.right = '1px';
  221. this.fontMenu.getElementsByTagName('img')[0].style.top = '5px';
  222. }
  223. this.addSeparator();
  224. this.sizeMenu = this.addMenu(Menus.prototype.defaultFontSize, mxResources.get('fontSize'), true, 'fontSize');
  225. this.sizeMenu.style.position = 'relative';
  226. this.sizeMenu.style.whiteSpace = 'nowrap';
  227. this.sizeMenu.style.overflow = 'hidden';
  228. this.sizeMenu.style.width = (mxClient.IS_QUIRKS) ? '44px' : '24px';
  229. this.setFontSize(Menus.prototype.defaultFontSize);
  230. if (EditorUi.compactUi)
  231. {
  232. this.sizeMenu.style.paddingRight = '18px';
  233. this.sizeMenu.getElementsByTagName('img')[0].style.right = '1px';
  234. this.sizeMenu.getElementsByTagName('img')[0].style.top = '5px';
  235. }
  236. var elts = this.addItems(['-', 'undo', 'redo','-', 'bold', 'italic', 'underline']);
  237. elts[1].setAttribute('title', mxResources.get('undo') + ' (' + this.editorUi.actions.get('undo').shortcut + ')');
  238. elts[2].setAttribute('title', mxResources.get('redo') + ' (' + this.editorUi.actions.get('redo').shortcut + ')');
  239. elts[4].setAttribute('title', mxResources.get('bold') + ' (' + this.editorUi.actions.get('bold').shortcut + ')');
  240. elts[5].setAttribute('title', mxResources.get('italic') + ' (' + this.editorUi.actions.get('italic').shortcut + ')');
  241. elts[6].setAttribute('title', mxResources.get('underline') + ' (' + this.editorUi.actions.get('underline').shortcut + ')');
  242. // KNOWN: Lost focus after click on submenu with text (not icon) in quirks and IE8. This is because the TD seems
  243. // to catch the focus on click in these browsers. NOTE: Workaround in mxPopupMenu for icon items (without text).
  244. var alignMenu = this.addMenuFunction('', mxResources.get('align'), false, mxUtils.bind(this, function(menu)
  245. {
  246. elt = menu.addItem('', null, mxUtils.bind(this, function()
  247. {
  248. document.execCommand('justifyleft', false, null);
  249. }), null, 'geIcon geSprite geSprite-left');
  250. elt.setAttribute('title', mxResources.get('left'));
  251. elt = menu.addItem('', null, mxUtils.bind(this, function()
  252. {
  253. document.execCommand('justifycenter', false, null);
  254. }), null, 'geIcon geSprite geSprite-center');
  255. elt.setAttribute('title', mxResources.get('center'));
  256. elt = menu.addItem('', null, mxUtils.bind(this, function()
  257. {
  258. document.execCommand('justifyright', false, null);
  259. }), null, 'geIcon geSprite geSprite-right');
  260. elt.setAttribute('title', mxResources.get('right'));
  261. elt = menu.addItem('', null, mxUtils.bind(this, function()
  262. {
  263. document.execCommand('justifyfull', false, null);
  264. }), null, 'geIcon geSprite geSprite-justifyfull');
  265. elt.setAttribute('title', mxResources.get('justifyfull'));
  266. elt = menu.addItem('', null, mxUtils.bind(this, function()
  267. {
  268. document.execCommand('insertorderedlist', false, null);
  269. }), null, 'geIcon geSprite geSprite-orderedlist');
  270. elt.setAttribute('title', mxResources.get('numberedList'));
  271. elt = menu.addItem('', null, mxUtils.bind(this, function()
  272. {
  273. document.execCommand('insertunorderedlist', false, null);
  274. }), null, 'geIcon geSprite geSprite-unorderedlist');
  275. elt.setAttribute('title', mxResources.get('bulletedList'));
  276. elt = menu.addItem('', null, mxUtils.bind(this, function()
  277. {
  278. document.execCommand('outdent', false, null);
  279. }), null, 'geIcon geSprite geSprite-outdent');
  280. elt.setAttribute('title', mxResources.get('decreaseIndent'));
  281. elt = menu.addItem('', null, mxUtils.bind(this, function()
  282. {
  283. document.execCommand('indent', false, null);
  284. }), null, 'geIcon geSprite geSprite-indent');
  285. elt.setAttribute('title', mxResources.get('increaseIndent'));
  286. }));
  287. alignMenu.style.position = 'relative';
  288. alignMenu.style.whiteSpace = 'nowrap';
  289. alignMenu.style.overflow = 'hidden';
  290. alignMenu.innerHTML = '<div class="geSprite geSprite-left" style="margin-left:-2px;"></div>' + this.dropdownImageHtml;
  291. alignMenu.style.width = (mxClient.IS_QUIRKS) ? '50px' : '30px';
  292. if (EditorUi.compactUi)
  293. {
  294. alignMenu.getElementsByTagName('img')[0].style.left = '22px';
  295. alignMenu.getElementsByTagName('img')[0].style.top = '5px';
  296. }
  297. var formatMenu = this.addMenuFunction('', mxResources.get('format'), false, mxUtils.bind(this, function(menu)
  298. {
  299. elt = menu.addItem('', null, this.editorUi.actions.get('subscript').funct,
  300. null, 'geIcon geSprite geSprite-subscript');
  301. elt.setAttribute('title', mxResources.get('subscript') + ' (Ctrl+,)');
  302. elt = menu.addItem('', null, this.editorUi.actions.get('superscript').funct,
  303. null, 'geIcon geSprite geSprite-superscript');
  304. elt.setAttribute('title', mxResources.get('superscript') + ' (Ctrl+.)');
  305. // KNOWN: IE+FF don't return keyboard focus after color dialog (calling focus doesn't help)
  306. elt = menu.addItem('', null, this.editorUi.actions.get('fontColor').funct,
  307. null, 'geIcon geSprite geSprite-fontcolor');
  308. elt.setAttribute('title', mxResources.get('fontColor'));
  309. elt = menu.addItem('', null, this.editorUi.actions.get('backgroundColor').funct,
  310. null, 'geIcon geSprite geSprite-fontbackground');
  311. elt.setAttribute('title', mxResources.get('backgroundColor'));
  312. elt = menu.addItem('', null, mxUtils.bind(this, function()
  313. {
  314. document.execCommand('removeformat', false, null);
  315. }), null, 'geIcon geSprite geSprite-removeformat');
  316. elt.setAttribute('title', mxResources.get('removeFormat'));
  317. }));
  318. formatMenu.style.position = 'relative';
  319. formatMenu.style.whiteSpace = 'nowrap';
  320. formatMenu.style.overflow = 'hidden';
  321. formatMenu.innerHTML = '<div class="geSprite geSprite-dots" style="margin-left:-2px;"></div>' +
  322. this.dropdownImageHtml;
  323. formatMenu.style.width = (mxClient.IS_QUIRKS) ? '50px' : '30px';
  324. if (EditorUi.compactUi)
  325. {
  326. formatMenu.getElementsByTagName('img')[0].style.left = '22px';
  327. formatMenu.getElementsByTagName('img')[0].style.top = '5px';
  328. }
  329. this.addSeparator();
  330. this.addButton('geIcon geSprite geSprite-code', mxResources.get('html'), function()
  331. {
  332. graph.cellEditor.toggleViewMode();
  333. if (graph.cellEditor.textarea.innerHTML.length > 0 && (graph.cellEditor.textarea.innerHTML != '&nbsp;' || !graph.cellEditor.clearOnChange))
  334. {
  335. window.setTimeout(function()
  336. {
  337. document.execCommand('selectAll', false, null);
  338. });
  339. }
  340. });
  341. this.addSeparator();
  342. // FIXME: Uses geButton here and geLabel in main menu
  343. var insertMenu = this.addMenuFunction('', mxResources.get('insert'), true, mxUtils.bind(this, function(menu)
  344. {
  345. menu.addItem(mxResources.get('insertLink'), null, mxUtils.bind(this, function()
  346. {
  347. this.editorUi.actions.get('link').funct();
  348. }));
  349. menu.addItem(mxResources.get('insertImage'), null, mxUtils.bind(this, function()
  350. {
  351. this.editorUi.actions.get('image').funct();
  352. }));
  353. menu.addItem(mxResources.get('insertHorizontalRule'), null, mxUtils.bind(this, function()
  354. {
  355. document.execCommand('inserthorizontalrule', false, null);
  356. }));
  357. }));
  358. insertMenu.style.whiteSpace = 'nowrap';
  359. insertMenu.style.overflow = 'hidden';
  360. insertMenu.style.position = 'relative';
  361. insertMenu.innerHTML = '<div class="geSprite geSprite-plus" style="margin-left:-4px;margin-top:-3px;"></div>' +
  362. this.dropdownImageHtml;
  363. insertMenu.style.width = (mxClient.IS_QUIRKS) ? '36px' : '16px';
  364. // Fix for item size in kennedy theme
  365. if (EditorUi.compactUi)
  366. {
  367. insertMenu.getElementsByTagName('img')[0].style.left = '24px';
  368. insertMenu.getElementsByTagName('img')[0].style.top = '5px';
  369. insertMenu.style.width = (mxClient.IS_QUIRKS) ? '50px' : '30px';
  370. }
  371. this.addSeparator();
  372. // KNOWN: All table stuff does not work with undo/redo
  373. // KNOWN: Lost focus after click on submenu with text (not icon) in quirks and IE8. This is because the TD seems
  374. // to catch the focus on click in these browsers. NOTE: Workaround in mxPopupMenu for icon items (without text).
  375. var elt = this.addMenuFunction('geIcon geSprite geSprite-table', mxResources.get('table'), false, mxUtils.bind(this, function(menu)
  376. {
  377. var elt = graph.getSelectedElement();
  378. var cell = graph.getParentByName(elt, 'TD', graph.cellEditor.text2);
  379. var row = graph.getParentByName(elt, 'TR', graph.cellEditor.text2);
  380. if (row == null)
  381. {
  382. this.editorUi.menus.addInsertTableItem(menu);
  383. }
  384. else
  385. {
  386. var table = graph.getParentByName(row, 'TABLE', graph.cellEditor.text2);
  387. elt = menu.addItem('', null, mxUtils.bind(this, function()
  388. {
  389. try
  390. {
  391. graph.selectNode(graph.insertColumn(table, (cell != null) ? cell.cellIndex : 0));
  392. }
  393. catch (e)
  394. {
  395. mxUtils.alert(mxResources.get('error') + ': ' + e.message);
  396. }
  397. }), null, 'geIcon geSprite geSprite-insertcolumnbefore');
  398. elt.setAttribute('title', mxResources.get('insertColumnBefore'));
  399. elt = menu.addItem('', null, mxUtils.bind(this, function()
  400. {
  401. try
  402. {
  403. graph.selectNode(graph.insertColumn(table, (cell != null) ? cell.cellIndex + 1 : -1));
  404. }
  405. catch (e)
  406. {
  407. mxUtils.alert(mxResources.get('error') + ': ' + e.message);
  408. }
  409. }), null, 'geIcon geSprite geSprite-insertcolumnafter');
  410. elt.setAttribute('title', mxResources.get('insertColumnAfter'));
  411. elt = menu.addItem('Delete column', null, mxUtils.bind(this, function()
  412. {
  413. if (cell != null)
  414. {
  415. try
  416. {
  417. graph.deleteColumn(table, cell.cellIndex);
  418. }
  419. catch (e)
  420. {
  421. mxUtils.alert(mxResources.get('error') + ': ' + e.message);
  422. }
  423. }
  424. }), null, 'geIcon geSprite geSprite-deletecolumn');
  425. elt.setAttribute('title', mxResources.get('deleteColumn'));
  426. elt = menu.addItem('', null, mxUtils.bind(this, function()
  427. {
  428. try
  429. {
  430. graph.selectNode(graph.insertRow(table, row.sectionRowIndex));
  431. }
  432. catch (e)
  433. {
  434. mxUtils.alert(mxResources.get('error') + ': ' + e.message);
  435. }
  436. }), null, 'geIcon geSprite geSprite-insertrowbefore');
  437. elt.setAttribute('title', mxResources.get('insertRowBefore'));
  438. elt = menu.addItem('', null, mxUtils.bind(this, function()
  439. {
  440. try
  441. {
  442. graph.selectNode(graph.insertRow(table, row.sectionRowIndex + 1));
  443. }
  444. catch (e)
  445. {
  446. mxUtils.alert(mxResources.get('error') + ': ' + e.message);
  447. }
  448. }), null, 'geIcon geSprite geSprite-insertrowafter');
  449. elt.setAttribute('title', mxResources.get('insertRowAfter'));
  450. elt = menu.addItem('', null, mxUtils.bind(this, function()
  451. {
  452. try
  453. {
  454. graph.deleteRow(table, row.sectionRowIndex);
  455. }
  456. catch (e)
  457. {
  458. mxUtils.alert(mxResources.get('error') + ': ' + e.message);
  459. }
  460. }), null, 'geIcon geSprite geSprite-deleterow');
  461. elt.setAttribute('title', mxResources.get('deleteRow'));
  462. elt = menu.addItem('', null, mxUtils.bind(this, function()
  463. {
  464. // Converts rgb(r,g,b) values
  465. var color = table.style.borderColor.replace(
  466. /\brgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g,
  467. function($0, $1, $2, $3) {
  468. return "#" + ("0"+Number($1).toString(16)).substr(-2) + ("0"+Number($2).toString(16)).substr(-2) + ("0"+Number($3).toString(16)).substr(-2);
  469. });
  470. this.editorUi.pickColor(color, function(newColor)
  471. {
  472. if (newColor == null || newColor == mxConstants.NONE)
  473. {
  474. table.removeAttribute('border');
  475. table.style.border = '';
  476. table.style.borderCollapse = '';
  477. }
  478. else
  479. {
  480. table.setAttribute('border', '1');
  481. table.style.border = '1px solid ' + newColor;
  482. table.style.borderCollapse = 'collapse';
  483. }
  484. });
  485. }), null, 'geIcon geSprite geSprite-strokecolor');
  486. elt.setAttribute('title', mxResources.get('borderColor'));
  487. elt = menu.addItem('', null, mxUtils.bind(this, function()
  488. {
  489. // Converts rgb(r,g,b) values
  490. var color = table.style.backgroundColor.replace(
  491. /\brgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g,
  492. function($0, $1, $2, $3) {
  493. return "#" + ("0"+Number($1).toString(16)).substr(-2) + ("0"+Number($2).toString(16)).substr(-2) + ("0"+Number($3).toString(16)).substr(-2);
  494. });
  495. this.editorUi.pickColor(color, function(newColor)
  496. {
  497. if (newColor == null || newColor == mxConstants.NONE)
  498. {
  499. table.style.backgroundColor = '';
  500. }
  501. else
  502. {
  503. table.style.backgroundColor = newColor;
  504. }
  505. });
  506. }), null, 'geIcon geSprite geSprite-fillcolor');
  507. elt.setAttribute('title', mxResources.get('backgroundColor'));
  508. elt = menu.addItem('', null, mxUtils.bind(this, function()
  509. {
  510. var value = table.getAttribute('cellPadding') || 0;
  511. var dlg = new FilenameDialog(this.editorUi, value, mxResources.get('apply'), mxUtils.bind(this, function(newValue)
  512. {
  513. if (newValue != null && newValue.length > 0)
  514. {
  515. table.setAttribute('cellPadding', newValue);
  516. }
  517. else
  518. {
  519. table.removeAttribute('cellPadding');
  520. }
  521. }), mxResources.get('spacing'));
  522. this.editorUi.showDialog(dlg.container, 300, 80, true, true);
  523. dlg.init();
  524. }), null, 'geIcon geSprite geSprite-fit');
  525. elt.setAttribute('title', mxResources.get('spacing'));
  526. elt = menu.addItem('', null, mxUtils.bind(this, function()
  527. {
  528. table.setAttribute('align', 'left');
  529. }), null, 'geIcon geSprite geSprite-left');
  530. elt.setAttribute('title', mxResources.get('left'));
  531. elt = menu.addItem('', null, mxUtils.bind(this, function()
  532. {
  533. table.setAttribute('align', 'center');
  534. }), null, 'geIcon geSprite geSprite-center');
  535. elt.setAttribute('title', mxResources.get('center'));
  536. elt = menu.addItem('', null, mxUtils.bind(this, function()
  537. {
  538. table.setAttribute('align', 'right');
  539. }), null, 'geIcon geSprite geSprite-right');
  540. elt.setAttribute('title', mxResources.get('right'));
  541. }
  542. }));
  543. elt.style.position = 'relative';
  544. elt.style.whiteSpace = 'nowrap';
  545. elt.style.overflow = 'hidden';
  546. elt.innerHTML = '<div class="geSprite geSprite-table" style="margin-left:-2px;"></div>' + this.dropdownImageHtml;
  547. elt.style.width = (mxClient.IS_QUIRKS) ? '50px' : '30px';
  548. // Fix for item size in kennedy theme
  549. if (EditorUi.compactUi)
  550. {
  551. elt.getElementsByTagName('img')[0].style.left = '22px';
  552. elt.getElementsByTagName('img')[0].style.top = '5px';
  553. }
  554. };
  555. /**
  556. * Hides the current menu.
  557. */
  558. Toolbar.prototype.hideMenu = function()
  559. {
  560. this.editorUi.hideCurrentMenu();
  561. };
  562. /**
  563. * Adds a label to the toolbar.
  564. */
  565. Toolbar.prototype.addMenu = function(label, tooltip, showLabels, name, c, showAll)
  566. {
  567. var menu = this.editorUi.menus.get(name);
  568. var elt = this.addMenuFunction(label, tooltip, showLabels, function()
  569. {
  570. menu.funct.apply(menu, arguments);
  571. }, c, showAll);
  572. menu.addListener('stateChanged', function()
  573. {
  574. elt.setEnabled(menu.enabled);
  575. });
  576. return elt;
  577. };
  578. /**
  579. * Adds a label to the toolbar.
  580. */
  581. Toolbar.prototype.addMenuFunction = function(label, tooltip, showLabels, funct, c, showAll)
  582. {
  583. return this.addMenuFunctionInContainer((c != null) ? c : this.container, label, tooltip, showLabels, funct, showAll);
  584. };
  585. /**
  586. * Adds a label to the toolbar.
  587. */
  588. Toolbar.prototype.addMenuFunctionInContainer = function(container, label, tooltip, showLabels, funct, showAll)
  589. {
  590. var elt = (showLabels) ? this.createLabel(label) : this.createButton(label);
  591. this.initElement(elt, tooltip);
  592. this.addMenuHandler(elt, showLabels, funct, showAll);
  593. container.appendChild(elt);
  594. return elt;
  595. };
  596. /**
  597. * Adds a separator to the separator.
  598. */
  599. Toolbar.prototype.addSeparator = function(c)
  600. {
  601. c = (c != null) ? c : this.container;
  602. var elt = document.createElement('div');
  603. elt.className = 'geSeparator';
  604. c.appendChild(elt);
  605. return elt;
  606. };
  607. /**
  608. * Adds given action item
  609. */
  610. Toolbar.prototype.addItems = function(keys, c, ignoreDisabled)
  611. {
  612. var items = [];
  613. for (var i = 0; i < keys.length; i++)
  614. {
  615. var key = keys[i];
  616. if (key == '-')
  617. {
  618. items.push(this.addSeparator(c));
  619. }
  620. else
  621. {
  622. items.push(this.addItem('geSprite-' + key.toLowerCase(), key, c, ignoreDisabled));
  623. }
  624. }
  625. return items;
  626. };
  627. /**
  628. * Adds given action item
  629. */
  630. Toolbar.prototype.addItem = function(sprite, key, c, ignoreDisabled)
  631. {
  632. var action = this.editorUi.actions.get(key);
  633. var elt = null;
  634. if (action != null)
  635. {
  636. var tooltip = action.label;
  637. if (action.shortcut != null)
  638. {
  639. tooltip += ' (' + action.shortcut + ')';
  640. }
  641. elt = this.addButton(sprite, tooltip, action.funct, c);
  642. if (!ignoreDisabled)
  643. {
  644. elt.setEnabled(action.enabled);
  645. action.addListener('stateChanged', function()
  646. {
  647. elt.setEnabled(action.enabled);
  648. });
  649. }
  650. }
  651. return elt;
  652. };
  653. /**
  654. * Adds a button to the toolbar.
  655. */
  656. Toolbar.prototype.addButton = function(classname, tooltip, funct, c)
  657. {
  658. var elt = this.createButton(classname);
  659. c = (c != null) ? c : this.container;
  660. this.initElement(elt, tooltip);
  661. this.addClickHandler(elt, funct);
  662. c.appendChild(elt);
  663. return elt;
  664. };
  665. /**
  666. * Initializes the given toolbar element.
  667. */
  668. Toolbar.prototype.initElement = function(elt, tooltip)
  669. {
  670. // Adds tooltip
  671. if (tooltip != null)
  672. {
  673. elt.setAttribute('title', tooltip);
  674. }
  675. this.addEnabledState(elt);
  676. };
  677. /**
  678. * Adds enabled state with setter to DOM node (avoids JS wrapper).
  679. */
  680. Toolbar.prototype.addEnabledState = function(elt)
  681. {
  682. var classname = elt.className;
  683. elt.setEnabled = function(value)
  684. {
  685. elt.enabled = value;
  686. if (value)
  687. {
  688. elt.className = classname;
  689. }
  690. else
  691. {
  692. elt.className = classname + ' mxDisabled';
  693. }
  694. };
  695. elt.setEnabled(true);
  696. };
  697. /**
  698. * Adds enabled state with setter to DOM node (avoids JS wrapper).
  699. */
  700. Toolbar.prototype.addClickHandler = function(elt, funct)
  701. {
  702. if (funct != null)
  703. {
  704. mxEvent.addListener(elt, 'click', function(evt)
  705. {
  706. if (elt.enabled)
  707. {
  708. funct(evt);
  709. }
  710. mxEvent.consume(evt);
  711. });
  712. if (document.documentMode != null && document.documentMode >= 9)
  713. {
  714. // Prevents focus
  715. mxEvent.addListener(elt, 'mousedown', function(evt)
  716. {
  717. evt.preventDefault();
  718. });
  719. }
  720. }
  721. };
  722. /**
  723. * Creates and returns a new button.
  724. */
  725. Toolbar.prototype.createButton = function(classname)
  726. {
  727. var elt = document.createElement('a');
  728. elt.setAttribute('href', 'javascript:void(0);');
  729. elt.className = 'geButton';
  730. var inner = document.createElement('div');
  731. if (classname != null)
  732. {
  733. inner.className = 'geSprite ' + classname;
  734. }
  735. elt.appendChild(inner);
  736. return elt;
  737. };
  738. /**
  739. * Creates and returns a new button.
  740. */
  741. Toolbar.prototype.createLabel = function(label, tooltip)
  742. {
  743. var elt = document.createElement('a');
  744. elt.setAttribute('href', 'javascript:void(0);');
  745. elt.className = 'geLabel';
  746. mxUtils.write(elt, label);
  747. return elt;
  748. };
  749. /**
  750. * Adds a handler for showing a menu in the given element.
  751. */
  752. Toolbar.prototype.addMenuHandler = function(elt, showLabels, funct, showAll)
  753. {
  754. if (funct != null)
  755. {
  756. var graph = this.editorUi.editor.graph;
  757. var menu = null;
  758. var show = true;
  759. mxEvent.addListener(elt, 'click', mxUtils.bind(this, function(evt)
  760. {
  761. if (show && (elt.enabled == null || elt.enabled))
  762. {
  763. graph.popupMenuHandler.hideMenu();
  764. menu = new mxPopupMenu(funct);
  765. menu.div.className += ' geToolbarMenu';
  766. menu.showDisabled = showAll;
  767. menu.labels = showLabels;
  768. menu.autoExpand = true;
  769. var offset = mxUtils.getOffset(elt);
  770. menu.popup(offset.x, offset.y + elt.offsetHeight, null, evt);
  771. this.editorUi.setCurrentMenu(menu, elt);
  772. // Workaround for scrollbar hiding menu items
  773. if (!showLabels && menu.div.scrollHeight > menu.div.clientHeight)
  774. {
  775. menu.div.style.width = '40px';
  776. }
  777. menu.hideMenu = mxUtils.bind(this, function()
  778. {
  779. mxPopupMenu.prototype.hideMenu.apply(menu, arguments);
  780. this.editorUi.resetCurrentMenu();
  781. menu.destroy();
  782. });
  783. // Extends destroy to reset global state
  784. menu.addListener(mxEvent.EVENT_HIDE, mxUtils.bind(this, function()
  785. {
  786. this.currentElt = null;
  787. }));
  788. }
  789. show = true;
  790. mxEvent.consume(evt);
  791. }));
  792. // Hides menu if already showing
  793. mxEvent.addListener(elt, 'mousedown', mxUtils.bind(this, function(evt)
  794. {
  795. show = this.currentElt != elt;
  796. // Prevents focus
  797. if (document.documentMode != null && document.documentMode >= 9)
  798. {
  799. evt.preventDefault();
  800. }
  801. }));
  802. }
  803. };
  804. /**
  805. * Adds a handler for showing a menu in the given element.
  806. */
  807. Toolbar.prototype.destroy = function()
  808. {
  809. if (this.gestureHandler != null)
  810. {
  811. mxEvent.removeGestureListeners(document, this.gestureHandler);
  812. this.gestureHandler = null;
  813. }
  814. };