Upgrade to ExtJS 3.0.3 - Released 10/11/2009
[extjs.git] / examples / ux / Spinner.js
1 /*!
2  * Ext JS Library 3.0.3
3  * Copyright(c) 2006-2009 Ext JS, LLC
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 /**\r
8  * @class Ext.ux.Spinner\r
9  * @extends Ext.util.Observable\r
10  * Creates a Spinner control utilized by Ext.ux.form.SpinnerField\r
11  */\r
12 Ext.ux.Spinner = Ext.extend(Ext.util.Observable, {\r
13     incrementValue: 1,\r
14     alternateIncrementValue: 5,\r
15     triggerClass: 'x-form-spinner-trigger',\r
16     splitterClass: 'x-form-spinner-splitter',\r
17     alternateKey: Ext.EventObject.shiftKey,\r
18     defaultValue: 0,\r
19     accelerate: false,\r
20 \r
21     constructor: function(config){\r
22         Ext.ux.Spinner.superclass.constructor.call(this, config);\r
23         Ext.apply(this, config);\r
24         this.mimicing = false;\r
25     },\r
26 \r
27     init: function(field){\r
28         this.field = field;\r
29 \r
30         field.afterMethod('onRender', this.doRender, this);\r
31         field.afterMethod('onEnable', this.doEnable, this);\r
32         field.afterMethod('onDisable', this.doDisable, this);\r
33         field.afterMethod('afterRender', this.doAfterRender, this);\r
34         field.afterMethod('onResize', this.doResize, this);\r
35         field.afterMethod('onFocus', this.doFocus, this);\r
36         field.beforeMethod('onDestroy', this.doDestroy, this);\r
37     },\r
38 \r
39     doRender: function(ct, position){\r
40         var el = this.el = this.field.getEl();\r
41         var f = this.field;\r
42 \r
43         if (!f.wrap) {\r
44             f.wrap = this.wrap = el.wrap({\r
45                 cls: "x-form-field-wrap"\r
46             });\r
47         }\r
48         else {\r
49             this.wrap = f.wrap.addClass('x-form-field-wrap');\r
50         }\r
51 \r
52         this.trigger = this.wrap.createChild({\r
53             tag: "img",\r
54             src: Ext.BLANK_IMAGE_URL,\r
55             cls: "x-form-trigger " + this.triggerClass\r
56         });\r
57 \r
58         if (!f.width) {\r
59             this.wrap.setWidth(el.getWidth() + this.trigger.getWidth());\r
60         }\r
61 \r
62         this.splitter = this.wrap.createChild({\r
63             tag: 'div',\r
64             cls: this.splitterClass,\r
65             style: 'width:13px; height:2px;'\r
66         });\r
67         this.splitter.setRight((Ext.isIE) ? 1 : 2).setTop(10).show();\r
68 \r
69         this.proxy = this.trigger.createProxy('', this.splitter, true);\r
70         this.proxy.addClass("x-form-spinner-proxy");\r
71         this.proxy.setStyle('left', '0px');\r
72         this.proxy.setSize(14, 1);\r
73         this.proxy.hide();\r
74         this.dd = new Ext.dd.DDProxy(this.splitter.dom.id, "SpinnerDrag", {\r
75             dragElId: this.proxy.id\r
76         });\r
77 \r
78         this.initTrigger();\r
79         this.initSpinner();\r
80     },\r
81 \r
82     doAfterRender: function(){\r
83         var y;\r
84         if (Ext.isIE && this.el.getY() != (y = this.trigger.getY())) {\r
85             this.el.position();\r
86             this.el.setY(y);\r
87         }\r
88     },\r
89 \r
90     doEnable: function(){\r
91         if (this.wrap) {\r
92             this.wrap.removeClass(this.field.disabledClass);\r
93         }\r
94     },\r
95 \r
96     doDisable: function(){\r
97         if (this.wrap) {\r
98             this.wrap.addClass(this.field.disabledClass);\r
99             this.el.removeClass(this.field.disabledClass);\r
100         }\r
101     },\r
102 \r
103     doResize: function(w, h){\r
104         if (typeof w == 'number') {\r
105             this.el.setWidth(w - this.trigger.getWidth());\r
106         }\r
107         this.wrap.setWidth(this.el.getWidth() + this.trigger.getWidth());\r
108     },\r
109 \r
110     doFocus: function(){\r
111         if (!this.mimicing) {\r
112             this.wrap.addClass('x-trigger-wrap-focus');\r
113             this.mimicing = true;\r
114             Ext.get(Ext.isIE ? document.body : document).on("mousedown", this.mimicBlur, this, {\r
115                 delay: 10\r
116             });\r
117             this.el.on('keydown', this.checkTab, this);\r
118         }\r
119     },\r
120 \r
121     // private\r
122     checkTab: function(e){\r
123         if (e.getKey() == e.TAB) {\r
124             this.triggerBlur();\r
125         }\r
126     },\r
127 \r
128     // private\r
129     mimicBlur: function(e){\r
130         if (!this.wrap.contains(e.target) && this.field.validateBlur(e)) {\r
131             this.triggerBlur();\r
132         }\r
133     },\r
134 \r
135     // private\r
136     triggerBlur: function(){\r
137         this.mimicing = false;\r
138         Ext.get(Ext.isIE ? document.body : document).un("mousedown", this.mimicBlur, this);\r
139         this.el.un("keydown", this.checkTab, this);\r
140         this.field.beforeBlur();\r
141         this.wrap.removeClass('x-trigger-wrap-focus');\r
142         this.field.onBlur.call(this.field);\r
143     },\r
144 \r
145     initTrigger: function(){\r
146         this.trigger.addClassOnOver('x-form-trigger-over');\r
147         this.trigger.addClassOnClick('x-form-trigger-click');\r
148     },\r
149 \r
150     initSpinner: function(){\r
151         this.field.addEvents({\r
152             'spin': true,\r
153             'spinup': true,\r
154             'spindown': true\r
155         });\r
156 \r
157         this.keyNav = new Ext.KeyNav(this.el, {\r
158             "up": function(e){\r
159                 e.preventDefault();\r
160                 this.onSpinUp();\r
161             },\r
162 \r
163             "down": function(e){\r
164                 e.preventDefault();\r
165                 this.onSpinDown();\r
166             },\r
167 \r
168             "pageUp": function(e){\r
169                 e.preventDefault();\r
170                 this.onSpinUpAlternate();\r
171             },\r
172 \r
173             "pageDown": function(e){\r
174                 e.preventDefault();\r
175                 this.onSpinDownAlternate();\r
176             },\r
177 \r
178             scope: this\r
179         });\r
180 \r
181         this.repeater = new Ext.util.ClickRepeater(this.trigger, {\r
182             accelerate: this.accelerate\r
183         });\r
184         this.field.mon(this.repeater, "click", this.onTriggerClick, this, {\r
185             preventDefault: true\r
186         });\r
187 \r
188         this.field.mon(this.trigger, {\r
189             mouseover: this.onMouseOver,\r
190             mouseout: this.onMouseOut,\r
191             mousemove: this.onMouseMove,\r
192             mousedown: this.onMouseDown,\r
193             mouseup: this.onMouseUp,\r
194             scope: this,\r
195             preventDefault: true\r
196         });\r
197 \r
198         this.field.mon(this.wrap, "mousewheel", this.handleMouseWheel, this);\r
199 \r
200         this.dd.setXConstraint(0, 0, 10)\r
201         this.dd.setYConstraint(1500, 1500, 10);\r
202         this.dd.endDrag = this.endDrag.createDelegate(this);\r
203         this.dd.startDrag = this.startDrag.createDelegate(this);\r
204         this.dd.onDrag = this.onDrag.createDelegate(this);\r
205     },\r
206 \r
207     onMouseOver: function(){\r
208         if (this.disabled) {\r
209             return;\r
210         }\r
211         var middle = this.getMiddle();\r
212         this.tmpHoverClass = (Ext.EventObject.getPageY() < middle) ? 'x-form-spinner-overup' : 'x-form-spinner-overdown';\r
213         this.trigger.addClass(this.tmpHoverClass);\r
214     },\r
215 \r
216     //private\r
217     onMouseOut: function(){\r
218         this.trigger.removeClass(this.tmpHoverClass);\r
219     },\r
220 \r
221     //private\r
222     onMouseMove: function(){\r
223         if (this.disabled) {\r
224             return;\r
225         }\r
226         var middle = this.getMiddle();\r
227         if (((Ext.EventObject.getPageY() > middle) && this.tmpHoverClass == "x-form-spinner-overup") ||\r
228         ((Ext.EventObject.getPageY() < middle) && this.tmpHoverClass == "x-form-spinner-overdown")) {\r
229         }\r
230     },\r
231 \r
232     //private\r
233     onMouseDown: function(){\r
234         if (this.disabled) {\r
235             return;\r
236         }\r
237         var middle = this.getMiddle();\r
238         this.tmpClickClass = (Ext.EventObject.getPageY() < middle) ? 'x-form-spinner-clickup' : 'x-form-spinner-clickdown';\r
239         this.trigger.addClass(this.tmpClickClass);\r
240     },\r
241 \r
242     //private\r
243     onMouseUp: function(){\r
244         this.trigger.removeClass(this.tmpClickClass);\r
245     },\r
246 \r
247     //private\r
248     onTriggerClick: function(){\r
249         if (this.disabled || this.el.dom.readOnly) {\r
250             return;\r
251         }\r
252         var middle = this.getMiddle();\r
253         var ud = (Ext.EventObject.getPageY() < middle) ? 'Up' : 'Down';\r
254         this['onSpin' + ud]();\r
255     },\r
256 \r
257     //private\r
258     getMiddle: function(){\r
259         var t = this.trigger.getTop();\r
260         var h = this.trigger.getHeight();\r
261         var middle = t + (h / 2);\r
262         return middle;\r
263     },\r
264 \r
265     //private\r
266     //checks if control is allowed to spin\r
267     isSpinnable: function(){\r
268         if (this.disabled || this.el.dom.readOnly) {\r
269             Ext.EventObject.preventDefault(); //prevent scrolling when disabled/readonly\r
270             return false;\r
271         }\r
272         return true;\r
273     },\r
274 \r
275     handleMouseWheel: function(e){\r
276         //disable scrolling when not focused\r
277         if (this.wrap.hasClass('x-trigger-wrap-focus') == false) {\r
278             return;\r
279         }\r
280 \r
281         var delta = e.getWheelDelta();\r
282         if (delta > 0) {\r
283             this.onSpinUp();\r
284             e.stopEvent();\r
285         }\r
286         else\r
287             if (delta < 0) {\r
288                 this.onSpinDown();\r
289                 e.stopEvent();\r
290             }\r
291     },\r
292 \r
293     //private\r
294     startDrag: function(){\r
295         this.proxy.show();\r
296         this._previousY = Ext.fly(this.dd.getDragEl()).getTop();\r
297     },\r
298 \r
299     //private\r
300     endDrag: function(){\r
301         this.proxy.hide();\r
302     },\r
303 \r
304     //private\r
305     onDrag: function(){\r
306         if (this.disabled) {\r
307             return;\r
308         }\r
309         var y = Ext.fly(this.dd.getDragEl()).getTop();\r
310         var ud = '';\r
311 \r
312         if (this._previousY > y) {\r
313             ud = 'Up';\r
314         } //up\r
315         if (this._previousY < y) {\r
316             ud = 'Down';\r
317         } //down\r
318         if (ud != '') {\r
319             this['onSpin' + ud]();\r
320         }\r
321 \r
322         this._previousY = y;\r
323     },\r
324 \r
325     //private\r
326     onSpinUp: function(){\r
327         if (this.isSpinnable() == false) {\r
328             return;\r
329         }\r
330         if (Ext.EventObject.shiftKey == true) {\r
331             this.onSpinUpAlternate();\r
332             return;\r
333         }\r
334         else {\r
335             this.spin(false, false);\r
336         }\r
337         this.field.fireEvent("spin", this);\r
338         this.field.fireEvent("spinup", this);\r
339     },\r
340 \r
341     //private\r
342     onSpinDown: function(){\r
343         if (this.isSpinnable() == false) {\r
344             return;\r
345         }\r
346         if (Ext.EventObject.shiftKey == true) {\r
347             this.onSpinDownAlternate();\r
348             return;\r
349         }\r
350         else {\r
351             this.spin(true, false);\r
352         }\r
353         this.field.fireEvent("spin", this);\r
354         this.field.fireEvent("spindown", this);\r
355     },\r
356 \r
357     //private\r
358     onSpinUpAlternate: function(){\r
359         if (this.isSpinnable() == false) {\r
360             return;\r
361         }\r
362         this.spin(false, true);\r
363         this.field.fireEvent("spin", this);\r
364         this.field.fireEvent("spinup", this);\r
365     },\r
366 \r
367     //private\r
368     onSpinDownAlternate: function(){\r
369         if (this.isSpinnable() == false) {\r
370             return;\r
371         }\r
372         this.spin(true, true);\r
373         this.field.fireEvent("spin", this);\r
374         this.field.fireEvent("spindown", this);\r
375     },\r
376 \r
377     spin: function(down, alternate){\r
378         var v = parseFloat(this.field.getValue());\r
379         var incr = (alternate == true) ? this.alternateIncrementValue : this.incrementValue;\r
380         (down == true) ? v -= incr : v += incr;\r
381 \r
382         v = (isNaN(v)) ? this.defaultValue : v;\r
383         v = this.fixBoundries(v);\r
384         this.field.setRawValue(v);\r
385     },\r
386 \r
387     fixBoundries: function(value){\r
388         var v = value;\r
389 \r
390         if (this.field.minValue != undefined && v < this.field.minValue) {\r
391             v = this.field.minValue;\r
392         }\r
393         if (this.field.maxValue != undefined && v > this.field.maxValue) {\r
394             v = this.field.maxValue;\r
395         }\r
396 \r
397         return this.fixPrecision(v);\r
398     },\r
399 \r
400     // private\r
401     fixPrecision: function(value){\r
402         var nan = isNaN(value);\r
403         if (!this.field.allowDecimals || this.field.decimalPrecision == -1 || nan || !value) {\r
404             return nan ? '' : value;\r
405         }\r
406         return parseFloat(parseFloat(value).toFixed(this.field.decimalPrecision));\r
407     },\r
408 \r
409     doDestroy: function(){\r
410         if (this.trigger) {\r
411             this.trigger.remove();\r
412         }\r
413         if (this.wrap) {\r
414             this.wrap.remove();\r
415             delete this.field.wrap;\r
416         }\r
417 \r
418         if (this.splitter) {\r
419             this.splitter.remove();\r
420         }\r
421 \r
422         if (this.dd) {\r
423             this.dd.unreg();\r
424             this.dd = null;\r
425         }\r
426 \r
427         if (this.proxy) {\r
428             this.proxy.remove();\r
429         }\r
430 \r
431         if (this.repeater) {\r
432             this.repeater.purgeListeners();\r
433         }\r
434     }\r
435 });\r
436 \r
437 //backwards compat\r
438 Ext.form.Spinner = Ext.ux.Spinner;