1 /* SpinButton control 2 * 3 * Adds bells and whistles to any ordinary textbox to 4 * make it look and feel like a SpinButton Control. 5 * 6 * Originally written by George Adamson, Software Unity (george.jquery@softwareunity.com) August 2006. 7 * - Added min/max options 8 * - Added step size option 9 * - Added bigStep (page up/down) option 10 * 11 * Modifications made by Mark Gibson, (mgibson@designlinks.net) September 2006: 12 * - Converted to jQuery plugin 13 * - Allow limited or unlimited min/max values 14 * - Allow custom class names, and add class to input element 15 * - Removed global vars 16 * - Reset (to original or through config) when invalid value entered 17 * - Repeat whilst holding mouse button down (with initial pause, like keyboard repeat) 18 * - Support mouse wheel in Firefox 19 * - Fix double click in IE 20 * - Refactored some code and renamed some vars 21 * 22 * Tested in IE6, Opera9, Firefox 1.5 23 * v1.0 11 Aug 2006 - George Adamson - First release 24 * v1.1 Aug 2006 - George Adamson - Minor enhancements 25 * v1.2 27 Sep 2006 - Mark Gibson - Major enhancements 26 * v1.3a 28 Sep 2006 - George Adamson - Minor enhancements 27 * rf1.3a 15 Nov 2007 - Pavel Yaschenko - some changes 28 29 Sample usage: 30 31 // Create group of settings to initialise spinbutton(s). (Optional) 32 var myOptions = { 33 min: 0, // Set lower limit. 34 max: 100, // Set upper limit. 35 step: 1, // Set increment size. 36 spinClass: mySpinBtnClass, // CSS class to style the spinbutton. (Class also specifies url of the up/down button image.) 37 upClass: mySpinUpClass, // CSS class for style when mouse over up button. 38 downClass: mySpinDnClass // CSS class for style when mouse over down button. 39 } 40 41 $(document).ready(function(){ 42 43 // Initialise INPUT element(s) as SpinButtons: (passing options if desired) 44 $("#myInputElement").SpinButton(myOptions); 45 46 }); 47 48 */ 49 var sbjQuery = jQuery; 50 sbjQuery.fn.SpinButton = function(cfg){ 51 return this.each(function(){ 52 53 // Apply specified options or defaults: 54 // (Ought to refactor this some day to use $.extend() instead) 55 this.spinCfg = { 56 //min: cfg && cfg.min ? Number(cfg.min) : null, 57 //max: cfg && cfg.max ? Number(cfg.max) : null, 58 min: cfg && !isNaN(parseFloat(cfg.min)) ? Number(cfg.min) : null, // Fixes bug with min:0 59 max: cfg && !isNaN(parseFloat(cfg.max)) ? Number(cfg.max) : null, 60 step: cfg && cfg.step ? Number(cfg.step) : 1, 61 page: cfg && cfg.page ? Number(cfg.page) : 10, 62 upClass: cfg && cfg.upClass ? cfg.upClass : 'up', 63 downClass: cfg && cfg.downClass ? cfg.downClass : 'down', 64 reset: cfg && cfg.reset ? cfg.reset : this.value, 65 delay: cfg && cfg.delay ? Number(cfg.delay) : 500, 66 interval: cfg && cfg.interval ? Number(cfg.interval) : 100, 67 _btn_width: 20, 68 _btn_height: 12, 69 _direction: null, 70 _delay: null, 71 _repeat: null, 72 73 digits: cfg && cfg.digits ? Number(cfg.digits) : 1 74 }; 75 76 this.adjustValue = function(i){ 77 var v = this.value.toLowerCase(); 78 if (v=="am") 79 { 80 this.value="PM"; 81 return; 82 } 83 else if (v=="pm") { 84 this.value="AM"; 85 return; 86 } 87 v = (isNaN(this.value) ? this.spinCfg.reset : Number(this.value)) + Number(i); 88 if (this.spinCfg.min !== null) v = (v<this.spinCfg.min ? (this.spinCfg.max != null ? this.spinCfg.max : this.spinCfg.min) : v); 89 if (this.spinCfg.max !== null) v = (v>this.spinCfg.max ? (this.spinCfg.min != null ? this.spinCfg.min : this.spinCfg.max) : v); 90 91 var value = String(v); 92 while (value.length<this.spinCfg.digits) value="0"+value; 93 94 this.value = value; 95 }; 96 97 sbjQuery(this) 98 // .addClass(cfg && cfg.spinClass ? cfg.spinClass : 'spin-button') 99 // 100 // .mousemove(function(e){ 101 // // Determine which button mouse is over, or not (spin direction): 102 // var x = e.pageX || e.x; 103 // var y = e.pageY || e.y; 104 // var el = e.target || e.srcElement; 105 // var direction = 106 // (x > coord(el,'offsetLeft') + el.offsetWidth - this.spinCfg._btn_width) 107 // ? ((y < coord(el,'offsetTop') + this.spinCfg._btn_height) ? 1 : -1) : 0; 108 // 109 // if (direction !== this.spinCfg._direction) { 110 // // Style up/down buttons: 111 // switch(direction){ 112 // case 1: // Up arrow: 113 // sbjQuery(this).removeClass(this.spinCfg.downClass).addClass(this.spinCfg.upClass); 114 // break; 115 // case -1: // Down arrow: 116 // sbjQuery(this).removeClass(this.spinCfg.upClass).addClass(this.spinCfg.downClass); 117 // break; 118 // default: // Mouse is elsewhere in the textbox 119 // sbjQuery(this).removeClass(this.spinCfg.upClass).removeClass(this.spinCfg.downClass); 120 // } 121 // 122 // // Set spin direction: 123 // this.spinCfg._direction = direction; 124 // } 125 // }) 126 // 127 // .mouseout(function(){ 128 // // Reset up/down buttons to their normal appearance when mouse moves away: 129 // sbjQuery(this).removeClass(this.spinCfg.upClass).removeClass(this.spinCfg.downClass); 130 // this.spinCfg._direction = null; 131 // }) 132 133 // .mousedown(function(e){ 134 // if (this.spinCfg._direction != 0) { 135 // // Respond to click on one of the buttons: 136 // var self = this; 137 // var adjust = function() { 138 // self.adjustValue(self.spinCfg._direction * self.spinCfg.step); 139 // }; 140 // 141 // adjust(); 142 // 143 // // Initial delay before repeating adjustment 144 // self.spinCfg._delay = window.setTimeout(function() { 145 // adjust(); 146 // // Repeat adjust at regular intervals 147 // self.spinCfg._repeat = window.setInterval(adjust, self.spinCfg.interval); 148 // }, self.spinCfg.delay); 149 // } 150 // }) 151 // 152 // .mouseup(function(e){ 153 // // Cancel repeating adjustment 154 // window.clearInterval(this.spinCfg._repeat); 155 // window.clearTimeout(this.spinCfg._delay); 156 // }) 157 // 158 // .dblclick(function(e) { 159 // if (sbjQuery.browser.msie) 160 // this.adjustValue(this.spinCfg._direction * this.spinCfg.step); 161 // }) 162 163 .keydown(function(e){ 164 // Respond to up/down arrow keys. 165 switch(e.keyCode){ 166 case 38: this.adjustValue(this.spinCfg.step); break; // Up 167 case 40: this.adjustValue(-this.spinCfg.step); break; // Down 168 case 33: this.adjustValue(this.spinCfg.page); break; // PageUp 169 case 34: this.adjustValue(-this.spinCfg.page); break; // PageDown 170 } 171 }) 172 173 .bind("mousewheel", function(e){ 174 // Respond to mouse wheel in IE. (It returns up/dn motion in multiples of 120) 175 if (e.wheelDelta >= 120) 176 this.adjustValue(this.spinCfg.step); 177 else if (e.wheelDelta <= -120) 178 this.adjustValue(-this.spinCfg.step); 179 180 e.preventDefault(); 181 }) 182 183 .change(function(e){ 184 this.adjustValue(0); 185 }); 186 187 var self = this; 188 189 var btnUp = document.getElementById(this.id + 'BtnUp'); 190 sbjQuery(btnUp) 191 .mousedown(function(e){ 192 // Respond to click on one of the buttons: 193 var adjust = function() { 194 self.adjustValue(self.spinCfg.step); 195 }; 196 197 adjust(); 198 199 // Initial delay before repeating adjustment 200 self.spinCfg._delay = window.setTimeout(function() { 201 adjust(); 202 // Repeat adjust at regular intervals 203 self.spinCfg._repeat = window.setInterval(adjust, self.spinCfg.interval); 204 }, self.spinCfg.delay); 205 self.spinCfg._repeater = true; 206 return false; 207 }) 208 209 .mouseup(function(e){ 210 // Cancel repeating adjustment 211 self.spinCfg._repeater = false; 212 window.clearInterval(self.spinCfg._repeat); 213 window.clearTimeout(self.spinCfg._delay); 214 }) 215 216 .dblclick(function(e) { 217 if (RichFaces.browser.msie) 218 self.adjustValue(self.spinCfg.step); 219 }) 220 .mouseout(function(e){ 221 // Cancel repeating adjustment 222 if (self.spinCfg._repeater) 223 { 224 self.spinCfg._repeater = false 225 window.clearInterval(self.spinCfg._repeat); 226 window.clearTimeout(self.spinCfg._delay); 227 } 228 }); 229 230 var btnDown = document.getElementById(this.id + 'BtnDown'); 231 sbjQuery(btnDown) 232 .mousedown(function(e){ 233 // Respond to click on one of the buttons: 234 var adjust = function() { 235 self.adjustValue(-self.spinCfg.step); 236 }; 237 238 adjust(); 239 240 // Initial delay before repeating adjustment 241 self.spinCfg._delay = window.setTimeout(function() { 242 adjust(); 243 // Repeat adjust at regular intervals 244 self.spinCfg._repeat = window.setInterval(adjust, self.spinCfg.interval); 245 }, self.spinCfg.delay); 246 self.spinCfg._repeater = true; 247 return false; 248 }) 249 250 .mouseup(function(e){ 251 // Cancel repeating adjustment 252 self.spinCfg._repeater = false; 253 window.clearInterval(self.spinCfg._repeat); 254 window.clearTimeout(self.spinCfg._delay); 255 }) 256 257 .dblclick(function(e) { 258 if (RichFaces.browser.msie) 259 self.adjustValue(-self.spinCfg.step); 260 }) 261 .mouseout(function(e){ 262 // Cancel repeating adjustment 263 if (self.spinCfg._repeater) 264 { 265 self.spinCfg._repeater = false 266 window.clearInterval(self.spinCfg._repeat); 267 window.clearTimeout(self.spinCfg._delay); 268 } 269 }); 270 271 272 if (this.addEventListener) { 273 // Respond to mouse wheel in Firefox 274 this.addEventListener('DOMMouseScroll', function(e) { 275 if (e.detail > 0) 276 this.adjustValue(-this.spinCfg.step); 277 else if (e.detail < 0) 278 this.adjustValue(this.spinCfg.step); 279 280 e.preventDefault(); 281 }, false); 282 } 283 }); 284 285 function coord(el,prop) { 286 var c = el[prop], b = document.body; 287 288 while ((el = el.offsetParent) && (el != b)) { 289 if (!RichFaces.browser.msie || (el.currentStyle.position != 'relative')) 290 c += el[prop]; 291 } 292 293 return c; 294 } 295 }; 296