Upgrade to ExtJS 4.0.0 - Released 04/26/2011
[extjs.git] / src / util / ClickRepeater.js
1 /**
2  * @class Ext.util.ClickRepeater
3  * @extends Ext.util.Observable
4  *
5  * A wrapper class which can be applied to any element. Fires a "click" event while the
6  * mouse is pressed. The interval between firings may be specified in the config but
7  * defaults to 20 milliseconds.
8  *
9  * Optionally, a CSS class may be applied to the element during the time it is pressed.
10  *
11  * @constructor
12  * @param {Mixed} el The element to listen on
13  * @param {Object} config
14  */
15
16 Ext.define('Ext.util.ClickRepeater', {
17     extend: 'Ext.util.Observable',
18
19     constructor : function(el, config){
20         this.el = Ext.get(el);
21         this.el.unselectable();
22
23         Ext.apply(this, config);
24
25         this.addEvents(
26         /**
27          * @event mousedown
28          * Fires when the mouse button is depressed.
29          * @param {Ext.util.ClickRepeater} this
30          * @param {Ext.EventObject} e
31          */
32         "mousedown",
33         /**
34          * @event click
35          * Fires on a specified interval during the time the element is pressed.
36          * @param {Ext.util.ClickRepeater} this
37          * @param {Ext.EventObject} e
38          */
39         "click",
40         /**
41          * @event mouseup
42          * Fires when the mouse key is released.
43          * @param {Ext.util.ClickRepeater} this
44          * @param {Ext.EventObject} e
45          */
46         "mouseup"
47         );
48
49         if(!this.disabled){
50             this.disabled = true;
51             this.enable();
52         }
53
54         // allow inline handler
55         if(this.handler){
56             this.on("click", this.handler,  this.scope || this);
57         }
58
59         this.callParent();
60     },
61
62     /**
63      * @cfg {Mixed} el The element to act as a button.
64      */
65
66     /**
67      * @cfg {String} pressedCls A CSS class name to be applied to the element while pressed.
68      */
69
70     /**
71      * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
72      * "interval" and "delay" are ignored.
73      */
74
75     /**
76      * @cfg {Number} interval The interval between firings of the "click" event. Default 20 ms.
77      */
78     interval : 20,
79
80     /**
81      * @cfg {Number} delay The initial delay before the repeating event begins firing.
82      * Similar to an autorepeat key delay.
83      */
84     delay: 250,
85
86     /**
87      * @cfg {Boolean} preventDefault True to prevent the default click event
88      */
89     preventDefault : true,
90     /**
91      * @cfg {Boolean} stopDefault True to stop the default click event
92      */
93     stopDefault : false,
94
95     timer : 0,
96
97     /**
98      * Enables the repeater and allows events to fire.
99      */
100     enable: function(){
101         if(this.disabled){
102             this.el.on('mousedown', this.handleMouseDown, this);
103             if (Ext.isIE){
104                 this.el.on('dblclick', this.handleDblClick, this);
105             }
106             if(this.preventDefault || this.stopDefault){
107                 this.el.on('click', this.eventOptions, this);
108             }
109         }
110         this.disabled = false;
111     },
112
113     /**
114      * Disables the repeater and stops events from firing.
115      */
116     disable: function(/* private */ force){
117         if(force || !this.disabled){
118             clearTimeout(this.timer);
119             if(this.pressedCls){
120                 this.el.removeCls(this.pressedCls);
121             }
122             Ext.getDoc().un('mouseup', this.handleMouseUp, this);
123             this.el.removeAllListeners();
124         }
125         this.disabled = true;
126     },
127
128     /**
129      * Convenience function for setting disabled/enabled by boolean.
130      * @param {Boolean} disabled
131      */
132     setDisabled: function(disabled){
133         this[disabled ? 'disable' : 'enable']();
134     },
135
136     eventOptions: function(e){
137         if(this.preventDefault){
138             e.preventDefault();
139         }
140         if(this.stopDefault){
141             e.stopEvent();
142         }
143     },
144
145     // private
146     destroy : function() {
147         this.disable(true);
148         Ext.destroy(this.el);
149         this.clearListeners();
150     },
151
152     handleDblClick : function(e){
153         clearTimeout(this.timer);
154         this.el.blur();
155
156         this.fireEvent("mousedown", this, e);
157         this.fireEvent("click", this, e);
158     },
159
160     // private
161     handleMouseDown : function(e){
162         clearTimeout(this.timer);
163         this.el.blur();
164         if(this.pressedCls){
165             this.el.addCls(this.pressedCls);
166         }
167         this.mousedownTime = new Date();
168
169         Ext.getDoc().on("mouseup", this.handleMouseUp, this);
170         this.el.on("mouseout", this.handleMouseOut, this);
171
172         this.fireEvent("mousedown", this, e);
173         this.fireEvent("click", this, e);
174
175         // Do not honor delay or interval if acceleration wanted.
176         if (this.accelerate) {
177             this.delay = 400;
178         }
179
180         // Re-wrap the event object in a non-shared object, so it doesn't lose its context if
181         // the global shared EventObject gets a new Event put into it before the timer fires.
182         e = new Ext.EventObjectImpl(e);
183
184         this.timer =  Ext.defer(this.click, this.delay || this.interval, this, [e]);
185     },
186
187     // private
188     click : function(e){
189         this.fireEvent("click", this, e);
190         this.timer =  Ext.defer(this.click, this.accelerate ?
191             this.easeOutExpo(Ext.Date.getElapsed(this.mousedownTime),
192                 400,
193                 -390,
194                 12000) :
195             this.interval, this, [e]);
196     },
197
198     easeOutExpo : function (t, b, c, d) {
199         return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
200     },
201
202     // private
203     handleMouseOut : function(){
204         clearTimeout(this.timer);
205         if(this.pressedCls){
206             this.el.removeCls(this.pressedCls);
207         }
208         this.el.on("mouseover", this.handleMouseReturn, this);
209     },
210
211     // private
212     handleMouseReturn : function(){
213         this.el.un("mouseover", this.handleMouseReturn, this);
214         if(this.pressedCls){
215             this.el.addCls(this.pressedCls);
216         }
217         this.click();
218     },
219
220     // private
221     handleMouseUp : function(e){
222         clearTimeout(this.timer);
223         this.el.un("mouseover", this.handleMouseReturn, this);
224         this.el.un("mouseout", this.handleMouseOut, this);
225         Ext.getDoc().un("mouseup", this.handleMouseUp, this);
226         if(this.pressedCls){
227             this.el.removeCls(this.pressedCls);
228         }
229         this.fireEvent("mouseup", this, e);
230     }
231 });