JQuerySpinBtn.js
9.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
/* SpinButton control
*
* Adds bells and whistles to any ordinary textbox to
* make it look and feel like a SpinButton Control.
*
* Originally written by George Adamson, Software Unity (george.jquery@softwareunity.com) August 2006.
* - Added min/max options
* - Added step size option
* - Added bigStep (page up/down) option
*
* Modifications made by Mark Gibson, (mgibson@designlinks.net) September 2006:
* - Converted to jQuery plugin
* - Allow limited or unlimited min/max values
* - Allow custom class names, and add class to input element
* - Removed global vars
* - Reset (to original or through config) when invalid value entered
* - Repeat whilst holding mouse button down (with initial pause, like keyboard repeat)
* - Support mouse wheel in Firefox
* - Fix double click in IE
* - Refactored some code and renamed some vars
*
* Modifications by Jeff Schiller, June 2009:
* - provide callback function for when the value changes based on the following
* http://www.mail-archive.com/jquery-en@googlegroups.com/msg36070.html
* Modifications by Jeff Schiller, July 2009:
* - improve styling for widget in Opera
* - consistent key-repeat handling cross-browser
* Modifications by Alexis Deveria, October 2009:
* - provide "stepfunc" callback option to allow custom function to run when changing a value
* - Made adjustValue(0) only run on certain keyup events, not all.
*
* Tested in IE6, Opera9, Firefox 1.5
* v1.0 11 Aug 2006 - George Adamson - First release
* v1.1 Aug 2006 - George Adamson - Minor enhancements
* v1.2 27 Sep 2006 - Mark Gibson - Major enhancements
* v1.3a 28 Sep 2006 - George Adamson - Minor enhancements
* v1.4 18 Jun 2009 - Jeff Schiller - Added callback function
* v1.5 06 Jul 2009 - Jeff Schiller - Fixes for Opera.
* v1.6 13 Oct 2009 - Alexis Deveria - Added stepfunc function
* v1.7 21 Oct 2009 - Alexis Deveria - Minor fixes
* Fast-repeat for keys and live updating as you type.
* v1.8 12 Jan 2010 - Benjamin Thomas - Fixes for mouseout behavior.
* Added smallStep
Sample usage:
// Create group of settings to initialise spinbutton(s). (Optional)
var myOptions = {
min: 0, // Set lower limit.
max: 100, // Set upper limit.
step: 1, // Set increment size.
smallStep: 0.5, // Set shift-click increment size.
spinClass: mySpinBtnClass, // CSS class to style the spinbutton. (Class also specifies url of the up/down button image.)
upClass: mySpinUpClass, // CSS class for style when mouse over up button.
downClass: mySpinDnClass // CSS class for style when mouse over down button.
}
$(document).ready(function(){
// Initialise INPUT element(s) as SpinButtons: (passing options if desired)
$("#myInputElement").SpinButton(myOptions);
});
*/
$.fn.SpinButton = function(cfg){
return this.each(function(){
this.repeating = false;
// Apply specified options or defaults:
// (Ought to refactor this some day to use $.extend() instead)
this.spinCfg = {
//min: cfg && cfg.min ? Number(cfg.min) : null,
//max: cfg && cfg.max ? Number(cfg.max) : null,
min: cfg && !isNaN(parseFloat(cfg.min)) ? Number(cfg.min) : null, // Fixes bug with min:0
max: cfg && !isNaN(parseFloat(cfg.max)) ? Number(cfg.max) : null,
step: cfg && cfg.step ? Number(cfg.step) : 1,
stepfunc: cfg && cfg.stepfunc ? cfg.stepfunc : false,
page: cfg && cfg.page ? Number(cfg.page) : 10,
upClass: cfg && cfg.upClass ? cfg.upClass : 'up',
downClass: cfg && cfg.downClass ? cfg.downClass : 'down',
reset: cfg && cfg.reset ? cfg.reset : this.value,
delay: cfg && cfg.delay ? Number(cfg.delay) : 500,
interval: cfg && cfg.interval ? Number(cfg.interval) : 100,
_btn_width: 20,
_direction: null,
_delay: null,
_repeat: null,
callback: cfg && cfg.callback ? cfg.callback : null
};
// if a smallStep isn't supplied, use half the regular step
this.spinCfg.smallStep = cfg && cfg.smallStep ? cfg.smallStep : this.spinCfg.step/2;
this.adjustValue = function(i){
var v;
if(isNaN(this.value)) {
v = this.spinCfg.reset;
} else if($.isFunction(this.spinCfg.stepfunc)) {
v = this.spinCfg.stepfunc(this, i);
} else {
// weirdest javascript bug ever: 5.1 + 0.1 = 5.199999999
v = Number((Number(this.value) + Number(i)).toFixed(5));
}
if (this.spinCfg.min !== null) v = Math.max(v, this.spinCfg.min);
if (this.spinCfg.max !== null) v = Math.min(v, this.spinCfg.max);
this.value = v;
if ($.isFunction(this.spinCfg.callback)) this.spinCfg.callback(this);
};
$(this)
.addClass(cfg && cfg.spinClass ? cfg.spinClass : 'spin-button')
.mousemove(function(e){
// Determine which button mouse is over, or not (spin direction):
var x = e.pageX || e.x;
var y = e.pageY || e.y;
var el = e.target || e.srcElement;
var scale = svgEditor.tool_scale || 1;
var height = $(el).height()/2;
var direction =
(x > coord(el,'offsetLeft') + el.offsetWidth*scale - this.spinCfg._btn_width)
? ((y < coord(el,'offsetTop') + height*scale) ? 1 : -1) : 0;
if (direction !== this.spinCfg._direction) {
// Style up/down buttons:
switch(direction){
case 1: // Up arrow:
$(this).removeClass(this.spinCfg.downClass).addClass(this.spinCfg.upClass);
break;
case -1: // Down arrow:
$(this).removeClass(this.spinCfg.upClass).addClass(this.spinCfg.downClass);
break;
default: // Mouse is elsewhere in the textbox
$(this).removeClass(this.spinCfg.upClass).removeClass(this.spinCfg.downClass);
}
// Set spin direction:
this.spinCfg._direction = direction;
}
})
.mouseout(function(){
// Reset up/down buttons to their normal appearance when mouse moves away:
$(this).removeClass(this.spinCfg.upClass).removeClass(this.spinCfg.downClass);
this.spinCfg._direction = null;
window.clearInterval(this.spinCfg._repeat);
window.clearTimeout(this.spinCfg._delay);
})
.mousedown(function(e){
if ( e.button === 0 && this.spinCfg._direction != 0) {
// Respond to click on one of the buttons:
var self = this;
var stepSize = e.shiftKey ? self.spinCfg.smallStep : self.spinCfg.step
var adjust = function() {
self.adjustValue(self.spinCfg._direction * stepSize);
};
adjust();
// Initial delay before repeating adjustment
self.spinCfg._delay = window.setTimeout(function() {
adjust();
// Repeat adjust at regular intervals
self.spinCfg._repeat = window.setInterval(adjust, self.spinCfg.interval);
}, self.spinCfg.delay);
}
})
.mouseup(function(e){
// Cancel repeating adjustment
window.clearInterval(this.spinCfg._repeat);
window.clearTimeout(this.spinCfg._delay);
})
.dblclick(function(e) {
if ($.browser.msie)
this.adjustValue(this.spinCfg._direction * this.spinCfg.step);
})
.keydown(function(e){
// Respond to up/down arrow keys.
switch(e.keyCode){
case 38: this.adjustValue(this.spinCfg.step); break; // Up
case 40: this.adjustValue(-this.spinCfg.step); break; // Down
case 33: this.adjustValue(this.spinCfg.page); break; // PageUp
case 34: this.adjustValue(-this.spinCfg.page); break; // PageDown
}
})
/*
http://unixpapa.com/js/key.html describes the current state-of-affairs for
key repeat events:
- Safari 3.1 changed their model so that keydown is reliably repeated going forward
- Firefox and Opera still only repeat the keypress event, not the keydown
*/
.keypress(function(e){
if (this.repeating) {
// Respond to up/down arrow keys.
switch(e.keyCode){
case 38: this.adjustValue(this.spinCfg.step); break; // Up
case 40: this.adjustValue(-this.spinCfg.step); break; // Down
case 33: this.adjustValue(this.spinCfg.page); break; // PageUp
case 34: this.adjustValue(-this.spinCfg.page); break; // PageDown
}
}
// we always ignore the first keypress event (use the keydown instead)
else {
this.repeating = true;
}
})
// clear the 'repeating' flag
.keyup(function(e) {
this.repeating = false;
switch(e.keyCode){
case 38: // Up
case 40: // Down
case 33: // PageUp
case 34: // PageDown
case 13: this.adjustValue(0); break; // Enter/Return
}
})
.bind("mousewheel", function(e){
// Respond to mouse wheel in IE. (It returns up/dn motion in multiples of 120)
if (e.wheelDelta >= 120)
this.adjustValue(this.spinCfg.step);
else if (e.wheelDelta <= -120)
this.adjustValue(-this.spinCfg.step);
e.preventDefault();
})
.change(function(e){
this.adjustValue(0);
});
if (this.addEventListener) {
// Respond to mouse wheel in Firefox
this.addEventListener('DOMMouseScroll', function(e) {
if (e.detail > 0)
this.adjustValue(-this.spinCfg.step);
else if (e.detail < 0)
this.adjustValue(this.spinCfg.step);
e.preventDefault();
}, false);
}
});
function coord(el,prop) {
var c = el[prop], b = document.body;
while ((el = el.offsetParent) && (el != b)) {
if (!$.browser.msie || (el.currentStyle.position != 'relative'))
c += el[prop];
}
return c;
}
};