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.

1123 lines
37 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.Button
  9. * @extends Ext.BoxComponent
  10. * Simple Button class
  11. * @cfg {String} text The button text to be used as innerHTML (html tags are accepted)
  12. * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
  13. * CSS property of the button by default, so if you want a mixed icon/text button, set cls:'x-btn-text-icon')
  14. * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event).
  15. * The handler is passed the following parameters:<div class="mdetail-params"><ul>
  16. * <li><code>b</code> : Button<div class="sub-desc">This Button.</div></li>
  17. * <li><code>e</code> : EventObject<div class="sub-desc">The click event.</div></li>
  18. * </ul></div>
  19. * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width).
  20. * See also {@link Ext.Panel}.<tt>{@link Ext.Panel#minButtonWidth minButtonWidth}</tt>.
  21. * @cfg {String/Object} tooltip The tooltip for the button - can be a string to be used as innerHTML (html tags are accepted) or QuickTips config object
  22. * @cfg {Boolean} hidden True to start hidden (defaults to false)
  23. * @cfg {Boolean} disabled True to start disabled (defaults to false)
  24. * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
  25. * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed)
  26. * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
  27. * a {@link Ext.util.ClickRepeater ClickRepeater} config object (defaults to false).
  28. * @constructor
  29. * Create a new button
  30. * @param {Object} config The config object
  31. * @xtype button
  32. */
  33. Ext.Button = Ext.extend(Ext.BoxComponent, {
  34. /**
  35. * Read-only. True if this button is hidden
  36. * @type Boolean
  37. */
  38. hidden : false,
  39. /**
  40. * Read-only. True if this button is disabled
  41. * @type Boolean
  42. */
  43. disabled : false,
  44. /**
  45. * Read-only. True if this button is pressed (only if enableToggle = true)
  46. * @type Boolean
  47. */
  48. pressed : false,
  49. /**
  50. * @cfg {Number} tabIndex Set a DOM tabIndex for this button (defaults to undefined)
  51. */
  52. /**
  53. * @cfg {Boolean} allowDepress
  54. * False to not allow a pressed Button to be depressed (defaults to undefined). Only valid when {@link #enableToggle} is true.
  55. */
  56. /**
  57. * @cfg {Boolean} enableToggle
  58. * True to enable pressed/not pressed toggling (defaults to false)
  59. */
  60. enableToggle : false,
  61. /**
  62. * @cfg {Function} toggleHandler
  63. * Function called when a Button with {@link #enableToggle} set to true is clicked. Two arguments are passed:<ul class="mdetail-params">
  64. * <li><b>button</b> : Ext.Button<div class="sub-desc">this Button object</div></li>
  65. * <li><b>state</b> : Boolean<div class="sub-desc">The next state of the Button, true means pressed.</div></li>
  66. * </ul>
  67. */
  68. /**
  69. * @cfg {Mixed} menu
  70. * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
  71. */
  72. /**
  73. * @cfg {String} menuAlign
  74. * The position to align the menu to (see {@link Ext.Element#alignTo} for more details, defaults to 'tl-bl?').
  75. */
  76. menuAlign : 'tl-bl?',
  77. /**
  78. * @cfg {String} overflowText If used in a {@link Ext.Toolbar Toolbar}, the
  79. * text to be used if this item is shown in the overflow menu. See also
  80. * {@link Ext.Toolbar.Item}.<code>{@link Ext.Toolbar.Item#overflowText overflowText}</code>.
  81. */
  82. /**
  83. * @cfg {String} iconCls
  84. * A css class which sets a background image to be used as the icon for this button
  85. */
  86. /**
  87. * @cfg {String} type
  88. * submit, reset or button - defaults to 'button'
  89. */
  90. type : 'button',
  91. // private
  92. menuClassTarget : 'tr:nth(2)',
  93. /**
  94. * @cfg {String} clickEvent
  95. * The DOM event that will fire the handler of the button. This can be any valid event name (dblclick, contextmenu).
  96. * Defaults to <tt>'click'</tt>.
  97. */
  98. clickEvent : 'click',
  99. /**
  100. * @cfg {Boolean} handleMouseEvents
  101. * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
  102. */
  103. handleMouseEvents : true,
  104. /**
  105. * @cfg {String} tooltipType
  106. * The type of tooltip to use. Either 'qtip' (default) for QuickTips or 'title' for title attribute.
  107. */
  108. tooltipType : 'qtip',
  109. /**
  110. * @cfg {String} buttonSelector
  111. * <p>(Optional) A {@link Ext.DomQuery DomQuery} selector which is used to extract the active, clickable element from the
  112. * DOM structure created.</p>
  113. * <p>When a custom {@link #template} is used, you must ensure that this selector results in the selection of
  114. * a focussable element.</p>
  115. * <p>Defaults to <b><tt>'button:first-child'</tt></b>.</p>
  116. */
  117. buttonSelector : 'button:first-child',
  118. /**
  119. * @cfg {String} scale
  120. * <p>(Optional) The size of the Button. Three values are allowed:</p>
  121. * <ul class="mdetail-params">
  122. * <li>'small'<div class="sub-desc">Results in the button element being 16px high.</div></li>
  123. * <li>'medium'<div class="sub-desc">Results in the button element being 24px high.</div></li>
  124. * <li>'large'<div class="sub-desc">Results in the button element being 32px high.</div></li>
  125. * </ul>
  126. * <p>Defaults to <b><tt>'small'</tt></b>.</p>
  127. */
  128. scale : 'small',
  129. /**
  130. * @cfg {Object} scope The scope (<tt><b>this</b></tt> reference) in which the
  131. * <code>{@link #handler}</code> and <code>{@link #toggleHandler}</code> is
  132. * executed. Defaults to this Button.
  133. */
  134. /**
  135. * @cfg {String} iconAlign
  136. * <p>(Optional) The side of the Button box to render the icon. Four values are allowed:</p>
  137. * <ul class="mdetail-params">
  138. * <li>'top'<div class="sub-desc"></div></li>
  139. * <li>'right'<div class="sub-desc"></div></li>
  140. * <li>'bottom'<div class="sub-desc"></div></li>
  141. * <li>'left'<div class="sub-desc"></div></li>
  142. * </ul>
  143. * <p>Defaults to <b><tt>'left'</tt></b>.</p>
  144. */
  145. iconAlign : 'left',
  146. /**
  147. * @cfg {String} arrowAlign
  148. * <p>(Optional) The side of the Button box to render the arrow if the button has an associated {@link #menu}.
  149. * Two values are allowed:</p>
  150. * <ul class="mdetail-params">
  151. * <li>'right'<div class="sub-desc"></div></li>
  152. * <li>'bottom'<div class="sub-desc"></div></li>
  153. * </ul>
  154. * <p>Defaults to <b><tt>'right'</tt></b>.</p>
  155. */
  156. arrowAlign : 'right',
  157. /**
  158. * @cfg {Ext.Template} template (Optional)
  159. * <p>A {@link Ext.Template Template} used to create the Button's DOM structure.</p>
  160. * Instances, or subclasses which need a different DOM structure may provide a different
  161. * template layout in conjunction with an implementation of {@link #getTemplateArgs}.
  162. * @type Ext.Template
  163. * @property template
  164. */
  165. /**
  166. * @cfg {String} cls
  167. * A CSS class string to apply to the button's main element.
  168. */
  169. /**
  170. * @property menu
  171. * @type Menu
  172. * The {@link Ext.menu.Menu Menu} object associated with this Button when configured with the {@link #menu} config option.
  173. */
  174. initComponent : function(){
  175. Ext.Button.superclass.initComponent.call(this);
  176. this.addEvents(
  177. /**
  178. * @event click
  179. * Fires when this button is clicked
  180. * @param {Button} this
  181. * @param {EventObject} e The click event
  182. */
  183. 'click',
  184. /**
  185. * @event toggle
  186. * Fires when the 'pressed' state of this button changes (only if enableToggle = true)
  187. * @param {Button} this
  188. * @param {Boolean} pressed
  189. */
  190. 'toggle',
  191. /**
  192. * @event mouseover
  193. * Fires when the mouse hovers over the button
  194. * @param {Button} this
  195. * @param {Event} e The event object
  196. */
  197. 'mouseover',
  198. /**
  199. * @event mouseout
  200. * Fires when the mouse exits the button
  201. * @param {Button} this
  202. * @param {Event} e The event object
  203. */
  204. 'mouseout',
  205. /**
  206. * @event menushow
  207. * If this button has a menu, this event fires when it is shown
  208. * @param {Button} this
  209. * @param {Menu} menu
  210. */
  211. 'menushow',
  212. /**
  213. * @event menuhide
  214. * If this button has a menu, this event fires when it is hidden
  215. * @param {Button} this
  216. * @param {Menu} menu
  217. */
  218. 'menuhide',
  219. /**
  220. * @event menutriggerover
  221. * If this button has a menu, this event fires when the mouse enters the menu triggering element
  222. * @param {Button} this
  223. * @param {Menu} menu
  224. * @param {EventObject} e
  225. */
  226. 'menutriggerover',
  227. /**
  228. * @event menutriggerout
  229. * If this button has a menu, this event fires when the mouse leaves the menu triggering element
  230. * @param {Button} this
  231. * @param {Menu} menu
  232. * @param {EventObject} e
  233. */
  234. 'menutriggerout'
  235. );
  236. if(this.menu){
  237. this.menu = Ext.menu.MenuMgr.get(this.menu);
  238. }
  239. if(Ext.isString(this.toggleGroup)){
  240. this.enableToggle = true;
  241. }
  242. },
  243. /**
  244. * <p>This method returns an Array which provides substitution parameters for the {@link #template Template} used
  245. * to create this Button's DOM structure.</p>
  246. * <p>Instances or subclasses which use a different Template to create a different DOM structure may need to provide their
  247. * own implementation of this method.</p>
  248. * <p>The default implementation which provides data for the default {@link #template} returns an Array containing the
  249. * following items:</p><div class="mdetail-params"><ul>
  250. * <li>The &lt;button&gt;'s {@link #type}</li>
  251. * <li>A CSS class name applied to the Button's main &lt;tbody&gt; element which determines the button's scale and icon alignment.</li>
  252. * <li>A CSS class to determine the presence and position of an arrow icon. (<code>'x-btn-arrow'</code> or <code>'x-btn-arrow-bottom'</code> or <code>''</code>)</li>
  253. * <li>The {@link #cls} CSS class name applied to the button's wrapping &lt;table&gt; element.</li>
  254. * <li>The Component id which is applied to the button's wrapping &lt;table&gt; element.</li>
  255. * </ul></div>
  256. * @return {Array} Substitution data for a Template.
  257. */
  258. getTemplateArgs : function(){
  259. return [this.type, 'x-btn-' + this.scale + ' x-btn-icon-' + this.scale + '-' + this.iconAlign, this.getMenuClass(), this.cls, this.id];
  260. },
  261. // private
  262. setButtonClass : function(){
  263. if(this.useSetClass){
  264. if(!Ext.isEmpty(this.oldCls)){
  265. this.el.removeClass([this.oldCls, 'x-btn-pressed']);
  266. }
  267. this.oldCls = (this.iconCls || this.icon) ? (this.text ? ' x-btn-text-icon' : ' x-btn-icon') : ' x-btn-noicon';
  268. this.el.addClass([this.oldCls, this.pressed ? 'x-btn-pressed' : null]);
  269. }
  270. },
  271. // protected
  272. getMenuClass : function(){
  273. return this.menu ? (this.arrowAlign != 'bottom' ? 'x-btn-arrow' : 'x-btn-arrow-bottom') : '';
  274. },
  275. // private
  276. onRender : function(ct, position){
  277. if(!this.template){
  278. if(!Ext.Button.buttonTemplate){
  279. // hideous table template
  280. Ext.Button.buttonTemplate = new Ext.Template(
  281. '<table id="{4}" cellspacing="0" class="x-btn {3}"><tbody class="{1}">',
  282. '<tr><td class="x-btn-tl"><i>&#160;</i></td><td class="x-btn-tc"></td><td class="x-btn-tr"><i>&#160;</i></td></tr>',
  283. '<tr><td class="x-btn-ml"><i>&#160;</i></td><td class="x-btn-mc"><em class="{2}" unselectable="on"><button type="{0}"></button></em></td><td class="x-btn-mr"><i>&#160;</i></td></tr>',
  284. '<tr><td class="x-btn-bl"><i>&#160;</i></td><td class="x-btn-bc"></td><td class="x-btn-br"><i>&#160;</i></td></tr>',
  285. '</tbody></table>');
  286. Ext.Button.buttonTemplate.compile();
  287. }
  288. this.template = Ext.Button.buttonTemplate;
  289. }
  290. var btn, targs = this.getTemplateArgs();
  291. if(position){
  292. btn = this.template.insertBefore(position, targs, true);
  293. }else{
  294. btn = this.template.append(ct, targs, true);
  295. }
  296. /**
  297. * An {@link Ext.Element Element} encapsulating the Button's clickable element. By default,
  298. * this references a <tt>&lt;button&gt;</tt> element. Read only.
  299. * @type Ext.Element
  300. * @property btnEl
  301. */
  302. this.btnEl = btn.child(this.buttonSelector);
  303. this.mon(this.btnEl, {
  304. scope: this,
  305. focus: this.onFocus,
  306. blur: this.onBlur
  307. });
  308. this.initButtonEl(btn, this.btnEl);
  309. Ext.ButtonToggleMgr.register(this);
  310. },
  311. // private
  312. initButtonEl : function(btn, btnEl){
  313. this.el = btn;
  314. this.setIcon(this.icon);
  315. this.setText(this.text);
  316. this.setIconClass(this.iconCls);
  317. if(Ext.isDefined(this.tabIndex)){
  318. btnEl.dom.tabIndex = this.tabIndex;
  319. }
  320. if(this.tooltip){
  321. this.setTooltip(this.tooltip, true);
  322. }
  323. if(this.handleMouseEvents){
  324. this.mon(btn, {
  325. scope: this,
  326. mouseover: this.onMouseOver,
  327. mousedown: this.onMouseDown
  328. });
  329. // new functionality for monitoring on the document level
  330. //this.mon(btn, 'mouseout', this.onMouseOut, this);
  331. }
  332. if(this.menu){
  333. this.mon(this.menu, {
  334. scope: this,
  335. show: this.onMenuShow,
  336. hide: this.onMenuHide
  337. });
  338. }
  339. if(this.repeat){
  340. var repeater = new Ext.util.ClickRepeater(btn, Ext.isObject(this.repeat) ? this.repeat : {});
  341. this.mon(repeater, 'click', this.onClick, this);
  342. }
  343. this.mon(btn, this.clickEvent, this.onClick, this);
  344. },
  345. // private
  346. afterRender : function(){
  347. Ext.Button.superclass.afterRender.call(this);
  348. this.useSetClass = true;
  349. this.setButtonClass();
  350. this.doc = Ext.getDoc();
  351. this.doAutoWidth();
  352. },
  353. /**
  354. * Sets the CSS class that provides a background image to use as the button's icon. This method also changes
  355. * the value of the {@link iconCls} config internally.
  356. * @param {String} cls The CSS class providing the icon image
  357. * @return {Ext.Button} this
  358. */
  359. setIconClass : function(cls){
  360. this.iconCls = cls;
  361. if(this.el){
  362. this.btnEl.dom.className = '';
  363. this.btnEl.addClass(['x-btn-text', cls || '']);
  364. this.setButtonClass();
  365. }
  366. return this;
  367. },
  368. /**
  369. * Sets the tooltip for this Button.
  370. * @param {String/Object} tooltip. This may be:<div class="mdesc-details"><ul>
  371. * <li><b>String</b> : A string to be used as innerHTML (html tags are accepted) to show in a tooltip</li>
  372. * <li><b>Object</b> : A configuration object for {@link Ext.QuickTips#register}.</li>
  373. * </ul></div>
  374. * @return {Ext.Button} this
  375. */
  376. setTooltip : function(tooltip, /* private */ initial){
  377. if(this.rendered){
  378. if(!initial){
  379. this.clearTip();
  380. }
  381. if(Ext.isObject(tooltip)){
  382. Ext.QuickTips.register(Ext.apply({
  383. target: this.btnEl.id
  384. }, tooltip));
  385. this.tooltip = tooltip;
  386. }else{
  387. this.btnEl.dom[this.tooltipType] = tooltip;
  388. }
  389. }else{
  390. this.tooltip = tooltip;
  391. }
  392. return this;
  393. },
  394. // private
  395. clearTip : function(){
  396. if(Ext.isObject(this.tooltip)){
  397. Ext.QuickTips.unregister(this.btnEl);
  398. }
  399. },
  400. // private
  401. beforeDestroy : function(){
  402. if(this.rendered){
  403. this.clearTip();
  404. }
  405. if(this.menu && this.destroyMenu !== false) {
  406. Ext.destroy(this.menu);
  407. }
  408. Ext.destroy(this.repeater);
  409. },
  410. // private
  411. onDestroy : function(){
  412. if(this.rendered){
  413. this.doc.un('mouseover', this.monitorMouseOver, this);
  414. this.doc.un('mouseup', this.onMouseUp, this);
  415. delete this.doc;
  416. delete this.btnEl;
  417. Ext.ButtonToggleMgr.unregister(this);
  418. }
  419. Ext.Button.superclass.onDestroy.call(this);
  420. },
  421. // private
  422. doAutoWidth : function(){
  423. if(this.el && this.text && this.width === undefined){
  424. this.el.setWidth('auto');
  425. if(Ext.isIE7 && Ext.isStrict){
  426. var ib = this.btnEl;
  427. if(ib && ib.getWidth() > 20){
  428. ib.clip();
  429. ib.setWidth(Ext.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
  430. }
  431. }
  432. if(this.minWidth){
  433. if(this.el.getWidth() < this.minWidth){
  434. this.el.setWidth(this.minWidth);
  435. }
  436. }
  437. }
  438. },
  439. /**
  440. * Assigns this Button's click handler
  441. * @param {Function} handler The function to call when the button is clicked
  442. * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function is executed.
  443. * Defaults to this Button.
  444. * @return {Ext.Button} this
  445. */
  446. setHandler : function(handler, scope){
  447. this.handler = handler;
  448. this.scope = scope;
  449. return this;
  450. },
  451. /**
  452. * Sets this Button's text
  453. * @param {String} text The button text
  454. * @return {Ext.Button} this
  455. */
  456. setText : function(text){
  457. this.text = text;
  458. if(this.el){
  459. this.btnEl.update(text || '&#160;');
  460. this.setButtonClass();
  461. }
  462. this.doAutoWidth();
  463. return this;
  464. },
  465. /**
  466. * Sets the background image (inline style) of the button. This method also changes
  467. * the value of the {@link icon} config internally.
  468. * @param {String} icon The path to an image to display in the button
  469. * @return {Ext.Button} this
  470. */
  471. setIcon : function(icon){
  472. this.icon = icon;
  473. if(this.el){
  474. this.btnEl.setStyle('background-image', icon ? 'url(' + icon + ')' : '');
  475. this.setButtonClass();
  476. }
  477. return this;
  478. },
  479. /**
  480. * Gets the text for this Button
  481. * @return {String} The button text
  482. */
  483. getText : function(){
  484. return this.text;
  485. },
  486. /**
  487. * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
  488. * @param {Boolean} state (optional) Force a particular state
  489. * @param {Boolean} supressEvent (optional) True to stop events being fired when calling this method.
  490. * @return {Ext.Button} this
  491. */
  492. toggle : function(state, suppressEvent){
  493. state = state === undefined ? !this.pressed : !!state;
  494. if(state != this.pressed){
  495. if(this.rendered){
  496. this.el[state ? 'addClass' : 'removeClass']('x-btn-pressed');
  497. }
  498. this.pressed = state;
  499. if(!suppressEvent){
  500. this.fireEvent('toggle', this, state);
  501. if(this.toggleHandler){
  502. this.toggleHandler.call(this.scope || this, this, state);
  503. }
  504. }
  505. }
  506. return this;
  507. },
  508. // private
  509. onDisable : function(){
  510. this.onDisableChange(true);
  511. },
  512. // private
  513. onEnable : function(){
  514. this.onDisableChange(false);
  515. },
  516. onDisableChange : function(disabled){
  517. if(this.el){
  518. if(!Ext.isIE6 || !this.text){
  519. this.el[disabled ? 'addClass' : 'removeClass'](this.disabledClass);
  520. }
  521. this.el.dom.disabled = disabled;
  522. }
  523. this.disabled = disabled;
  524. },
  525. /**
  526. * Show this button's menu (if it has one)
  527. */
  528. showMenu : function(){
  529. if(this.rendered && this.menu){
  530. if(this.tooltip){
  531. Ext.QuickTips.getQuickTip().cancelShow(this.btnEl);
  532. }
  533. if(this.menu.isVisible()){
  534. this.menu.hide();
  535. }
  536. this.menu.ownerCt = this;
  537. this.menu.show(this.el, this.menuAlign);
  538. }
  539. return this;
  540. },
  541. /**
  542. * Hide this button's menu (if it has one)
  543. */
  544. hideMenu : function(){
  545. if(this.hasVisibleMenu()){
  546. this.menu.hide();
  547. }
  548. return this;
  549. },
  550. /**
  551. * Returns true if the button has a menu and it is visible
  552. * @return {Boolean}
  553. */
  554. hasVisibleMenu : function(){
  555. return this.menu && this.menu.ownerCt == this && this.menu.isVisible();
  556. },
  557. // private
  558. onClick : function(e){
  559. if(e){
  560. e.preventDefault();
  561. }
  562. if(e.button !== 0){
  563. return;
  564. }
  565. if(!this.disabled){
  566. if(this.enableToggle && (this.allowDepress !== false || !this.pressed)){
  567. this.toggle();
  568. }
  569. if(this.menu && !this.hasVisibleMenu() && !this.ignoreNextClick){
  570. this.showMenu();
  571. }
  572. this.fireEvent('click', this, e);
  573. if(this.handler){
  574. //this.el.removeClass('x-btn-over');
  575. this.handler.call(this.scope || this, this, e);
  576. }
  577. }
  578. },
  579. // private
  580. isMenuTriggerOver : function(e, internal){
  581. return this.menu && !internal;
  582. },
  583. // private
  584. isMenuTriggerOut : function(e, internal){
  585. return this.menu && !internal;
  586. },
  587. // private
  588. onMouseOver : function(e){
  589. if(!this.disabled){
  590. var internal = e.within(this.el, true);
  591. if(!internal){
  592. this.el.addClass('x-btn-over');
  593. if(!this.monitoringMouseOver){
  594. this.doc.on('mouseover', this.monitorMouseOver, this);
  595. this.monitoringMouseOver = true;
  596. }
  597. this.fireEvent('mouseover', this, e);
  598. }
  599. if(this.isMenuTriggerOver(e, internal)){
  600. this.fireEvent('menutriggerover', this, this.menu, e);
  601. }
  602. }
  603. },
  604. // private
  605. monitorMouseOver : function(e){
  606. if(e.target != this.el.dom && !e.within(this.el)){
  607. if(this.monitoringMouseOver){
  608. this.doc.un('mouseover', this.monitorMouseOver, this);
  609. this.monitoringMouseOver = false;
  610. }
  611. this.onMouseOut(e);
  612. }
  613. },
  614. // private
  615. onMouseOut : function(e){
  616. var internal = e.within(this.el) && e.target != this.el.dom;
  617. this.el.removeClass('x-btn-over');
  618. this.fireEvent('mouseout', this, e);
  619. if(this.isMenuTriggerOut(e, internal)){
  620. this.fireEvent('menutriggerout', this, this.menu, e);
  621. }
  622. },
  623. focus : function() {
  624. this.btnEl.focus();
  625. },
  626. blur : function() {
  627. this.btnEl.blur();
  628. },
  629. // private
  630. onFocus : function(e){
  631. if(!this.disabled){
  632. this.el.addClass('x-btn-focus');
  633. }
  634. },
  635. // private
  636. onBlur : function(e){
  637. this.el.removeClass('x-btn-focus');
  638. },
  639. // private
  640. getClickEl : function(e, isUp){
  641. return this.el;
  642. },
  643. // private
  644. onMouseDown : function(e){
  645. if(!this.disabled && e.button === 0){
  646. this.getClickEl(e).addClass('x-btn-click');
  647. this.doc.on('mouseup', this.onMouseUp, this);
  648. }
  649. },
  650. // private
  651. onMouseUp : function(e){
  652. if(e.button === 0){
  653. this.getClickEl(e, true).removeClass('x-btn-click');
  654. this.doc.un('mouseup', this.onMouseUp, this);
  655. }
  656. },
  657. // private
  658. onMenuShow : function(e){
  659. if(this.menu.ownerCt == this){
  660. this.menu.ownerCt = this;
  661. this.ignoreNextClick = 0;
  662. this.el.addClass('x-btn-menu-active');
  663. this.fireEvent('menushow', this, this.menu);
  664. }
  665. },
  666. // private
  667. onMenuHide : function(e){
  668. if(this.menu.ownerCt == this){
  669. this.el.removeClass('x-btn-menu-active');
  670. this.ignoreNextClick = this.restoreClick.defer(250, this);
  671. this.fireEvent('menuhide', this, this.menu);
  672. delete this.menu.ownerCt;
  673. }
  674. },
  675. // private
  676. restoreClick : function(){
  677. this.ignoreNextClick = 0;
  678. }
  679. /**
  680. * @cfg {String} autoEl @hide
  681. */
  682. /**
  683. * @cfg {String/Object} html @hide
  684. */
  685. /**
  686. * @cfg {String} contentEl @hide
  687. */
  688. /**
  689. * @cfg {Mixed} data @hide
  690. */
  691. /**
  692. * @cfg {Mixed} tpl @hide
  693. */
  694. /**
  695. * @cfg {String} tplWriteMode @hide
  696. */
  697. });
  698. Ext.reg('button', Ext.Button);
  699. // Private utility class used by Button
  700. Ext.ButtonToggleMgr = function(){
  701. var groups = {};
  702. function toggleGroup(btn, state){
  703. if(state){
  704. var g = groups[btn.toggleGroup];
  705. for(var i = 0, l = g.length; i < l; i++){
  706. if(g[i] != btn){
  707. g[i].toggle(false);
  708. }
  709. }
  710. }
  711. }
  712. return {
  713. register : function(btn){
  714. if(!btn.toggleGroup){
  715. return;
  716. }
  717. var g = groups[btn.toggleGroup];
  718. if(!g){
  719. g = groups[btn.toggleGroup] = [];
  720. }
  721. g.push(btn);
  722. btn.on('toggle', toggleGroup);
  723. },
  724. unregister : function(btn){
  725. if(!btn.toggleGroup){
  726. return;
  727. }
  728. var g = groups[btn.toggleGroup];
  729. if(g){
  730. g.remove(btn);
  731. btn.un('toggle', toggleGroup);
  732. }
  733. },
  734. /**
  735. * Gets the pressed button in the passed group or null
  736. * @param {String} group
  737. * @return Button
  738. */
  739. getPressed : function(group){
  740. var g = groups[group];
  741. if(g){
  742. for(var i = 0, len = g.length; i < len; i++){
  743. if(g[i].pressed === true){
  744. return g[i];
  745. }
  746. }
  747. }
  748. return null;
  749. }
  750. };
  751. }();
  752. /**
  753. * @class Ext.SplitButton
  754. * @extends Ext.Button
  755. * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
  756. * click event of the button. Typically this would be used to display a dropdown menu that provides additional
  757. * options to the primary button action, but any custom handler can provide the arrowclick implementation. Example usage:
  758. * <pre><code>
  759. // display a dropdown menu:
  760. new Ext.SplitButton({
  761. renderTo: 'button-ct', // the container id
  762. text: 'Options',
  763. handler: optionsHandler, // handle a click on the button itself
  764. menu: new Ext.menu.Menu({
  765. items: [
  766. // these items will render as dropdown menu items when the arrow is clicked:
  767. {text: 'Item 1', handler: item1Handler},
  768. {text: 'Item 2', handler: item2Handler}
  769. ]
  770. })
  771. });
  772. // Instead of showing a menu, you provide any type of custom
  773. // functionality you want when the dropdown arrow is clicked:
  774. new Ext.SplitButton({
  775. renderTo: 'button-ct',
  776. text: 'Options',
  777. handler: optionsHandler,
  778. arrowHandler: myCustomHandler
  779. });
  780. </code></pre>
  781. * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
  782. * @cfg {String} arrowTooltip The title attribute of the arrow
  783. * @constructor
  784. * Create a new menu button
  785. * @param {Object} config The config object
  786. * @xtype splitbutton
  787. */
  788. Ext.SplitButton = Ext.extend(Ext.Button, {
  789. // private
  790. arrowSelector : 'em',
  791. split: true,
  792. // private
  793. initComponent : function(){
  794. Ext.SplitButton.superclass.initComponent.call(this);
  795. /**
  796. * @event arrowclick
  797. * Fires when this button's arrow is clicked
  798. * @param {MenuButton} this
  799. * @param {EventObject} e The click event
  800. */
  801. this.addEvents("arrowclick");
  802. },
  803. // private
  804. onRender : function(){
  805. Ext.SplitButton.superclass.onRender.apply(this, arguments);
  806. if(this.arrowTooltip){
  807. this.el.child(this.arrowSelector).dom[this.tooltipType] = this.arrowTooltip;
  808. }
  809. },
  810. /**
  811. * Sets this button's arrow click handler.
  812. * @param {Function} handler The function to call when the arrow is clicked
  813. * @param {Object} scope (optional) Scope for the function passed above
  814. */
  815. setArrowHandler : function(handler, scope){
  816. this.arrowHandler = handler;
  817. this.scope = scope;
  818. },
  819. getMenuClass : function(){
  820. return 'x-btn-split' + (this.arrowAlign == 'bottom' ? '-bottom' : '');
  821. },
  822. isClickOnArrow : function(e){
  823. if (this.arrowAlign != 'bottom') {
  824. var visBtn = this.el.child('em.x-btn-split');
  825. var right = visBtn.getRegion().right - visBtn.getPadding('r');
  826. return e.getPageX() > right;
  827. } else {
  828. return e.getPageY() > this.btnEl.getRegion().bottom;
  829. }
  830. },
  831. // private
  832. onClick : function(e, t){
  833. e.preventDefault();
  834. if(!this.disabled){
  835. if(this.isClickOnArrow(e)){
  836. if(this.menu && !this.menu.isVisible() && !this.ignoreNextClick){
  837. this.showMenu();
  838. }
  839. this.fireEvent("arrowclick", this, e);
  840. if(this.arrowHandler){
  841. this.arrowHandler.call(this.scope || this, this, e);
  842. }
  843. }else{
  844. if(this.enableToggle){
  845. this.toggle();
  846. }
  847. this.fireEvent("click", this, e);
  848. if(this.handler){
  849. this.handler.call(this.scope || this, this, e);
  850. }
  851. }
  852. }
  853. },
  854. // private
  855. isMenuTriggerOver : function(e){
  856. return this.menu && e.target.tagName == this.arrowSelector;
  857. },
  858. // private
  859. isMenuTriggerOut : function(e, internal){
  860. return this.menu && e.target.tagName != this.arrowSelector;
  861. }
  862. });
  863. Ext.reg('splitbutton', Ext.SplitButton);/**
  864. * @class Ext.CycleButton
  865. * @extends Ext.SplitButton
  866. * A specialized SplitButton that contains a menu of {@link Ext.menu.CheckItem} elements. The button automatically
  867. * cycles through each menu item on click, raising the button's {@link #change} event (or calling the button's
  868. * {@link #changeHandler} function, if supplied) for the active menu item. Clicking on the arrow section of the
  869. * button displays the dropdown menu just like a normal SplitButton. Example usage:
  870. * <pre><code>
  871. var btn = new Ext.CycleButton({
  872. showText: true,
  873. prependText: 'View as ',
  874. items: [{
  875. text:'text only',
  876. iconCls:'view-text',
  877. checked:true
  878. },{
  879. text:'HTML',
  880. iconCls:'view-html'
  881. }],
  882. changeHandler:function(btn, item){
  883. Ext.Msg.alert('Change View', item.text);
  884. }
  885. });
  886. </code></pre>
  887. * @constructor
  888. * Create a new split button
  889. * @param {Object} config The config object
  890. * @xtype cycle
  891. */
  892. Ext.CycleButton = Ext.extend(Ext.SplitButton, {
  893. /**
  894. * @cfg {Array} items An array of {@link Ext.menu.CheckItem} <b>config</b> objects to be used when creating the
  895. * button's menu items (e.g., {text:'Foo', iconCls:'foo-icon'})
  896. */
  897. /**
  898. * @cfg {Boolean} showText True to display the active item's text as the button text (defaults to false)
  899. */
  900. /**
  901. * @cfg {String} prependText A static string to prepend before the active item's text when displayed as the
  902. * button's text (only applies when showText = true, defaults to '')
  903. */
  904. /**
  905. * @cfg {Function} changeHandler A callback function that will be invoked each time the active menu
  906. * item in the button's menu has changed. If this callback is not supplied, the SplitButton will instead
  907. * fire the {@link #change} event on active item change. The changeHandler function will be called with the
  908. * following argument list: (SplitButton this, Ext.menu.CheckItem item)
  909. */
  910. /**
  911. * @cfg {String} forceIcon A css class which sets an image to be used as the static icon for this button. This
  912. * icon will always be displayed regardless of which item is selected in the dropdown list. This overrides the
  913. * default behavior of changing the button's icon to match the selected item's icon on change.
  914. */
  915. /**
  916. * @property menu
  917. * @type Menu
  918. * The {@link Ext.menu.Menu Menu} object used to display the {@link Ext.menu.CheckItem CheckItems} representing the available choices.
  919. */
  920. // private
  921. getItemText : function(item){
  922. if(item && this.showText === true){
  923. var text = '';
  924. if(this.prependText){
  925. text += this.prependText;
  926. }
  927. text += item.text;
  928. return text;
  929. }
  930. return undefined;
  931. },
  932. /**
  933. * Sets the button's active menu item.
  934. * @param {Ext.menu.CheckItem} item The item to activate
  935. * @param {Boolean} suppressEvent True to prevent the button's change event from firing (defaults to false)
  936. */
  937. setActiveItem : function(item, suppressEvent){
  938. if(!Ext.isObject(item)){
  939. item = this.menu.getComponent(item);
  940. }
  941. if(item){
  942. if(!this.rendered){
  943. this.text = this.getItemText(item);
  944. this.iconCls = item.iconCls;
  945. }else{
  946. var t = this.getItemText(item);
  947. if(t){
  948. this.setText(t);
  949. }
  950. this.setIconClass(item.iconCls);
  951. }
  952. this.activeItem = item;
  953. if(!item.checked){
  954. item.setChecked(true, false);
  955. }
  956. if(this.forceIcon){
  957. this.setIconClass(this.forceIcon);
  958. }
  959. if(!suppressEvent){
  960. this.fireEvent('change', this, item);
  961. }
  962. }
  963. },
  964. /**
  965. * Gets the currently active menu item.
  966. * @return {Ext.menu.CheckItem} The active item
  967. */
  968. getActiveItem : function(){
  969. return this.activeItem;
  970. },
  971. // private
  972. initComponent : function(){
  973. this.addEvents(
  974. /**
  975. * @event change
  976. * Fires after the button's active menu item has changed. Note that if a {@link #changeHandler} function
  977. * is set on this CycleButton, it will be called instead on active item change and this change event will
  978. * not be fired.
  979. * @param {Ext.CycleButton} this
  980. * @param {Ext.menu.CheckItem} item The menu item that was selected
  981. */
  982. "change"
  983. );
  984. if(this.changeHandler){
  985. this.on('change', this.changeHandler, this.scope||this);
  986. delete this.changeHandler;
  987. }
  988. this.itemCount = this.items.length;
  989. this.menu = {cls:'x-cycle-menu', items:[]};
  990. var checked = 0;
  991. Ext.each(this.items, function(item, i){
  992. Ext.apply(item, {
  993. group: item.group || this.id,
  994. itemIndex: i,
  995. checkHandler: this.checkHandler,
  996. scope: this,
  997. checked: item.checked || false
  998. });
  999. this.menu.items.push(item);
  1000. if(item.checked){
  1001. checked = i;
  1002. }
  1003. }, this);
  1004. Ext.CycleButton.superclass.initComponent.call(this);
  1005. this.on('click', this.toggleSelected, this);
  1006. this.setActiveItem(checked, true);
  1007. },
  1008. // private
  1009. checkHandler : function(item, pressed){
  1010. if(pressed){
  1011. this.setActiveItem(item);
  1012. }
  1013. },
  1014. /**
  1015. * This is normally called internally on button click, but can be called externally to advance the button's
  1016. * active item programmatically to the next one in the menu. If the current item is the last one in the menu
  1017. * the active item will be set to the first item in the menu.
  1018. */
  1019. toggleSelected : function(){
  1020. var m = this.menu;
  1021. m.render();
  1022. // layout if we haven't before so the items are active
  1023. if(!m.hasLayout){
  1024. m.doLayout();
  1025. }
  1026. var nextIdx, checkItem;
  1027. for (var i = 1; i < this.itemCount; i++) {
  1028. nextIdx = (this.activeItem.itemIndex + i) % this.itemCount;
  1029. // check the potential item
  1030. checkItem = m.items.itemAt(nextIdx);
  1031. // if its not disabled then check it.
  1032. if (!checkItem.disabled) {
  1033. checkItem.setChecked(true);
  1034. break;
  1035. }
  1036. }
  1037. }
  1038. });
  1039. Ext.reg('cycle', Ext.CycleButton);