Forked mumble-django project from https://bitbucket.org/Svedrin/mumble-django
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1688 lines
54 KiB

  1. /*!
  2. * Ext JS Library 3.2.0
  3. * Copyright(c) 2006-2010 Ext JS, Inc.
  4. * licensing@extjs.com
  5. * http://www.extjs.com/license
  6. */
  7. /**
  8. * @class Ext.menu.Menu
  9. * @extends Ext.Container
  10. * <p>A menu object. This is the container to which you may add menu items. Menu can also serve as a base class
  11. * when you want a specialized menu based off of another component (like {@link Ext.menu.DateMenu} for example).</p>
  12. * <p>Menus may contain either {@link Ext.menu.Item menu items}, or general {@link Ext.Component Component}s.</p>
  13. * <p>To make a contained general {@link Ext.Component Component} line up with other {@link Ext.menu.Item menu items}
  14. * specify <tt>iconCls: 'no-icon'</tt>. This reserves a space for an icon, and indents the Component in line
  15. * with the other menu items. See {@link Ext.form.ComboBox}.{@link Ext.form.ComboBox#getListParent getListParent}
  16. * for an example.</p>
  17. * <p>By default, Menus are absolutely positioned, floating Components. By configuring a Menu with
  18. * <b><tt>{@link #floating}:false</tt></b>, a Menu may be used as child of a Container.</p>
  19. *
  20. * @xtype menu
  21. */
  22. Ext.menu.Menu = Ext.extend(Ext.Container, {
  23. /**
  24. * @cfg {Object} defaults
  25. * A config object that will be applied to all items added to this container either via the {@link #items}
  26. * config or via the {@link #add} method. The defaults config can contain any number of
  27. * name/value property pairs to be added to each item, and should be valid for the types of items
  28. * being added to the menu.
  29. */
  30. /**
  31. * @cfg {Mixed} items
  32. * An array of items to be added to this menu. Menus may contain either {@link Ext.menu.Item menu items},
  33. * or general {@link Ext.Component Component}s.
  34. */
  35. /**
  36. * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
  37. */
  38. minWidth : 120,
  39. /**
  40. * @cfg {Boolean/String} shadow True or 'sides' for the default effect, 'frame' for 4-way shadow, and 'drop'
  41. * for bottom-right shadow (defaults to 'sides')
  42. */
  43. shadow : 'sides',
  44. /**
  45. * @cfg {String} subMenuAlign The {@link Ext.Element#alignTo} anchor position value to use for submenus of
  46. * this menu (defaults to 'tl-tr?')
  47. */
  48. subMenuAlign : 'tl-tr?',
  49. /**
  50. * @cfg {String} defaultAlign The default {@link Ext.Element#alignTo} anchor position value for this menu
  51. * relative to its element of origin (defaults to 'tl-bl?')
  52. */
  53. defaultAlign : 'tl-bl?',
  54. /**
  55. * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
  56. */
  57. allowOtherMenus : false,
  58. /**
  59. * @cfg {Boolean} ignoreParentClicks True to ignore clicks on any item in this menu that is a parent item (displays
  60. * a submenu) so that the submenu is not dismissed when clicking the parent item (defaults to false).
  61. */
  62. ignoreParentClicks : false,
  63. /**
  64. * @cfg {Boolean} enableScrolling True to allow the menu container to have scroller controls if the menu is too long (defaults to true).
  65. */
  66. enableScrolling : true,
  67. /**
  68. * @cfg {Number} maxHeight The maximum height of the menu. Only applies when enableScrolling is set to True (defaults to null).
  69. */
  70. maxHeight : null,
  71. /**
  72. * @cfg {Number} scrollIncrement The amount to scroll the menu. Only applies when enableScrolling is set to True (defaults to 24).
  73. */
  74. scrollIncrement : 24,
  75. /**
  76. * @cfg {Boolean} showSeparator True to show the icon separator. (defaults to true).
  77. */
  78. showSeparator : true,
  79. /**
  80. * @cfg {Array} defaultOffsets An array specifying the [x, y] offset in pixels by which to
  81. * change the default Menu popup position after aligning according to the {@link #defaultAlign}
  82. * configuration. Defaults to <tt>[0, 0]</tt>.
  83. */
  84. defaultOffsets : [0, 0],
  85. /**
  86. * @cfg {Boolean} plain
  87. * True to remove the incised line down the left side of the menu. Defaults to <tt>false</tt>.
  88. */
  89. plain : false,
  90. /**
  91. * @cfg {Boolean} floating
  92. * <p>By default, a Menu configured as <b><code>floating:true</code></b>
  93. * will be rendered as an {@link Ext.Layer} (an absolutely positioned,
  94. * floating Component with zindex=15000).
  95. * If configured as <b><code>floating:false</code></b>, the Menu may be
  96. * used as child item of another Container instead of a free-floating
  97. * {@link Ext.Layer Layer}.
  98. */
  99. floating : true,
  100. /**
  101. * @cfg {Number} zIndex
  102. * zIndex to use when the menu is floating.
  103. */
  104. zIndex: 15000,
  105. // private
  106. hidden : true,
  107. /**
  108. * @cfg {String/Object} layout
  109. * This class assigns a default layout (<code>layout:'<b>menu</b>'</code>).
  110. * Developers <i>may</i> override this configuration option if another layout is required.
  111. * See {@link Ext.Container#layout} for additional information.
  112. */
  113. layout : 'menu',
  114. hideMode : 'offsets', // Important for laying out Components
  115. scrollerHeight : 8,
  116. autoLayout : true, // Provided for backwards compat
  117. defaultType : 'menuitem',
  118. bufferResize : false,
  119. initComponent : function(){
  120. if(Ext.isArray(this.initialConfig)){
  121. Ext.apply(this, {items:this.initialConfig});
  122. }
  123. this.addEvents(
  124. /**
  125. * @event click
  126. * Fires when this menu is clicked (or when the enter key is pressed while it is active)
  127. * @param {Ext.menu.Menu} this
  128. * @param {Ext.menu.Item} menuItem The menu item that was clicked
  129. * @param {Ext.EventObject} e
  130. */
  131. 'click',
  132. /**
  133. * @event mouseover
  134. * Fires when the mouse is hovering over this menu
  135. * @param {Ext.menu.Menu} this
  136. * @param {Ext.EventObject} e
  137. * @param {Ext.menu.Item} menuItem The menu item that was clicked
  138. */
  139. 'mouseover',
  140. /**
  141. * @event mouseout
  142. * Fires when the mouse exits this menu
  143. * @param {Ext.menu.Menu} this
  144. * @param {Ext.EventObject} e
  145. * @param {Ext.menu.Item} menuItem The menu item that was clicked
  146. */
  147. 'mouseout',
  148. /**
  149. * @event itemclick
  150. * Fires when a menu item contained in this menu is clicked
  151. * @param {Ext.menu.BaseItem} baseItem The BaseItem that was clicked
  152. * @param {Ext.EventObject} e
  153. */
  154. 'itemclick'
  155. );
  156. Ext.menu.MenuMgr.register(this);
  157. if(this.floating){
  158. Ext.EventManager.onWindowResize(this.hide, this);
  159. }else{
  160. if(this.initialConfig.hidden !== false){
  161. this.hidden = false;
  162. }
  163. this.internalDefaults = {hideOnClick: false};
  164. }
  165. Ext.menu.Menu.superclass.initComponent.call(this);
  166. if(this.autoLayout){
  167. var fn = this.doLayout.createDelegate(this, []);
  168. this.on({
  169. add: fn,
  170. remove: fn
  171. });
  172. }
  173. },
  174. //private
  175. getLayoutTarget : function() {
  176. return this.ul;
  177. },
  178. // private
  179. onRender : function(ct, position){
  180. if(!ct){
  181. ct = Ext.getBody();
  182. }
  183. var dh = {
  184. id: this.getId(),
  185. cls: 'x-menu ' + ((this.floating) ? 'x-menu-floating x-layer ' : '') + (this.cls || '') + (this.plain ? ' x-menu-plain' : '') + (this.showSeparator ? '' : ' x-menu-nosep'),
  186. style: this.style,
  187. cn: [
  188. {tag: 'a', cls: 'x-menu-focus', href: '#', onclick: 'return false;', tabIndex: '-1'},
  189. {tag: 'ul', cls: 'x-menu-list'}
  190. ]
  191. };
  192. if(this.floating){
  193. this.el = new Ext.Layer({
  194. shadow: this.shadow,
  195. dh: dh,
  196. constrain: false,
  197. parentEl: ct,
  198. zindex: this.zIndex
  199. });
  200. }else{
  201. this.el = ct.createChild(dh);
  202. }
  203. Ext.menu.Menu.superclass.onRender.call(this, ct, position);
  204. if(!this.keyNav){
  205. this.keyNav = new Ext.menu.MenuNav(this);
  206. }
  207. // generic focus element
  208. this.focusEl = this.el.child('a.x-menu-focus');
  209. this.ul = this.el.child('ul.x-menu-list');
  210. this.mon(this.ul, {
  211. scope: this,
  212. click: this.onClick,
  213. mouseover: this.onMouseOver,
  214. mouseout: this.onMouseOut
  215. });
  216. if(this.enableScrolling){
  217. this.mon(this.el, {
  218. scope: this,
  219. delegate: '.x-menu-scroller',
  220. click: this.onScroll,
  221. mouseover: this.deactivateActive
  222. });
  223. }
  224. },
  225. // private
  226. findTargetItem : function(e){
  227. var t = e.getTarget('.x-menu-list-item', this.ul, true);
  228. if(t && t.menuItemId){
  229. return this.items.get(t.menuItemId);
  230. }
  231. },
  232. // private
  233. onClick : function(e){
  234. var t = this.findTargetItem(e);
  235. if(t){
  236. if(t.isFormField){
  237. this.setActiveItem(t);
  238. }else if(t instanceof Ext.menu.BaseItem){
  239. if(t.menu && this.ignoreParentClicks){
  240. t.expandMenu();
  241. e.preventDefault();
  242. }else if(t.onClick){
  243. t.onClick(e);
  244. this.fireEvent('click', this, t, e);
  245. }
  246. }
  247. }
  248. },
  249. // private
  250. setActiveItem : function(item, autoExpand){
  251. if(item != this.activeItem){
  252. this.deactivateActive();
  253. if((this.activeItem = item).isFormField){
  254. item.focus();
  255. }else{
  256. item.activate(autoExpand);
  257. }
  258. }else if(autoExpand){
  259. item.expandMenu();
  260. }
  261. },
  262. deactivateActive : function(){
  263. var a = this.activeItem;
  264. if(a){
  265. if(a.isFormField){
  266. //Fields cannot deactivate, but Combos must collapse
  267. if(a.collapse){
  268. a.collapse();
  269. }
  270. }else{
  271. a.deactivate();
  272. }
  273. delete this.activeItem;
  274. }
  275. },
  276. // private
  277. tryActivate : function(start, step){
  278. var items = this.items;
  279. for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
  280. var item = items.get(i);
  281. if(!item.disabled && (item.canActivate || item.isFormField)){
  282. this.setActiveItem(item, false);
  283. return item;
  284. }
  285. }
  286. return false;
  287. },
  288. // private
  289. onMouseOver : function(e){
  290. var t = this.findTargetItem(e);
  291. if(t){
  292. if(t.canActivate && !t.disabled){
  293. this.setActiveItem(t, true);
  294. }
  295. }
  296. this.over = true;
  297. this.fireEvent('mouseover', this, e, t);
  298. },
  299. // private
  300. onMouseOut : function(e){
  301. var t = this.findTargetItem(e);
  302. if(t){
  303. if(t == this.activeItem && t.shouldDeactivate && t.shouldDeactivate(e)){
  304. this.activeItem.deactivate();
  305. delete this.activeItem;
  306. }
  307. }
  308. this.over = false;
  309. this.fireEvent('mouseout', this, e, t);
  310. },
  311. // private
  312. onScroll : function(e, t){
  313. if(e){
  314. e.stopEvent();
  315. }
  316. var ul = this.ul.dom, top = Ext.fly(t).is('.x-menu-scroller-top');
  317. ul.scrollTop += this.scrollIncrement * (top ? -1 : 1);
  318. if(top ? ul.scrollTop <= 0 : ul.scrollTop + this.activeMax >= ul.scrollHeight){
  319. this.onScrollerOut(null, t);
  320. }
  321. },
  322. // private
  323. onScrollerIn : function(e, t){
  324. var ul = this.ul.dom, top = Ext.fly(t).is('.x-menu-scroller-top');
  325. if(top ? ul.scrollTop > 0 : ul.scrollTop + this.activeMax < ul.scrollHeight){
  326. Ext.fly(t).addClass(['x-menu-item-active', 'x-menu-scroller-active']);
  327. }
  328. },
  329. // private
  330. onScrollerOut : function(e, t){
  331. Ext.fly(t).removeClass(['x-menu-item-active', 'x-menu-scroller-active']);
  332. },
  333. /**
  334. * If <code>{@link #floating}=true</code>, shows this menu relative to
  335. * another element using {@link #showat}, otherwise uses {@link Ext.Component#show}.
  336. * @param {Mixed} element The element to align to
  337. * @param {String} position (optional) The {@link Ext.Element#alignTo} anchor position to use in aligning to
  338. * the element (defaults to this.defaultAlign)
  339. * @param {Ext.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
  340. */
  341. show : function(el, pos, parentMenu){
  342. if(this.floating){
  343. this.parentMenu = parentMenu;
  344. if(!this.el){
  345. this.render();
  346. this.doLayout(false, true);
  347. }
  348. this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign, this.defaultOffsets), parentMenu);
  349. }else{
  350. Ext.menu.Menu.superclass.show.call(this);
  351. }
  352. },
  353. /**
  354. * Displays this menu at a specific xy position and fires the 'show' event if a
  355. * handler for the 'beforeshow' event does not return false cancelling the operation.
  356. * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
  357. * @param {Ext.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
  358. */
  359. showAt : function(xy, parentMenu){
  360. if(this.fireEvent('beforeshow', this) !== false){
  361. this.parentMenu = parentMenu;
  362. if(!this.el){
  363. this.render();
  364. }
  365. if(this.enableScrolling){
  366. // set the position so we can figure out the constrain value.
  367. this.el.setXY(xy);
  368. //constrain the value, keep the y coordinate the same
  369. xy[1] = this.constrainScroll(xy[1]);
  370. xy = [this.el.adjustForConstraints(xy)[0], xy[1]];
  371. }else{
  372. //constrain to the viewport.
  373. xy = this.el.adjustForConstraints(xy);
  374. }
  375. this.el.setXY(xy);
  376. this.el.show();
  377. Ext.menu.Menu.superclass.onShow.call(this);
  378. if(Ext.isIE){
  379. // internal event, used so we don't couple the layout to the menu
  380. this.fireEvent('autosize', this);
  381. if(!Ext.isIE8){
  382. this.el.repaint();
  383. }
  384. }
  385. this.hidden = false;
  386. this.focus();
  387. this.fireEvent('show', this);
  388. }
  389. },
  390. constrainScroll : function(y){
  391. var max, full = this.ul.setHeight('auto').getHeight(),
  392. returnY = y, normalY, parentEl, scrollTop, viewHeight;
  393. if(this.floating){
  394. parentEl = Ext.fly(this.el.dom.parentNode);
  395. scrollTop = parentEl.getScroll().top;
  396. viewHeight = parentEl.getViewSize().height;
  397. //Normalize y by the scroll position for the parent element. Need to move it into the coordinate space
  398. //of the view.
  399. normalY = y - scrollTop;
  400. max = this.maxHeight ? this.maxHeight : viewHeight - normalY;
  401. if(full > viewHeight) {
  402. max = viewHeight;
  403. //Set returnY equal to (0,0) in view space by reducing y by the value of normalY
  404. returnY = y - normalY;
  405. } else if(max < full) {
  406. returnY = y - (full - max);
  407. max = full;
  408. }
  409. }else{
  410. max = this.getHeight();
  411. }
  412. // Always respect maxHeight
  413. if (this.maxHeight){
  414. max = Math.min(this.maxHeight, max);
  415. }
  416. if(full > max && max > 0){
  417. this.activeMax = max - this.scrollerHeight * 2 - this.el.getFrameWidth('tb') - Ext.num(this.el.shadowOffset, 0);
  418. this.ul.setHeight(this.activeMax);
  419. this.createScrollers();
  420. this.el.select('.x-menu-scroller').setDisplayed('');
  421. }else{
  422. this.ul.setHeight(full);
  423. this.el.select('.x-menu-scroller').setDisplayed('none');
  424. }
  425. this.ul.dom.scrollTop = 0;
  426. return returnY;
  427. },
  428. createScrollers : function(){
  429. if(!this.scroller){
  430. this.scroller = {
  431. pos: 0,
  432. top: this.el.insertFirst({
  433. tag: 'div',
  434. cls: 'x-menu-scroller x-menu-scroller-top',
  435. html: '&#160;'
  436. }),
  437. bottom: this.el.createChild({
  438. tag: 'div',
  439. cls: 'x-menu-scroller x-menu-scroller-bottom',
  440. html: '&#160;'
  441. })
  442. };
  443. this.scroller.top.hover(this.onScrollerIn, this.onScrollerOut, this);
  444. this.scroller.topRepeater = new Ext.util.ClickRepeater(this.scroller.top, {
  445. listeners: {
  446. click: this.onScroll.createDelegate(this, [null, this.scroller.top], false)
  447. }
  448. });
  449. this.scroller.bottom.hover(this.onScrollerIn, this.onScrollerOut, this);
  450. this.scroller.bottomRepeater = new Ext.util.ClickRepeater(this.scroller.bottom, {
  451. listeners: {
  452. click: this.onScroll.createDelegate(this, [null, this.scroller.bottom], false)
  453. }
  454. });
  455. }
  456. },
  457. onLayout : function(){
  458. if(this.isVisible()){
  459. if(this.enableScrolling){
  460. this.constrainScroll(this.el.getTop());
  461. }
  462. if(this.floating){
  463. this.el.sync();
  464. }
  465. }
  466. },
  467. focus : function(){
  468. if(!this.hidden){
  469. this.doFocus.defer(50, this);
  470. }
  471. },
  472. doFocus : function(){
  473. if(!this.hidden){
  474. this.focusEl.focus();
  475. }
  476. },
  477. /**
  478. * Hides this menu and optionally all parent menus
  479. * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
  480. */
  481. hide : function(deep){
  482. if (!this.isDestroyed) {
  483. this.deepHide = deep;
  484. Ext.menu.Menu.superclass.hide.call(this);
  485. delete this.deepHide;
  486. }
  487. },
  488. // private
  489. onHide : function(){
  490. Ext.menu.Menu.superclass.onHide.call(this);
  491. this.deactivateActive();
  492. if(this.el && this.floating){
  493. this.el.hide();
  494. }
  495. var pm = this.parentMenu;
  496. if(this.deepHide === true && pm){
  497. if(pm.floating){
  498. pm.hide(true);
  499. }else{
  500. pm.deactivateActive();
  501. }
  502. }
  503. },
  504. // private
  505. lookupComponent : function(c){
  506. if(Ext.isString(c)){
  507. c = (c == 'separator' || c == '-') ? new Ext.menu.Separator() : new Ext.menu.TextItem(c);
  508. this.applyDefaults(c);
  509. }else{
  510. if(Ext.isObject(c)){
  511. c = this.getMenuItem(c);
  512. }else if(c.tagName || c.el){ // element. Wrap it.
  513. c = new Ext.BoxComponent({
  514. el: c
  515. });
  516. }
  517. }
  518. return c;
  519. },
  520. applyDefaults : function(c){
  521. if(!Ext.isString(c)){
  522. c = Ext.menu.Menu.superclass.applyDefaults.call(this, c);
  523. var d = this.internalDefaults;
  524. if(d){
  525. if(c.events){
  526. Ext.applyIf(c.initialConfig, d);
  527. Ext.apply(c, d);
  528. }else{
  529. Ext.applyIf(c, d);
  530. }
  531. }
  532. }
  533. return c;
  534. },
  535. // private
  536. getMenuItem : function(config){
  537. if(!config.isXType){
  538. if(!config.xtype && Ext.isBoolean(config.checked)){
  539. return new Ext.menu.CheckItem(config)
  540. }
  541. return Ext.create(config, this.defaultType);
  542. }
  543. return config;
  544. },
  545. /**
  546. * Adds a separator bar to the menu
  547. * @return {Ext.menu.Item} The menu item that was added
  548. */
  549. addSeparator : function(){
  550. return this.add(new Ext.menu.Separator());
  551. },
  552. /**
  553. * Adds an {@link Ext.Element} object to the menu
  554. * @param {Mixed} el The element or DOM node to add, or its id
  555. * @return {Ext.menu.Item} The menu item that was added
  556. */
  557. addElement : function(el){
  558. return this.add(new Ext.menu.BaseItem({
  559. el: el
  560. }));
  561. },
  562. /**
  563. * Adds an existing object based on {@link Ext.menu.BaseItem} to the menu
  564. * @param {Ext.menu.Item} item The menu item to add
  565. * @return {Ext.menu.Item} The menu item that was added
  566. */
  567. addItem : function(item){
  568. return this.add(item);
  569. },
  570. /**
  571. * Creates a new {@link Ext.menu.Item} based an the supplied config object and adds it to the menu
  572. * @param {Object} config A MenuItem config object
  573. * @return {Ext.menu.Item} The menu item that was added
  574. */
  575. addMenuItem : function(config){
  576. return this.add(this.getMenuItem(config));
  577. },
  578. /**
  579. * Creates a new {@link Ext.menu.TextItem} with the supplied text and adds it to the menu
  580. * @param {String} text The text to display in the menu item
  581. * @return {Ext.menu.Item} The menu item that was added
  582. */
  583. addText : function(text){
  584. return this.add(new Ext.menu.TextItem(text));
  585. },
  586. //private
  587. onDestroy : function(){
  588. Ext.EventManager.removeResizeListener(this.hide, this);
  589. var pm = this.parentMenu;
  590. if(pm && pm.activeChild == this){
  591. delete pm.activeChild;
  592. }
  593. delete this.parentMenu;
  594. Ext.menu.Menu.superclass.onDestroy.call(this);
  595. Ext.menu.MenuMgr.unregister(this);
  596. if(this.keyNav) {
  597. this.keyNav.disable();
  598. }
  599. var s = this.scroller;
  600. if(s){
  601. Ext.destroy(s.topRepeater, s.bottomRepeater, s.top, s.bottom);
  602. }
  603. Ext.destroy(
  604. this.el,
  605. this.focusEl,
  606. this.ul
  607. );
  608. }
  609. });
  610. Ext.reg('menu', Ext.menu.Menu);
  611. // MenuNav is a private utility class used internally by the Menu
  612. Ext.menu.MenuNav = Ext.extend(Ext.KeyNav, function(){
  613. function up(e, m){
  614. if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
  615. m.tryActivate(m.items.length-1, -1);
  616. }
  617. }
  618. function down(e, m){
  619. if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
  620. m.tryActivate(0, 1);
  621. }
  622. }
  623. return {
  624. constructor : function(menu){
  625. Ext.menu.MenuNav.superclass.constructor.call(this, menu.el);
  626. this.scope = this.menu = menu;
  627. },
  628. doRelay : function(e, h){
  629. var k = e.getKey();
  630. // Keystrokes within a form Field (e.g.: down in a Combo) do not navigate. Allow only TAB
  631. if (this.menu.activeItem && this.menu.activeItem.isFormField && k != e.TAB) {
  632. return false;
  633. }
  634. if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
  635. this.menu.tryActivate(0, 1);
  636. return false;
  637. }
  638. return h.call(this.scope || this, e, this.menu);
  639. },
  640. tab: function(e, m) {
  641. e.stopEvent();
  642. if (e.shiftKey) {
  643. up(e, m);
  644. } else {
  645. down(e, m);
  646. }
  647. },
  648. up : up,
  649. down : down,
  650. right : function(e, m){
  651. if(m.activeItem){
  652. m.activeItem.expandMenu(true);
  653. }
  654. },
  655. left : function(e, m){
  656. m.hide();
  657. if(m.parentMenu && m.parentMenu.activeItem){
  658. m.parentMenu.activeItem.activate();
  659. }
  660. },
  661. enter : function(e, m){
  662. if(m.activeItem){
  663. e.stopPropagation();
  664. m.activeItem.onClick(e);
  665. m.fireEvent('click', this, m.activeItem);
  666. return true;
  667. }
  668. }
  669. };
  670. }());
  671. /**
  672. * @class Ext.menu.MenuMgr
  673. * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
  674. * @singleton
  675. */
  676. Ext.menu.MenuMgr = function(){
  677. var menus, active, groups = {}, attached = false, lastShow = new Date();
  678. // private - called when first menu is created
  679. function init(){
  680. menus = {};
  681. active = new Ext.util.MixedCollection();
  682. Ext.getDoc().addKeyListener(27, function(){
  683. if(active.length > 0){
  684. hideAll();
  685. }
  686. });
  687. }
  688. // private
  689. function hideAll(){
  690. if(active && active.length > 0){
  691. var c = active.clone();
  692. c.each(function(m){
  693. m.hide();
  694. });
  695. return true;
  696. }
  697. return false;
  698. }
  699. // private
  700. function onHide(m){
  701. active.remove(m);
  702. if(active.length < 1){
  703. Ext.getDoc().un("mousedown", onMouseDown);
  704. attached = false;
  705. }
  706. }
  707. // private
  708. function onShow(m){
  709. var last = active.last();
  710. lastShow = new Date();
  711. active.add(m);
  712. if(!attached){
  713. Ext.getDoc().on("mousedown", onMouseDown);
  714. attached = true;
  715. }
  716. if(m.parentMenu){
  717. m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
  718. m.parentMenu.activeChild = m;
  719. }else if(last && !last.isDestroyed && last.isVisible()){
  720. m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
  721. }
  722. }
  723. // private
  724. function onBeforeHide(m){
  725. if(m.activeChild){
  726. m.activeChild.hide();
  727. }
  728. if(m.autoHideTimer){
  729. clearTimeout(m.autoHideTimer);
  730. delete m.autoHideTimer;
  731. }
  732. }
  733. // private
  734. function onBeforeShow(m){
  735. var pm = m.parentMenu;
  736. if(!pm && !m.allowOtherMenus){
  737. hideAll();
  738. }else if(pm && pm.activeChild){
  739. pm.activeChild.hide();
  740. }
  741. }
  742. // private
  743. function onMouseDown(e){
  744. if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
  745. hideAll();
  746. }
  747. }
  748. // private
  749. function onBeforeCheck(mi, state){
  750. if(state){
  751. var g = groups[mi.group];
  752. for(var i = 0, l = g.length; i < l; i++){
  753. if(g[i] != mi){
  754. g[i].setChecked(false);
  755. }
  756. }
  757. }
  758. }
  759. return {
  760. /**
  761. * Hides all menus that are currently visible
  762. * @return {Boolean} success True if any active menus were hidden.
  763. */
  764. hideAll : function(){
  765. return hideAll();
  766. },
  767. // private
  768. register : function(menu){
  769. if(!menus){
  770. init();
  771. }
  772. menus[menu.id] = menu;
  773. menu.on({
  774. beforehide: onBeforeHide,
  775. hide: onHide,
  776. beforeshow: onBeforeShow,
  777. show: onShow
  778. });
  779. },
  780. /**
  781. * Returns a {@link Ext.menu.Menu} object
  782. * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
  783. * be used to generate and return a new Menu instance.
  784. * @return {Ext.menu.Menu} The specified menu, or null if none are found
  785. */
  786. get : function(menu){
  787. if(typeof menu == "string"){ // menu id
  788. if(!menus){ // not initialized, no menus to return
  789. return null;
  790. }
  791. return menus[menu];
  792. }else if(menu.events){ // menu instance
  793. return menu;
  794. }else if(typeof menu.length == 'number'){ // array of menu items?
  795. return new Ext.menu.Menu({items:menu});
  796. }else{ // otherwise, must be a config
  797. return Ext.create(menu, 'menu');
  798. }
  799. },
  800. // private
  801. unregister : function(menu){
  802. delete menus[menu.id];
  803. menu.un("beforehide", onBeforeHide);
  804. menu.un("hide", onHide);
  805. menu.un("beforeshow", onBeforeShow);
  806. menu.un("show", onShow);
  807. },
  808. // private
  809. registerCheckable : function(menuItem){
  810. var g = menuItem.group;
  811. if(g){
  812. if(!groups[g]){
  813. groups[g] = [];
  814. }
  815. groups[g].push(menuItem);
  816. menuItem.on("beforecheckchange", onBeforeCheck);
  817. }
  818. },
  819. // private
  820. unregisterCheckable : function(menuItem){
  821. var g = menuItem.group;
  822. if(g){
  823. groups[g].remove(menuItem);
  824. menuItem.un("beforecheckchange", onBeforeCheck);
  825. }
  826. },
  827. getCheckedItem : function(groupId){
  828. var g = groups[groupId];
  829. if(g){
  830. for(var i = 0, l = g.length; i < l; i++){
  831. if(g[i].checked){
  832. return g[i];
  833. }
  834. }
  835. }
  836. return null;
  837. },
  838. setCheckedItem : function(groupId, itemId){
  839. var g = groups[groupId];
  840. if(g){
  841. for(var i = 0, l = g.length; i < l; i++){
  842. if(g[i].id == itemId){
  843. g[i].setChecked(true);
  844. }
  845. }
  846. }
  847. return null;
  848. }
  849. };
  850. }();
  851. /**
  852. * @class Ext.menu.BaseItem
  853. * @extends Ext.Component
  854. * The base class for all items that render into menus. BaseItem provides default rendering, activated state
  855. * management and base configuration options shared by all menu components.
  856. * @constructor
  857. * Creates a new BaseItem
  858. * @param {Object} config Configuration options
  859. * @xtype menubaseitem
  860. */
  861. Ext.menu.BaseItem = Ext.extend(Ext.Component, {
  862. /**
  863. * @property parentMenu
  864. * @type Ext.menu.Menu
  865. * The parent Menu of this Item.
  866. */
  867. /**
  868. * @cfg {Function} handler
  869. * A function that will handle the click event of this menu item (optional).
  870. * The handler is passed the following parameters:<div class="mdetail-params"><ul>
  871. * <li><code>b</code> : Item<div class="sub-desc">This menu Item.</div></li>
  872. * <li><code>e</code> : EventObject<div class="sub-desc">The click event.</div></li>
  873. * </ul></div>
  874. */
  875. /**
  876. * @cfg {Object} scope
  877. * The scope (<tt><b>this</b></tt> reference) in which the handler function will be called.
  878. */
  879. /**
  880. * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
  881. */
  882. canActivate : false,
  883. /**
  884. * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
  885. */
  886. activeClass : "x-menu-item-active",
  887. /**
  888. * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
  889. */
  890. hideOnClick : true,
  891. /**
  892. * @cfg {Number} clickHideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 1)
  893. */
  894. clickHideDelay : 1,
  895. // private
  896. ctype : "Ext.menu.BaseItem",
  897. // private
  898. actionMode : "container",
  899. initComponent : function(){
  900. Ext.menu.BaseItem.superclass.initComponent.call(this);
  901. this.addEvents(
  902. /**
  903. * @event click
  904. * Fires when this item is clicked
  905. * @param {Ext.menu.BaseItem} this
  906. * @param {Ext.EventObject} e
  907. */
  908. 'click',
  909. /**
  910. * @event activate
  911. * Fires when this item is activated
  912. * @param {Ext.menu.BaseItem} this
  913. */
  914. 'activate',
  915. /**
  916. * @event deactivate
  917. * Fires when this item is deactivated
  918. * @param {Ext.menu.BaseItem} this
  919. */
  920. 'deactivate'
  921. );
  922. if(this.handler){
  923. this.on("click", this.handler, this.scope);
  924. }
  925. },
  926. // private
  927. onRender : function(container, position){
  928. Ext.menu.BaseItem.superclass.onRender.apply(this, arguments);
  929. if(this.ownerCt && this.ownerCt instanceof Ext.menu.Menu){
  930. this.parentMenu = this.ownerCt;
  931. }else{
  932. this.container.addClass('x-menu-list-item');
  933. this.mon(this.el, {
  934. scope: this,
  935. click: this.onClick,
  936. mouseenter: this.activate,
  937. mouseleave: this.deactivate
  938. });
  939. }
  940. },
  941. /**
  942. * Sets the function that will handle click events for this item (equivalent to passing in the {@link #handler}
  943. * config property). If an existing handler is already registered, it will be unregistered for you.
  944. * @param {Function} handler The function that should be called on click
  945. * @param {Object} scope The scope (<code>this</code> reference) in which the handler function is executed. Defaults to this menu item.
  946. */
  947. setHandler : function(handler, scope){
  948. if(this.handler){
  949. this.un("click", this.handler, this.scope);
  950. }
  951. this.on("click", this.handler = handler, this.scope = scope);
  952. },
  953. // private
  954. onClick : function(e){
  955. if(!this.disabled && this.fireEvent("click", this, e) !== false
  956. && (this.parentMenu && this.parentMenu.fireEvent("itemclick", this, e) !== false)){
  957. this.handleClick(e);
  958. }else{
  959. e.stopEvent();
  960. }
  961. },
  962. // private
  963. activate : function(){
  964. if(this.disabled){
  965. return false;
  966. }
  967. var li = this.container;
  968. li.addClass(this.activeClass);
  969. this.region = li.getRegion().adjust(2, 2, -2, -2);
  970. this.fireEvent("activate", this);
  971. return true;
  972. },
  973. // private
  974. deactivate : function(){
  975. this.container.removeClass(this.activeClass);
  976. this.fireEvent("deactivate", this);
  977. },
  978. // private
  979. shouldDeactivate : function(e){
  980. return !this.region || !this.region.contains(e.getPoint());
  981. },
  982. // private
  983. handleClick : function(e){
  984. var pm = this.parentMenu;
  985. if(this.hideOnClick){
  986. if(pm.floating){
  987. pm.hide.defer(this.clickHideDelay, pm, [true]);
  988. }else{
  989. pm.deactivateActive();
  990. }
  991. }
  992. },
  993. // private. Do nothing
  994. expandMenu : Ext.emptyFn,
  995. // private. Do nothing
  996. hideMenu : Ext.emptyFn
  997. });
  998. Ext.reg('menubaseitem', Ext.menu.BaseItem);/**
  999. * @class Ext.menu.TextItem
  1000. * @extends Ext.menu.BaseItem
  1001. * Adds a static text string to a menu, usually used as either a heading or group separator.
  1002. * @constructor
  1003. * Creates a new TextItem
  1004. * @param {Object/String} config If config is a string, it is used as the text to display, otherwise it
  1005. * is applied as a config object (and should contain a <tt>text</tt> property).
  1006. * @xtype menutextitem
  1007. */
  1008. Ext.menu.TextItem = Ext.extend(Ext.menu.BaseItem, {
  1009. /**
  1010. * @cfg {String} text The text to display for this item (defaults to '')
  1011. */
  1012. /**
  1013. * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
  1014. */
  1015. hideOnClick : false,
  1016. /**
  1017. * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
  1018. */
  1019. itemCls : "x-menu-text",
  1020. constructor : function(config){
  1021. if(typeof config == 'string'){
  1022. config = {text: config}
  1023. }
  1024. Ext.menu.TextItem.superclass.constructor.call(this, config);
  1025. },
  1026. // private
  1027. onRender : function(){
  1028. var s = document.createElement("span");
  1029. s.className = this.itemCls;
  1030. s.innerHTML = this.text;
  1031. this.el = s;
  1032. Ext.menu.TextItem.superclass.onRender.apply(this, arguments);
  1033. }
  1034. });
  1035. Ext.reg('menutextitem', Ext.menu.TextItem);/**
  1036. * @class Ext.menu.Separator
  1037. * @extends Ext.menu.BaseItem
  1038. * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
  1039. * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
  1040. * @constructor
  1041. * @param {Object} config Configuration options
  1042. * @xtype menuseparator
  1043. */
  1044. Ext.menu.Separator = Ext.extend(Ext.menu.BaseItem, {
  1045. /**
  1046. * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
  1047. */
  1048. itemCls : "x-menu-sep",
  1049. /**
  1050. * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
  1051. */
  1052. hideOnClick : false,
  1053. /**
  1054. * @cfg {String} activeClass
  1055. * @hide
  1056. */
  1057. activeClass: '',
  1058. // private
  1059. onRender : function(li){
  1060. var s = document.createElement("span");
  1061. s.className = this.itemCls;
  1062. s.innerHTML = "&#160;";
  1063. this.el = s;
  1064. li.addClass("x-menu-sep-li");
  1065. Ext.menu.Separator.superclass.onRender.apply(this, arguments);
  1066. }
  1067. });
  1068. Ext.reg('menuseparator', Ext.menu.Separator);/**
  1069. * @class Ext.menu.Item
  1070. * @extends Ext.menu.BaseItem
  1071. * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
  1072. * display items. Item extends the base functionality of {@link Ext.menu.BaseItem} by adding menu-specific
  1073. * activation and click handling.
  1074. * @constructor
  1075. * Creates a new Item
  1076. * @param {Object} config Configuration options
  1077. * @xtype menuitem
  1078. */
  1079. Ext.menu.Item = Ext.extend(Ext.menu.BaseItem, {
  1080. /**
  1081. * @property menu
  1082. * @type Ext.menu.Menu
  1083. * The submenu associated with this Item if one was configured.
  1084. */
  1085. /**
  1086. * @cfg {Mixed} menu (optional) Either an instance of {@link Ext.menu.Menu} or the config object for an
  1087. * {@link Ext.menu.Menu} which acts as the submenu when this item is activated.
  1088. */
  1089. /**
  1090. * @cfg {String} icon The path to an icon to display in this item (defaults to Ext.BLANK_IMAGE_URL). If
  1091. * icon is specified {@link #iconCls} should not be.
  1092. */
  1093. /**
  1094. * @cfg {String} iconCls A CSS class that specifies a background image that will be used as the icon for
  1095. * this item (defaults to ''). If iconCls is specified {@link #icon} should not be.
  1096. */
  1097. /**
  1098. * @cfg {String} text The text to display in this item (defaults to '').
  1099. */
  1100. /**
  1101. * @cfg {String} href The href attribute to use for the underlying anchor link (defaults to '#').
  1102. */
  1103. /**
  1104. * @cfg {String} hrefTarget The target attribute to use for the underlying anchor link (defaults to '').
  1105. */
  1106. /**
  1107. * @cfg {String} itemCls The default CSS class to use for menu items (defaults to 'x-menu-item')
  1108. */
  1109. itemCls : 'x-menu-item',
  1110. /**
  1111. * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
  1112. */
  1113. canActivate : true,
  1114. /**
  1115. * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
  1116. */
  1117. showDelay: 200,
  1118. // doc'd in BaseItem
  1119. hideDelay: 200,
  1120. // private
  1121. ctype: 'Ext.menu.Item',
  1122. initComponent : function(){
  1123. Ext.menu.Item.superclass.initComponent.call(this);
  1124. if(this.menu){
  1125. this.menu = Ext.menu.MenuMgr.get(this.menu);
  1126. this.menu.ownerCt = this;
  1127. }
  1128. },
  1129. // private
  1130. onRender : function(container, position){
  1131. if (!this.itemTpl) {
  1132. this.itemTpl = Ext.menu.Item.prototype.itemTpl = new Ext.XTemplate(
  1133. '<a id="{id}" class="{cls}" hidefocus="true" unselectable="on" href="{href}"',
  1134. '<tpl if="hrefTarget">',
  1135. ' target="{hrefTarget}"',
  1136. '</tpl>',
  1137. '>',
  1138. '<img src="{icon}" class="x-menu-item-icon {iconCls}"/>',
  1139. '<span class="x-menu-item-text">{text}</span>',
  1140. '</a>'
  1141. );
  1142. }
  1143. var a = this.getTemplateArgs();
  1144. this.el = position ? this.itemTpl.insertBefore(position, a, true) : this.itemTpl.append(container, a, true);
  1145. this.iconEl = this.el.child('img.x-menu-item-icon');
  1146. this.textEl = this.el.child('.x-menu-item-text');
  1147. if(!this.href) { // if no link defined, prevent the default anchor event
  1148. this.mon(this.el, 'click', Ext.emptyFn, null, { preventDefault: true });
  1149. }
  1150. Ext.menu.Item.superclass.onRender.call(this, container, position);
  1151. },
  1152. getTemplateArgs: function() {
  1153. return {
  1154. id: this.id,
  1155. cls: this.itemCls + (this.menu ? ' x-menu-item-arrow' : '') + (this.cls ? ' ' + this.cls : ''),
  1156. href: this.href || '#',
  1157. hrefTarget: this.hrefTarget,
  1158. icon: this.icon || Ext.BLANK_IMAGE_URL,
  1159. iconCls: this.iconCls || '',
  1160. text: this.itemText||this.text||'&#160;'
  1161. };
  1162. },
  1163. /**
  1164. * Sets the text to display in this menu item
  1165. * @param {String} text The text to display
  1166. */
  1167. setText : function(text){
  1168. this.text = text||'&#160;';
  1169. if(this.rendered){
  1170. this.textEl.update(this.text);
  1171. this.parentMenu.layout.doAutoSize();
  1172. }
  1173. },
  1174. /**
  1175. * Sets the CSS class to apply to the item's icon element
  1176. * @param {String} cls The CSS class to apply
  1177. */
  1178. setIconClass : function(cls){
  1179. var oldCls = this.iconCls;
  1180. this.iconCls = cls;
  1181. if(this.rendered){
  1182. this.iconEl.replaceClass(oldCls, this.iconCls);
  1183. }
  1184. },
  1185. //private
  1186. beforeDestroy: function(){
  1187. if (this.menu){
  1188. delete this.menu.ownerCt;
  1189. this.menu.destroy();
  1190. }
  1191. Ext.menu.Item.superclass.beforeDestroy.call(this);
  1192. },
  1193. // private
  1194. handleClick : function(e){
  1195. if(!this.href){ // if no link defined, stop the event automatically
  1196. e.stopEvent();
  1197. }
  1198. Ext.menu.Item.superclass.handleClick.apply(this, arguments);
  1199. },
  1200. // private
  1201. activate : function(autoExpand){
  1202. if(Ext.menu.Item.superclass.activate.apply(this, arguments)){
  1203. this.focus();
  1204. if(autoExpand){
  1205. this.expandMenu();
  1206. }
  1207. }
  1208. return true;
  1209. },
  1210. // private
  1211. shouldDeactivate : function(e){
  1212. if(Ext.menu.Item.superclass.shouldDeactivate.call(this, e)){
  1213. if(this.menu && this.menu.isVisible()){
  1214. return !this.menu.getEl().getRegion().contains(e.getPoint());
  1215. }
  1216. return true;
  1217. }
  1218. return false;
  1219. },
  1220. // private
  1221. deactivate : function(){
  1222. Ext.menu.Item.superclass.deactivate.apply(this, arguments);
  1223. this.hideMenu();
  1224. },
  1225. // private
  1226. expandMenu : function(autoActivate){
  1227. if(!this.disabled && this.menu){
  1228. clearTimeout(this.hideTimer);
  1229. delete this.hideTimer;
  1230. if(!this.menu.isVisible() && !this.showTimer){
  1231. this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
  1232. }else if (this.menu.isVisible() && autoActivate){
  1233. this.menu.tryActivate(0, 1);
  1234. }
  1235. }
  1236. },
  1237. // private
  1238. deferExpand : function(autoActivate){
  1239. delete this.showTimer;
  1240. this.menu.show(this.container, this.parentMenu.subMenuAlign || 'tl-tr?', this.parentMenu);
  1241. if(autoActivate){
  1242. this.menu.tryActivate(0, 1);
  1243. }
  1244. },
  1245. // private
  1246. hideMenu : function(){
  1247. clearTimeout(this.showTimer);
  1248. delete this.showTimer;
  1249. if(!this.hideTimer && this.menu && this.menu.isVisible()){
  1250. this.hideTimer = this.deferHide.defer(this.hideDelay, this);
  1251. }
  1252. },
  1253. // private
  1254. deferHide : function(){
  1255. delete this.hideTimer;
  1256. if(this.menu.over){
  1257. this.parentMenu.setActiveItem(this, false);
  1258. }else{
  1259. this.menu.hide();
  1260. }
  1261. }
  1262. });
  1263. Ext.reg('menuitem', Ext.menu.Item);/**
  1264. * @class Ext.menu.CheckItem
  1265. * @extends Ext.menu.Item
  1266. * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
  1267. * @constructor
  1268. * Creates a new CheckItem
  1269. * @param {Object} config Configuration options
  1270. * @xtype menucheckitem
  1271. */
  1272. Ext.menu.CheckItem = Ext.extend(Ext.menu.Item, {
  1273. /**
  1274. * @cfg {String} group
  1275. * All check items with the same group name will automatically be grouped into a single-select
  1276. * radio button group (defaults to '')
  1277. */
  1278. /**
  1279. * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
  1280. */
  1281. itemCls : "x-menu-item x-menu-check-item",
  1282. /**
  1283. * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
  1284. */
  1285. groupClass : "x-menu-group-item",
  1286. /**
  1287. * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false). Note that
  1288. * if this checkbox is part of a radio group (group = true) only the last item in the group that is
  1289. * initialized with checked = true will be rendered as checked.
  1290. */
  1291. checked: false,
  1292. // private
  1293. ctype: "Ext.menu.CheckItem",
  1294. initComponent : function(){
  1295. Ext.menu.CheckItem.superclass.initComponent.call(this);
  1296. this.addEvents(
  1297. /**
  1298. * @event beforecheckchange
  1299. * Fires before the checked value is set, providing an opportunity to cancel if needed
  1300. * @param {Ext.menu.CheckItem} this
  1301. * @param {Boolean} checked The new checked value that will be set
  1302. */
  1303. "beforecheckchange" ,
  1304. /**
  1305. * @event checkchange
  1306. * Fires after the checked value has been set
  1307. * @param {Ext.menu.CheckItem} this
  1308. * @param {Boolean} checked The checked value that was set
  1309. */
  1310. "checkchange"
  1311. );
  1312. /**
  1313. * A function that handles the checkchange event. The function is undefined by default, but if an implementation
  1314. * is provided, it will be called automatically when the checkchange event fires.
  1315. * @param {Ext.menu.CheckItem} this
  1316. * @param {Boolean} checked The checked value that was set
  1317. * @method checkHandler
  1318. */
  1319. if(this.checkHandler){
  1320. this.on('checkchange', this.checkHandler, this.scope);
  1321. }
  1322. Ext.menu.MenuMgr.registerCheckable(this);
  1323. },
  1324. // private
  1325. onRender : function(c){
  1326. Ext.menu.CheckItem.superclass.onRender.apply(this, arguments);
  1327. if(this.group){
  1328. this.el.addClass(this.groupClass);
  1329. }
  1330. if(this.checked){
  1331. this.checked = false;
  1332. this.setChecked(true, true);
  1333. }
  1334. },
  1335. // private
  1336. destroy : function(){
  1337. Ext.menu.MenuMgr.unregisterCheckable(this);
  1338. Ext.menu.CheckItem.superclass.destroy.apply(this, arguments);
  1339. },
  1340. /**
  1341. * Set the checked state of this item
  1342. * @param {Boolean} checked The new checked value
  1343. * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
  1344. */
  1345. setChecked : function(state, suppressEvent){
  1346. var suppress = suppressEvent === true;
  1347. if(this.checked != state && (suppress || this.fireEvent("beforecheckchange", this, state) !== false)){
  1348. if(this.container){
  1349. this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
  1350. }
  1351. this.checked = state;
  1352. if(!suppress){
  1353. this.fireEvent("checkchange", this, state);
  1354. }
  1355. }
  1356. },
  1357. // private
  1358. handleClick : function(e){
  1359. if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
  1360. this.setChecked(!this.checked);
  1361. }
  1362. Ext.menu.CheckItem.superclass.handleClick.apply(this, arguments);
  1363. }
  1364. });
  1365. Ext.reg('menucheckitem', Ext.menu.CheckItem);/**
  1366. * @class Ext.menu.DateMenu
  1367. * @extends Ext.menu.Menu
  1368. * <p>A menu containing an {@link Ext.DatePicker} Component.</p>
  1369. * <p>Notes:</p><div class="mdetail-params"><ul>
  1370. * <li>Although not listed here, the <b>constructor</b> for this class
  1371. * accepts all of the configuration options of <b>{@link Ext.DatePicker}</b>.</li>
  1372. * <li>If subclassing DateMenu, any configuration options for the DatePicker must be
  1373. * applied to the <tt><b>initialConfig</b></tt> property of the DateMenu.
  1374. * Applying {@link Ext.DatePicker DatePicker} configuration settings to
  1375. * <b><tt>this</tt></b> will <b>not</b> affect the DatePicker's configuration.</li>
  1376. * </ul></div>
  1377. * @xtype datemenu
  1378. */
  1379. Ext.menu.DateMenu = Ext.extend(Ext.menu.Menu, {
  1380. /**
  1381. * @cfg {Boolean} enableScrolling
  1382. * @hide
  1383. */
  1384. enableScrolling : false,
  1385. /**
  1386. * @cfg {Function} handler
  1387. * Optional. A function that will handle the select event of this menu.
  1388. * The handler is passed the following parameters:<div class="mdetail-params"><ul>
  1389. * <li><code>picker</code> : DatePicker<div class="sub-desc">The Ext.DatePicker.</div></li>
  1390. * <li><code>date</code> : Date<div class="sub-desc">The selected date.</div></li>
  1391. * </ul></div>
  1392. */
  1393. /**
  1394. * @cfg {Object} scope
  1395. * The scope (<tt><b>this</b></tt> reference) in which the <code>{@link #handler}</code>
  1396. * function will be called. Defaults to this DateMenu instance.
  1397. */
  1398. /**
  1399. * @cfg {Boolean} hideOnClick
  1400. * False to continue showing the menu after a date is selected, defaults to true.
  1401. */
  1402. hideOnClick : true,
  1403. /**
  1404. * @cfg {String} pickerId
  1405. * An id to assign to the underlying date picker. Defaults to <tt>null</tt>.
  1406. */
  1407. pickerId : null,
  1408. /**
  1409. * @cfg {Number} maxHeight
  1410. * @hide
  1411. */
  1412. /**
  1413. * @cfg {Number} scrollIncrement
  1414. * @hide
  1415. */
  1416. /**
  1417. * The {@link Ext.DatePicker} instance for this DateMenu
  1418. * @property picker
  1419. * @type DatePicker
  1420. */
  1421. cls : 'x-date-menu',
  1422. /**
  1423. * @event click
  1424. * @hide
  1425. */
  1426. /**
  1427. * @event itemclick
  1428. * @hide
  1429. */
  1430. initComponent : function(){
  1431. this.on('beforeshow', this.onBeforeShow, this);
  1432. if(this.strict = (Ext.isIE7 && Ext.isStrict)){
  1433. this.on('show', this.onShow, this, {single: true, delay: 20});
  1434. }
  1435. Ext.apply(this, {
  1436. plain: true,
  1437. showSeparator: false,
  1438. items: this.picker = new Ext.DatePicker(Ext.applyIf({
  1439. internalRender: this.strict || !Ext.isIE,
  1440. ctCls: 'x-menu-date-item',
  1441. id: this.pickerId
  1442. }, this.initialConfig))
  1443. });
  1444. this.picker.purgeListeners();
  1445. Ext.menu.DateMenu.superclass.initComponent.call(this);
  1446. /**
  1447. * @event select
  1448. * Fires when a date is selected from the {@link #picker Ext.DatePicker}
  1449. * @param {DatePicker} picker The {@link #picker Ext.DatePicker}
  1450. * @param {Date} date The selected date
  1451. */
  1452. this.relayEvents(this.picker, ['select']);
  1453. this.on('show', this.picker.focus, this.picker);
  1454. this.on('select', this.menuHide, this);
  1455. if(this.handler){
  1456. this.on('select', this.handler, this.scope || this);
  1457. }
  1458. },
  1459. menuHide : function() {
  1460. if(this.hideOnClick){
  1461. this.hide(true);
  1462. }
  1463. },
  1464. onBeforeShow : function(){
  1465. if(this.picker){
  1466. this.picker.hideMonthPicker(true);
  1467. }
  1468. },
  1469. onShow : function(){
  1470. var el = this.picker.getEl();
  1471. el.setWidth(el.getWidth()); //nasty hack for IE7 strict mode
  1472. }
  1473. });
  1474. Ext.reg('datemenu', Ext.menu.DateMenu);
  1475. /**
  1476. * @class Ext.menu.ColorMenu
  1477. * @extends Ext.menu.Menu
  1478. * <p>A menu containing a {@link Ext.ColorPalette} Component.</p>
  1479. * <p>Notes:</p><div class="mdetail-params"><ul>
  1480. * <li>Although not listed here, the <b>constructor</b> for this class
  1481. * accepts all of the configuration options of <b>{@link Ext.ColorPalette}</b>.</li>
  1482. * <li>If subclassing ColorMenu, any configuration options for the ColorPalette must be
  1483. * applied to the <tt><b>initialConfig</b></tt> property of the ColorMenu.
  1484. * Applying {@link Ext.ColorPalette ColorPalette} configuration settings to
  1485. * <b><tt>this</tt></b> will <b>not</b> affect the ColorPalette's configuration.</li>
  1486. * </ul></div> *
  1487. * @xtype colormenu
  1488. */
  1489. Ext.menu.ColorMenu = Ext.extend(Ext.menu.Menu, {
  1490. /**
  1491. * @cfg {Boolean} enableScrolling
  1492. * @hide
  1493. */
  1494. enableScrolling : false,
  1495. /**
  1496. * @cfg {Function} handler
  1497. * Optional. A function that will handle the select event of this menu.
  1498. * The handler is passed the following parameters:<div class="mdetail-params"><ul>
  1499. * <li><code>palette</code> : ColorPalette<div class="sub-desc">The {@link #palette Ext.ColorPalette}.</div></li>
  1500. * <li><code>color</code> : String<div class="sub-desc">The 6-digit color hex code (without the # symbol).</div></li>
  1501. * </ul></div>
  1502. */
  1503. /**
  1504. * @cfg {Object} scope
  1505. * The scope (<tt><b>this</b></tt> reference) in which the <code>{@link #handler}</code>
  1506. * function will be called. Defaults to this ColorMenu instance.
  1507. */
  1508. /**
  1509. * @cfg {Boolean} hideOnClick
  1510. * False to continue showing the menu after a color is selected, defaults to true.
  1511. */
  1512. hideOnClick : true,
  1513. cls : 'x-color-menu',
  1514. /**
  1515. * @cfg {String} paletteId
  1516. * An id to assign to the underlying color palette. Defaults to <tt>null</tt>.
  1517. */
  1518. paletteId : null,
  1519. /**
  1520. * @cfg {Number} maxHeight
  1521. * @hide
  1522. */
  1523. /**
  1524. * @cfg {Number} scrollIncrement
  1525. * @hide
  1526. */
  1527. /**
  1528. * @property palette
  1529. * @type ColorPalette
  1530. * The {@link Ext.ColorPalette} instance for this ColorMenu
  1531. */
  1532. /**
  1533. * @event click
  1534. * @hide
  1535. */
  1536. /**
  1537. * @event itemclick
  1538. * @hide
  1539. */
  1540. initComponent : function(){
  1541. Ext.apply(this, {
  1542. plain: true,
  1543. showSeparator: false,
  1544. items: this.palette = new Ext.ColorPalette(Ext.applyIf({
  1545. id: this.paletteId
  1546. }, this.initialConfig))
  1547. });
  1548. this.palette.purgeListeners();
  1549. Ext.menu.ColorMenu.superclass.initComponent.call(this);
  1550. /**
  1551. * @event select
  1552. * Fires when a color is selected from the {@link #palette Ext.ColorPalette}
  1553. * @param {Ext.ColorPalette} palette The {@link #palette Ext.ColorPalette}
  1554. * @param {String} color The 6-digit color hex code (without the # symbol)
  1555. */
  1556. this.relayEvents(this.palette, ['select']);
  1557. this.on('select', this.menuHide, this);
  1558. if(this.handler){
  1559. this.on('select', this.handler, this.scope || this);
  1560. }
  1561. },
  1562. menuHide : function(){
  1563. if(this.hideOnClick){
  1564. this.hide(true);
  1565. }
  1566. }
  1567. });
  1568. Ext.reg('colormenu', Ext.menu.ColorMenu);