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