Menu2.html 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  5. <title>The source code</title>
  6. <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
  7. <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
  8. <style type="text/css">
  9. .highlight { display: block; background-color: #ddd; }
  10. </style>
  11. <script type="text/javascript">
  12. function highlight() {
  13. document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
  14. }
  15. </script>
  16. </head>
  17. <body onload="prettyPrint(); highlight();">
  18. <pre class="prettyprint lang-js"><span id='Ext-menu-Menu'>/**
  19. </span> * A menu object. This is the container to which you may add {@link Ext.menu.Item menu items}.
  20. *
  21. * Menus may contain either {@link Ext.menu.Item menu items}, or general {@link Ext.Component Components}.
  22. * Menus may also contain {@link Ext.panel.AbstractPanel#dockedItems docked items} because it extends {@link Ext.panel.Panel}.
  23. *
  24. * To make a contained general {@link Ext.Component Component} line up with other {@link Ext.menu.Item menu items},
  25. * specify `{@link Ext.menu.Item#plain plain}: true`. This reserves a space for an icon, and indents the Component
  26. * in line with the other menu items.
  27. *
  28. * By default, Menus are absolutely positioned, floating Components. By configuring a Menu with `{@link #floating}: false`,
  29. * a Menu may be used as a child of a {@link Ext.container.Container Container}.
  30. *
  31. * @example
  32. * Ext.create('Ext.menu.Menu', {
  33. * width: 100,
  34. * margin: '0 0 10 0',
  35. * floating: false, // usually you want this set to True (default)
  36. * renderTo: Ext.getBody(), // usually rendered by it's containing component
  37. * items: [{
  38. * text: 'regular item 1'
  39. * },{
  40. * text: 'regular item 2'
  41. * },{
  42. * text: 'regular item 3'
  43. * }]
  44. * });
  45. *
  46. * Ext.create('Ext.menu.Menu', {
  47. * width: 100,
  48. * plain: true,
  49. * floating: false, // usually you want this set to True (default)
  50. * renderTo: Ext.getBody(), // usually rendered by it's containing component
  51. * items: [{
  52. * text: 'plain item 1'
  53. * },{
  54. * text: 'plain item 2'
  55. * },{
  56. * text: 'plain item 3'
  57. * }]
  58. * });
  59. */
  60. Ext.define('Ext.menu.Menu', {
  61. extend: 'Ext.panel.Panel',
  62. alias: 'widget.menu',
  63. requires: [
  64. 'Ext.layout.container.Fit',
  65. 'Ext.layout.container.VBox',
  66. 'Ext.menu.CheckItem',
  67. 'Ext.menu.Item',
  68. 'Ext.menu.KeyNav',
  69. 'Ext.menu.Manager',
  70. 'Ext.menu.Separator'
  71. ],
  72. <span id='Ext-menu-Menu-property-parentMenu'> /**
  73. </span> * @property {Ext.menu.Menu} parentMenu
  74. * The parent Menu of this Menu.
  75. */
  76. <span id='Ext-menu-Menu-cfg-enableKeyNav'> /**
  77. </span> * @cfg {Boolean} [enableKeyNav=true]
  78. * True to enable keyboard navigation for controlling the menu.
  79. * This option should generally be disabled when form fields are
  80. * being used inside the menu.
  81. */
  82. enableKeyNav: true,
  83. <span id='Ext-menu-Menu-cfg-allowOtherMenus'> /**
  84. </span> * @cfg {Boolean} [allowOtherMenus=false]
  85. * True to allow multiple menus to be displayed at the same time.
  86. */
  87. allowOtherMenus: false,
  88. <span id='Ext-menu-Menu-cfg-ariaRole'> /**
  89. </span> * @cfg {String} ariaRole
  90. * @private
  91. */
  92. ariaRole: 'menu',
  93. <span id='Ext-menu-Menu-cfg-autoRender'> /**
  94. </span> * @cfg {Boolean} autoRender
  95. * Floating is true, so autoRender always happens.
  96. * @private
  97. */
  98. <span id='Ext-menu-Menu-cfg-defaultAlign'> /**
  99. </span> * @cfg {String} [defaultAlign=&quot;tl-bl?&quot;]
  100. * The default {@link Ext.Element#getAlignToXY Ext.Element#getAlignToXY} anchor position value for this menu
  101. * relative to its element of origin.
  102. */
  103. defaultAlign: 'tl-bl?',
  104. <span id='Ext-menu-Menu-cfg-floating'> /**
  105. </span> * @cfg {Boolean} [floating=true]
  106. * A Menu configured as `floating: true` (the default) will be rendered as an absolutely positioned,
  107. * {@link Ext.Component#floating floating} {@link Ext.Component Component}. If configured as `floating: false`, the Menu may be
  108. * used as a child item of another {@link Ext.container.Container Container}.
  109. */
  110. floating: true,
  111. <span id='Ext-menu-Menu-cfg-constrain'> /**
  112. </span> * @cfg {Boolean} constrain
  113. * Menus are constrained to the document body by default.
  114. * @private
  115. */
  116. constrain: true,
  117. <span id='Ext-menu-Menu-cfg-hidden'> /**
  118. </span> * @cfg {Boolean} [hidden=undefined]
  119. * True to initially render the Menu as hidden, requiring to be shown manually.
  120. *
  121. * Defaults to `true` when `floating: true`, and defaults to `false` when `floating: false`.
  122. */
  123. hidden: true,
  124. hideMode: 'visibility',
  125. <span id='Ext-menu-Menu-cfg-ignoreParentClicks'> /**
  126. </span> * @cfg {Boolean} [ignoreParentClicks=false]
  127. * True to ignore clicks on any item in this menu that is a parent item (displays a submenu)
  128. * so that the submenu is not dismissed when clicking the parent item.
  129. */
  130. ignoreParentClicks: false,
  131. <span id='Ext-menu-Menu-property-isMenu'> /**
  132. </span> * @property {Boolean} isMenu
  133. * `true` in this class to identify an object as an instantiated Menu, or subclass thereof.
  134. */
  135. isMenu: true,
  136. <span id='Ext-menu-Menu-cfg-layout'> /**
  137. </span> * @cfg {String/Object} layout
  138. * @private
  139. */
  140. <span id='Ext-menu-Menu-cfg-showSeparator'> /**
  141. </span> * @cfg {Boolean} [showSeparator=true]
  142. * True to show the icon separator.
  143. */
  144. showSeparator : true,
  145. <span id='Ext-menu-Menu-cfg-minWidth'> /**
  146. </span> * @cfg {Number} [minWidth=120]
  147. * The minimum width of the Menu. The default minWidth only applies when the {@link #floating} config is true.
  148. */
  149. minWidth: undefined,
  150. defaultMinWidth: 120,
  151. <span id='Ext-menu-Menu-cfg-plain'> /**
  152. </span> * @cfg {Boolean} [plain=false]
  153. * True to remove the incised line down the left side of the menu and to not indent general Component items.
  154. */
  155. initComponent: function() {
  156. var me = this,
  157. prefix = Ext.baseCSSPrefix,
  158. cls = [prefix + 'menu'],
  159. bodyCls = me.bodyCls ? [me.bodyCls] : [],
  160. isFloating = me.floating !== false;
  161. me.addEvents(
  162. <span id='Ext-menu-Menu-event-click'> /**
  163. </span> * @event click
  164. * Fires when this menu is clicked
  165. * @param {Ext.menu.Menu} menu The menu which has been clicked
  166. * @param {Ext.Component} item The menu item that was clicked. `undefined` if not applicable.
  167. * @param {Ext.EventObject} e The underlying {@link Ext.EventObject}.
  168. */
  169. 'click',
  170. <span id='Ext-menu-Menu-event-mouseenter'> /**
  171. </span> * @event mouseenter
  172. * Fires when the mouse enters this menu
  173. * @param {Ext.menu.Menu} menu The menu
  174. * @param {Ext.EventObject} e The underlying {@link Ext.EventObject}
  175. */
  176. 'mouseenter',
  177. <span id='Ext-menu-Menu-event-mouseleave'> /**
  178. </span> * @event mouseleave
  179. * Fires when the mouse leaves this menu
  180. * @param {Ext.menu.Menu} menu The menu
  181. * @param {Ext.EventObject} e The underlying {@link Ext.EventObject}
  182. */
  183. 'mouseleave',
  184. <span id='Ext-menu-Menu-event-mouseover'> /**
  185. </span> * @event mouseover
  186. * Fires when the mouse is hovering over this menu
  187. * @param {Ext.menu.Menu} menu The menu
  188. * @param {Ext.Component} item The menu item that the mouse is over. `undefined` if not applicable.
  189. * @param {Ext.EventObject} e The underlying {@link Ext.EventObject}
  190. */
  191. 'mouseover'
  192. );
  193. Ext.menu.Manager.register(me);
  194. // Menu classes
  195. if (me.plain) {
  196. cls.push(prefix + 'menu-plain');
  197. }
  198. me.cls = cls.join(' ');
  199. // Menu body classes
  200. bodyCls.unshift(prefix + 'menu-body');
  201. me.bodyCls = bodyCls.join(' ');
  202. // Internal vbox layout, with scrolling overflow
  203. // Placed in initComponent (rather than prototype) in order to support dynamic layout/scroller
  204. // options if we wish to allow for such configurations on the Menu.
  205. // e.g., scrolling speed, vbox align stretch, etc.
  206. if (!me.layout) {
  207. me.layout = {
  208. type: 'vbox',
  209. align: 'stretchmax',
  210. overflowHandler: 'Scroller'
  211. };
  212. }
  213. // only apply the minWidth when we're floating &amp; one hasn't already been set
  214. if (isFloating &amp;&amp; me.minWidth === undefined) {
  215. me.minWidth = me.defaultMinWidth;
  216. }
  217. // hidden defaults to false if floating is configured as false
  218. if (!isFloating &amp;&amp; me.initialConfig.hidden !== true) {
  219. me.hidden = false;
  220. }
  221. me.callParent(arguments);
  222. me.on('beforeshow', function() {
  223. var hasItems = !!me.items.length;
  224. // FIXME: When a menu has its show cancelled because of no items, it
  225. // gets a visibility: hidden applied to it (instead of the default display: none)
  226. // Not sure why, but we remove this style when we want to show again.
  227. if (hasItems &amp;&amp; me.rendered) {
  228. me.el.setStyle('visibility', null);
  229. }
  230. return hasItems;
  231. });
  232. },
  233. beforeRender: function() {
  234. this.callParent(arguments);
  235. // Menus are usually floating: true, which means they shrink wrap their items.
  236. // However, when they are contained, and not auto sized, we must stretch the items.
  237. if (!this.getSizeModel().width.shrinkWrap) {
  238. this.layout.align = 'stretch';
  239. }
  240. },
  241. onBoxReady: function() {
  242. var me = this,
  243. separatorSpec;
  244. me.callParent(arguments);
  245. // TODO: Move this to a subTemplate When we support them in the future
  246. if (me.showSeparator) {
  247. separatorSpec = {
  248. cls: Ext.baseCSSPrefix + 'menu-icon-separator',
  249. html: '&amp;#160;'
  250. };
  251. if ((!Ext.isStrict &amp;&amp; Ext.isIE) || Ext.isIE6) {
  252. separatorSpec.style = 'height:' + me.el.getHeight() + 'px';
  253. }
  254. me.iconSepEl = me.layout.getElementTarget().insertFirst(separatorSpec);
  255. }
  256. me.mon(me.el, {
  257. click: me.onClick,
  258. mouseover: me.onMouseOver,
  259. scope: me
  260. });
  261. me.mouseMonitor = me.el.monitorMouseLeave(100, me.onMouseLeave, me);
  262. if (me.enableKeyNav) {
  263. me.keyNav = new Ext.menu.KeyNav(me);
  264. }
  265. },
  266. getBubbleTarget: function() {
  267. // If a submenu, this will have a parentMenu property
  268. // If a menu of a Button, it will have an ownerButton property
  269. // Else use the default method.
  270. return this.parentMenu || this.ownerButton || this.callParent(arguments);
  271. },
  272. <span id='Ext-menu-Menu-method-canActivateItem'> /**
  273. </span> * Returns whether a menu item can be activated or not.
  274. * @return {Boolean}
  275. */
  276. canActivateItem: function(item) {
  277. return item &amp;&amp; !item.isDisabled() &amp;&amp; item.isVisible() &amp;&amp; (item.canActivate || item.getXTypes().indexOf('menuitem') &lt; 0);
  278. },
  279. <span id='Ext-menu-Menu-method-deactivateActiveItem'> /**
  280. </span> * Deactivates the current active item on the menu, if one exists.
  281. */
  282. deactivateActiveItem: function(andBlurFocusedItem) {
  283. var me = this,
  284. activeItem = me.activeItem,
  285. focusedItem = me.focusedItem;
  286. if (activeItem) {
  287. activeItem.deactivate();
  288. if (!activeItem.activated) {
  289. delete me.activeItem;
  290. }
  291. }
  292. // Blur the focused item if we are being asked to do that too
  293. // Only needed if we are being hidden - mouseout does not blur.
  294. if (focusedItem &amp;&amp; andBlurFocusedItem) {
  295. focusedItem.blur();
  296. delete me.focusedItem;
  297. }
  298. },
  299. // inherit docs
  300. getFocusEl: function() {
  301. return this.focusedItem || this.el;
  302. },
  303. // inherit docs
  304. hide: function() {
  305. this.deactivateActiveItem(true);
  306. this.callParent(arguments);
  307. },
  308. // private
  309. getItemFromEvent: function(e) {
  310. return this.getChildByElement(e.getTarget());
  311. },
  312. lookupComponent: function(cmp) {
  313. var me = this;
  314. if (typeof cmp == 'string') {
  315. cmp = me.lookupItemFromString(cmp);
  316. } else if (Ext.isObject(cmp)) {
  317. cmp = me.lookupItemFromObject(cmp);
  318. }
  319. // Apply our minWidth to all of our child components so it's accounted
  320. // for in our VBox layout
  321. cmp.minWidth = cmp.minWidth || me.minWidth;
  322. return cmp;
  323. },
  324. // private
  325. lookupItemFromObject: function(cmp) {
  326. var me = this,
  327. prefix = Ext.baseCSSPrefix,
  328. cls;
  329. if (!cmp.isComponent) {
  330. if (!cmp.xtype) {
  331. cmp = Ext.create('Ext.menu.' + (Ext.isBoolean(cmp.checked) ? 'Check': '') + 'Item', cmp);
  332. } else {
  333. cmp = Ext.ComponentManager.create(cmp, cmp.xtype);
  334. }
  335. }
  336. if (cmp.isMenuItem) {
  337. cmp.parentMenu = me;
  338. }
  339. if (!cmp.isMenuItem &amp;&amp; !cmp.dock) {
  340. cls = [prefix + 'menu-item', prefix + 'menu-item-cmp'];
  341. if (!me.plain &amp;&amp; (cmp.indent === true || cmp.iconCls === 'no-icon')) {
  342. cls.push(prefix + 'menu-item-indent');
  343. }
  344. if (cmp.rendered) {
  345. cmp.el.addCls(cls);
  346. } else {
  347. cmp.cls = (cmp.cls ? cmp.cls : '') + ' ' + cls.join(' ');
  348. }
  349. }
  350. return cmp;
  351. },
  352. // private
  353. lookupItemFromString: function(cmp) {
  354. return (cmp == 'separator' || cmp == '-') ?
  355. new Ext.menu.Separator()
  356. : new Ext.menu.Item({
  357. canActivate: false,
  358. hideOnClick: false,
  359. plain: true,
  360. text: cmp
  361. });
  362. },
  363. onClick: function(e) {
  364. var me = this,
  365. item;
  366. if (me.disabled) {
  367. e.stopEvent();
  368. return;
  369. }
  370. item = (e.type === 'click') ? me.getItemFromEvent(e) : me.activeItem;
  371. if (item &amp;&amp; item.isMenuItem) {
  372. if (!item.menu || !me.ignoreParentClicks) {
  373. item.onClick(e);
  374. } else {
  375. e.stopEvent();
  376. }
  377. }
  378. // Click event may be fired without an item, so we need a second check
  379. if (!item || item.disabled) {
  380. item = undefined;
  381. }
  382. me.fireEvent('click', me, item, e);
  383. },
  384. onDestroy: function() {
  385. var me = this;
  386. Ext.menu.Manager.unregister(me);
  387. delete me.parentMenu;
  388. delete me.ownerButton;
  389. if (me.rendered) {
  390. me.el.un(me.mouseMonitor);
  391. Ext.destroy(me.keyNav);
  392. delete me.keyNav;
  393. }
  394. me.callParent(arguments);
  395. },
  396. onMouseLeave: function(e) {
  397. var me = this;
  398. me.deactivateActiveItem();
  399. if (me.disabled) {
  400. return;
  401. }
  402. me.fireEvent('mouseleave', me, e);
  403. },
  404. onMouseOver: function(e) {
  405. var me = this,
  406. fromEl = e.getRelatedTarget(),
  407. mouseEnter = !me.el.contains(fromEl),
  408. item = me.getItemFromEvent(e),
  409. parentMenu = me.parentMenu,
  410. parentItem = me.parentItem;
  411. if (mouseEnter &amp;&amp; parentMenu) {
  412. parentMenu.setActiveItem(parentItem);
  413. parentItem.cancelDeferHide();
  414. parentMenu.mouseMonitor.mouseenter();
  415. }
  416. if (me.disabled) {
  417. return;
  418. }
  419. // Do not activate the item if the mouseover was within the item, and it's already active
  420. if (item &amp;&amp; !item.activated) {
  421. me.setActiveItem(item);
  422. if (item.activated &amp;&amp; item.expandMenu) {
  423. item.expandMenu();
  424. }
  425. }
  426. if (mouseEnter) {
  427. me.fireEvent('mouseenter', me, e);
  428. }
  429. me.fireEvent('mouseover', me, item, e);
  430. },
  431. setActiveItem: function(item) {
  432. var me = this;
  433. if (item &amp;&amp; (item != me.activeItem)) {
  434. me.deactivateActiveItem();
  435. if (me.canActivateItem(item)) {
  436. if (item.activate) {
  437. item.activate();
  438. if (item.activated) {
  439. me.activeItem = item;
  440. me.focusedItem = item;
  441. me.focus();
  442. }
  443. } else {
  444. item.focus();
  445. me.focusedItem = item;
  446. }
  447. }
  448. item.el.scrollIntoView(me.layout.getRenderTarget());
  449. }
  450. },
  451. <span id='Ext-menu-Menu-method-showBy'> /**
  452. </span> * Shows the floating menu by the specified {@link Ext.Component Component} or {@link Ext.Element Element}.
  453. * @param {Ext.Component/Ext.Element} component The {@link Ext.Component} or {@link Ext.Element} to show the menu by.
  454. * @param {String} [position] Alignment position as used by {@link Ext.Element#getAlignToXY}.
  455. * Defaults to `{@link #defaultAlign}`.
  456. * @param {Number[]} [offsets] Alignment offsets as used by {@link Ext.Element#getAlignToXY}.
  457. * @return {Ext.menu.Menu} This Menu.
  458. */
  459. showBy: function(cmp, pos, off) {
  460. var me = this;
  461. if (me.floating &amp;&amp; cmp) {
  462. me.show();
  463. // Align to Component or Element using setPagePosition because normal show
  464. // methods are container-relative, and we must align to the requested element
  465. // or Component:
  466. me.setPagePosition(me.el.getAlignToXY(cmp.el || cmp, pos || me.defaultAlign, off));
  467. me.setVerticalPosition();
  468. }
  469. return me;
  470. },
  471. show: function() {
  472. var me = this,
  473. parentEl, viewHeight, result,
  474. maxWas = me.maxHeight;
  475. // we need to get scope parent for height constraint
  476. if (!me.rendered){
  477. me.doAutoRender();
  478. }
  479. // constrain the height to the curren viewable area
  480. if (me.floating) {
  481. //if our reset css is scoped, there will be a x-reset wrapper on this menu which we need to skip
  482. parentEl = Ext.fly(me.el.getScopeParent());
  483. viewHeight = parentEl.getViewSize().height;
  484. me.maxHeight = Math.min(maxWas || viewHeight, viewHeight);
  485. }
  486. result = me.callParent(arguments);
  487. me.maxHeight = maxWas;
  488. return result;
  489. },
  490. afterComponentLayout: function(width, height, oldWidth, oldHeight){
  491. var me = this;
  492. me.callParent(arguments);
  493. // fixup the separator
  494. if (me.showSeparator){
  495. me.iconSepEl.setHeight(me.componentLayout.lastComponentSize.contentHeight);
  496. }
  497. },
  498. // private
  499. // adjust the vertical position of the menu if the height of the
  500. // menu is equal (or greater than) the viewport size
  501. setVerticalPosition: function(){
  502. var me = this,
  503. max,
  504. y = me.el.getY(),
  505. returnY = y,
  506. height = me.getHeight(),
  507. viewportHeight = Ext.Element.getViewportHeight().height,
  508. parentEl = Ext.fly(me.el.getScopeParent()),
  509. viewHeight = parentEl.getViewSize().height,
  510. normalY = y - parentEl.getScroll().top; // factor in scrollTop of parent
  511. parentEl = null;
  512. if (me.floating) {
  513. max = me.maxHeight ? me.maxHeight : viewHeight - normalY;
  514. if (height &gt; viewHeight) {
  515. returnY = y - normalY;
  516. } else if (max &lt; height) {
  517. returnY = y - (height - max);
  518. } else if((y + height) &gt; viewportHeight){ // keep the document from scrolling
  519. returnY = viewportHeight - height;
  520. }
  521. }
  522. me.el.setY(returnY);
  523. }
  524. });</pre>
  525. </body>
  526. </html>