      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