source: trunk/web/addons/job_monarch/lib/extjs-30/pkgs/pkg-buttons-debug.js @ 647

Last change on this file since 647 was 625, checked in by ramonb, 15 years ago

lib/extjs-30:

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