MenuItem.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. /**
  2. * MenuItem.js
  3. *
  4. * Copyright, Moxiecode Systems AB
  5. * Released under LGPL License.
  6. *
  7. * License: http://www.tinymce.com/license
  8. * Contributing: http://www.tinymce.com/contributing
  9. */
  10. /**
  11. * Creates a new menu item.
  12. *
  13. * @-x-less MenuItem.less
  14. * @class tinymce.ui.MenuItem
  15. * @extends tinymce.ui.Control
  16. */
  17. define("tinymce/ui/MenuItem", [
  18. "tinymce/ui/Widget",
  19. "tinymce/ui/Factory",
  20. "tinymce/Env"
  21. ], function(Widget, Factory, Env) {
  22. "use strict";
  23. return Widget.extend({
  24. Defaults: {
  25. border: 0,
  26. role: 'menuitem'
  27. },
  28. /**
  29. * Constructs a instance with the specified settings.
  30. *
  31. * @constructor
  32. * @param {Object} settings Name/value object with settings.
  33. * @setting {Boolean} selectable Selectable menu.
  34. * @setting {Array} menu Submenu array with items.
  35. * @setting {String} shortcut Shortcut to display for menu item. Example: Ctrl+X
  36. */
  37. init: function(settings) {
  38. var self = this;
  39. self.hasPopup = true;
  40. self._super(settings);
  41. settings = self.settings;
  42. self.addClass('menu-item');
  43. if (settings.menu) {
  44. self.addClass('menu-item-expand');
  45. }
  46. if (settings.preview) {
  47. self.addClass('menu-item-preview');
  48. }
  49. if (self._text === '-' || self._text === '|') {
  50. self.addClass('menu-item-sep');
  51. self.aria('role', 'separator');
  52. self._text = '-';
  53. }
  54. if (settings.selectable) {
  55. self.aria('role', 'menuitemcheckbox');
  56. self.addClass('menu-item-checkbox');
  57. settings.icon = 'selected';
  58. }
  59. if (!settings.preview && !settings.selectable) {
  60. self.addClass('menu-item-normal');
  61. }
  62. self.on('mousedown', function(e) {
  63. e.preventDefault();
  64. });
  65. if (settings.menu && !settings.ariaHideMenu) {
  66. self.aria('haspopup', true);
  67. }
  68. },
  69. /**
  70. * Returns true/false if the menuitem has sub menu.
  71. *
  72. * @method hasMenus
  73. * @return {Boolean} True/false state if it has submenu.
  74. */
  75. hasMenus: function() {
  76. return !!this.settings.menu;
  77. },
  78. /**
  79. * Shows the menu for the menu item.
  80. *
  81. * @method showMenu
  82. */
  83. showMenu: function() {
  84. var self = this, settings = self.settings, menu, parent = self.parent();
  85. parent.items().each(function(ctrl) {
  86. if (ctrl !== self) {
  87. ctrl.hideMenu();
  88. }
  89. });
  90. if (settings.menu) {
  91. menu = self.menu;
  92. if (!menu) {
  93. menu = settings.menu;
  94. // Is menu array then auto constuct menu control
  95. if (menu.length) {
  96. menu = {
  97. type: 'menu',
  98. items: menu
  99. };
  100. } else {
  101. menu.type = menu.type || 'menu';
  102. }
  103. if (parent.settings.itemDefaults) {
  104. menu.itemDefaults = parent.settings.itemDefaults;
  105. }
  106. menu = self.menu = Factory.create(menu).parent(self).renderTo();
  107. menu.reflow();
  108. menu.fire('show');
  109. menu.on('cancel', function(e) {
  110. e.stopPropagation();
  111. self.focus();
  112. menu.hide();
  113. });
  114. menu.on('hide', function(e) {
  115. if (e.control === menu) {
  116. self.removeClass('selected');
  117. }
  118. });
  119. menu.submenu = true;
  120. } else {
  121. menu.show();
  122. }
  123. menu._parentMenu = parent;
  124. menu.addClass('menu-sub');
  125. var rel = menu.testMoveRel(
  126. self.getEl(),
  127. self.isRtl() ? ['tl-tr', 'bl-br', 'tr-tl', 'br-bl'] : ['tr-tl', 'br-bl', 'tl-tr', 'bl-br']
  128. );
  129. menu.moveRel(self.getEl(), rel);
  130. menu.rel = rel;
  131. rel = 'menu-sub-' + rel;
  132. menu.removeClass(menu._lastRel);
  133. menu.addClass(rel);
  134. menu._lastRel = rel;
  135. self.addClass('selected');
  136. self.aria('expanded', true);
  137. }
  138. },
  139. /**
  140. * Hides the menu for the menu item.
  141. *
  142. * @method hideMenu
  143. */
  144. hideMenu: function() {
  145. var self = this;
  146. if (self.menu) {
  147. self.menu.items().each(function(item) {
  148. if (item.hideMenu) {
  149. item.hideMenu();
  150. }
  151. });
  152. self.menu.hide();
  153. self.aria('expanded', false);
  154. }
  155. return self;
  156. },
  157. /**
  158. * Renders the control as a HTML string.
  159. *
  160. * @method renderHtml
  161. * @return {String} HTML representing the control.
  162. */
  163. renderHtml: function() {
  164. var self = this, id = self._id, settings = self.settings, prefix = self.classPrefix, text = self.encode(self._text);
  165. var icon = self.settings.icon, image = '', shortcut = settings.shortcut;
  166. if (icon) {
  167. self.parent().addClass('menu-has-icons');
  168. }
  169. if (settings.image) {
  170. icon = 'none';
  171. image = ' style="background-image: url(\'' + settings.image + '\')"';
  172. }
  173. if (shortcut && Env.mac) {
  174. // format shortcut for Mac
  175. shortcut = shortcut.replace(/ctrl\+alt\+/i, '⌥⌘'); // ctrl+cmd
  176. shortcut = shortcut.replace(/ctrl\+/i, '⌘'); // ctrl symbol
  177. shortcut = shortcut.replace(/alt\+/i, '⌥'); // cmd symbol
  178. shortcut = shortcut.replace(/shift\+/i, '⇧'); // shift symbol
  179. }
  180. icon = prefix + 'ico ' + prefix + 'i-' + (self.settings.icon || 'none');
  181. return (
  182. '<div id="' + id + '" class="' + self.classes() + '" tabindex="-1">' +
  183. (text !== '-' ? '<i class="' + icon + '"' + image + '></i>\u00a0' : '') +
  184. (text !== '-' ? '<span id="' + id + '-text" class="' + prefix + 'text">' + text + '</span>' : '') +
  185. (shortcut ? '<div id="' + id + '-shortcut" class="' + prefix + 'menu-shortcut">' + shortcut + '</div>' : '') +
  186. (settings.menu ? '<div class="' + prefix + 'caret"></div>' : '') +
  187. '</div>'
  188. );
  189. },
  190. /**
  191. * Gets invoked after the control has been rendered.
  192. *
  193. * @method postRender
  194. */
  195. postRender: function() {
  196. var self = this, settings = self.settings;
  197. var textStyle = settings.textStyle;
  198. if (typeof(textStyle) == "function") {
  199. textStyle = textStyle.call(this);
  200. }
  201. if (textStyle) {
  202. var textElm = self.getEl('text');
  203. if (textElm) {
  204. textElm.setAttribute('style', textStyle);
  205. }
  206. }
  207. self.on('mouseenter click', function(e) {
  208. if (e.control === self) {
  209. if (!settings.menu && e.type === 'click') {
  210. self.fire('select');
  211. self.parent().hideAll();
  212. } else {
  213. self.showMenu();
  214. if (e.aria) {
  215. self.menu.focus(true);
  216. }
  217. }
  218. }
  219. });
  220. self._super();
  221. return self;
  222. },
  223. active: function(state) {
  224. if (typeof(state) != "undefined") {
  225. this.aria('checked', state);
  226. }
  227. return this._super(state);
  228. },
  229. /**
  230. * Removes the control and it's menus.
  231. *
  232. * @method remove
  233. */
  234. remove: function() {
  235. this._super();
  236. if (this.menu) {
  237. this.menu.remove();
  238. }
  239. }
  240. });
  241. });