MenuButton.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. /**
  2. * MenuButton.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 button.
  12. *
  13. * @-x-less MenuButton.less
  14. * @class tinymce.ui.MenuButton
  15. * @extends tinymce.ui.Button
  16. */
  17. define("tinymce/ui/MenuButton", [
  18. "tinymce/ui/Button",
  19. "tinymce/ui/Factory",
  20. "tinymce/ui/MenuBar"
  21. ], function(Button, Factory, MenuBar) {
  22. "use strict";
  23. // TODO: Maybe add as some global function
  24. function isChildOf(node, parent) {
  25. while (node) {
  26. if (parent === node) {
  27. return true;
  28. }
  29. node = node.parentNode;
  30. }
  31. return false;
  32. }
  33. var MenuButton = Button.extend({
  34. /**
  35. * Constructs a instance with the specified settings.
  36. *
  37. * @constructor
  38. * @param {Object} settings Name/value object with settings.
  39. */
  40. init: function(settings) {
  41. var self = this;
  42. self._renderOpen = true;
  43. self._super(settings);
  44. self.addClass('menubtn');
  45. if (settings.fixedWidth) {
  46. self.addClass('fixed-width');
  47. }
  48. self.aria('haspopup', true);
  49. self.hasPopup = true;
  50. },
  51. /**
  52. * Shows the menu for the button.
  53. *
  54. * @method showMenu
  55. */
  56. showMenu: function() {
  57. var self = this, settings = self.settings, menu;
  58. if (self.menu && self.menu.visible()) {
  59. return self.hideMenu();
  60. }
  61. if (!self.menu) {
  62. menu = settings.menu || [];
  63. // Is menu array then auto constuct menu control
  64. if (menu.length) {
  65. menu = {
  66. type: 'menu',
  67. items: menu
  68. };
  69. } else {
  70. menu.type = menu.type || 'menu';
  71. }
  72. self.menu = Factory.create(menu).parent(self).renderTo();
  73. self.fire('createmenu');
  74. self.menu.reflow();
  75. self.menu.on('cancel', function(e) {
  76. if (e.control.parent() === self.menu) {
  77. e.stopPropagation();
  78. self.focus();
  79. self.hideMenu();
  80. }
  81. });
  82. // Move focus to button when a menu item is selected/clicked
  83. self.menu.on('select', function() {
  84. self.focus();
  85. });
  86. self.menu.on('show hide', function(e) {
  87. if (e.control == self.menu) {
  88. self.activeMenu(e.type == 'show');
  89. }
  90. self.aria('expanded', e.type == 'show');
  91. }).fire('show');
  92. }
  93. self.menu.show();
  94. self.menu.layoutRect({w: self.layoutRect().w});
  95. self.menu.moveRel(self.getEl(), self.isRtl() ? ['br-tr', 'tr-br'] : ['bl-tl', 'tl-bl']);
  96. },
  97. /**
  98. * Hides the menu for the button.
  99. *
  100. * @method hideMenu
  101. */
  102. hideMenu: function() {
  103. var self = this;
  104. if (self.menu) {
  105. self.menu.items().each(function(item) {
  106. if (item.hideMenu) {
  107. item.hideMenu();
  108. }
  109. });
  110. self.menu.hide();
  111. }
  112. },
  113. /**
  114. * Sets the active menu state.
  115. *
  116. * @private
  117. */
  118. activeMenu: function(state) {
  119. this.toggleClass('active', state);
  120. },
  121. /**
  122. * Renders the control as a HTML string.
  123. *
  124. * @method renderHtml
  125. * @return {String} HTML representing the control.
  126. */
  127. renderHtml: function() {
  128. var self = this, id = self._id, prefix = self.classPrefix;
  129. var icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + self.settings.icon : '';
  130. self.aria('role', self.parent() instanceof MenuBar ? 'menuitem' : 'button');
  131. return (
  132. '<div id="' + id + '" class="' + self.classes() + '" tabindex="-1" aria-labelledby="' + id + '">' +
  133. '<button id="' + id + '-open" role="presentation" type="button" tabindex="-1">' +
  134. (icon ? '<i class="' + icon + '"></i>' : '') +
  135. '<span>' + (self._text ? (icon ? '\u00a0' : '') + self.encode(self._text) : '') + '</span>' +
  136. ' <i class="' + prefix + 'caret"></i>' +
  137. '</button>' +
  138. '</div>'
  139. );
  140. },
  141. /**
  142. * Gets invoked after the control has been rendered.
  143. *
  144. * @method postRender
  145. */
  146. postRender: function() {
  147. var self = this;
  148. self.on('click', function(e) {
  149. if (e.control === self && isChildOf(e.target, self.getEl())) {
  150. self.showMenu();
  151. if (e.aria) {
  152. self.menu.items()[0].focus();
  153. }
  154. }
  155. });
  156. self.on('mouseenter', function(e) {
  157. var overCtrl = e.control, parent = self.parent(), hasVisibleSiblingMenu;
  158. if (overCtrl && parent && overCtrl instanceof MenuButton && overCtrl.parent() == parent) {
  159. parent.items().filter('MenuButton').each(function(ctrl) {
  160. if (ctrl.hideMenu && ctrl != overCtrl) {
  161. if (ctrl.menu && ctrl.menu.visible()) {
  162. hasVisibleSiblingMenu = true;
  163. }
  164. ctrl.hideMenu();
  165. }
  166. });
  167. if (hasVisibleSiblingMenu) {
  168. overCtrl.focus(); // Fix for: #5887
  169. overCtrl.showMenu();
  170. }
  171. }
  172. });
  173. return self._super();
  174. },
  175. /**
  176. * Sets/gets the current button text.
  177. *
  178. * @method text
  179. * @param {String} [text] New button text.
  180. * @return {String|tinymce.ui.MenuButton} Current text or current MenuButton instance.
  181. */
  182. text: function(text) {
  183. var self = this, i, children;
  184. if (self._rendered) {
  185. children = self.getEl('open').getElementsByTagName('span');
  186. for (i = 0; i < children.length; i++) {
  187. children[i].innerHTML = (self.settings.icon && text ? '\u00a0' : '') + self.encode(text);
  188. }
  189. }
  190. return this._super(text);
  191. },
  192. /**
  193. * Removes the control and it's menus.
  194. *
  195. * @method remove
  196. */
  197. remove: function() {
  198. this._super();
  199. if (this.menu) {
  200. this.menu.remove();
  201. }
  202. }
  203. });
  204. return MenuButton;
  205. });