JQuerySpinBtn.js 9.1 KB

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