All Downloads are FREE. Search and download functionalities are using the official Maven repository.

template.js.plugins.jquery.ui.spinner.js Maven / Gradle / Ivy

There is a newer version: 5.0.5
Show newest version
/*
 *jQuery UI Spinner 1.21
 *
 *Copyright (c) 2009-2010 Brant Burnett
 *Copyright (c) 2010 Johannes Geppert http://www.jgeppert.com
 *Dual licensed under the MIT or GPL Version 2 licenses.
 */
 (function($, undefined) {

var
	// constants
	active = 'ui-state-active',
	hover = 'ui-state-hover',
	disabled = 'ui-state-disabled',

	keyCode = $.ui.keyCode,
	up = keyCode.UP,
	down = keyCode.DOWN,
	right = keyCode.RIGHT,
	left = keyCode.LEFT,
	pageUp = keyCode.PAGE_UP,
	pageDown = keyCode.PAGE_DOWN,
	home = keyCode.HOME,
	end = keyCode.END,

	msie = $.browser.msie,
	mouseWheelEventName = $.browser.mozilla ? 'DOMMouseScroll' : 'mousewheel',

	// namespace for events on input
	eventNamespace = '.uispinner',

	// only these special keys will be accepted, all others will be ignored unless CTRL or ALT are pressed
	validKeys = [up, down, right, left, pageUp, pageDown, home, end, keyCode.BACKSPACE, keyCode.DELETE, keyCode.TAB],

	// stores the currently focused spinner
	// Note: due to oddities in the focus/blur events, this is part of a two-part system for confirming focus
	// this must set to the control, and the focus variable must be true
	// this is because hitting up/down arrows with mouse causes focus to change, but blur event for previous control doesn't fire
	focusCtrl;

$.widget('ui.spinner', {
	options: {
		min: null,
		max: null,
		allowNull: false,

		group: '',
		point: '.',
		prefix: '',
		suffix: '',
		places: null, // null causes it to detect the number of places in step

		defaultStep: 1, // real value is 'step', and should be passed as such.  This value is used to detect if passed value should override HTML5 attribute
		largeStep: 10,
		mouseWheel: true,
		increment: 'slow',
		className: null,
		showOn: 'always',
		width: 16,
		upIconClass: "ui-icon-triangle-1-n",
		downIconClass: "ui-icon-triangle-1-s",

		format: function(num, places) {
			var options = this,
				regex = /(\d+)(\d{3})/,
				result = ((isNaN(num) ? 0 : Math.abs(num)).toFixed(places)) + '';

			for (result = result.replace('.', options.point); regex.test(result) && options.group; result=result.replace(regex, '$1'+options.group+'$2')) {}
			return (num < 0 ? '-' : '') + options.prefix + result + options.suffix;
		},

		parse: function(val) {
			var options = this;

			if (options.group == '.') {
				val = val.replace('.', '');
			 }
			if (options.point != '.') {
				val = val.replace(options.point, '.');
			 }
			return parseFloat(val.replace(/[^0-9\-\.]/g, ''));
		}
	},

	// * Widget fields *
	// curvalue - current value
	// places - currently effective number of decimal places
	// oWidth - original input width (used for destroy)
	// oMargin - original input right margin (used for destroy)
	// counter - number of spins at the current spin speed
	// incCounter - index within options.increment of the current spin speed
	// selfChange - indicates that change event is being fired by the widget, so don't reprocess input value
	// inputMaxLength - initial maxLength value on the input
	// focused - this spinner currently has the focus

	_create: function() {
		// shortcuts
		var self = this,
			input = self.element,
			type = input.attr('type');

		if (!input.is('input') || ((type != 'text') && (type != 'number'))) {
			console.error('Invalid target for ui.spinner');
			return;
		}

		self._procOptions(true);
		self._createButtons(input);

		if (!input.is(':enabled')) {
			self.disable();
		}
	},

	_createButtons: function(input) {
		function getMargin(margin) {
			// IE8 returns auto if no margin specified
			return margin == 'auto' ? 0 : parseInt(margin, 10);
		}

		var self = this,
			options = self.options,
			className = options.className,
			buttonWidth = options.width,
			showOn = options.showOn,
			box = $.support.boxModel,
			height = input.outerHeight(),
			rightMargin = self.oMargin = getMargin(input.css('margin-right')), // store original width and right margin for later destroy
			wrapper = self.wrapper = input.css({ width: (self.oWidth = (box ? input.width() : input.outerWidth())) - buttonWidth,
												 marginRight: rightMargin + buttonWidth, textAlign: 'right' })
				.after('').next(),
			btnContainer = self.btnContainer = $(
				'
' + '
 
' + '
 
' + '
'), // object shortcuts upButton, downButton, buttons, icons, hoverDelay, hoverDelayCallback, // current state booleans hovered, inKeyDown, inSpecialKey, inMouseDown, // used to reverse left/right key directions rtl = input[0].dir == 'rtl'; // apply className before doing any calculations because it could affect them if (className) { wrapper.addClass(className); } wrapper.append(btnContainer.css({ height: height, left: -buttonWidth-rightMargin, // use offset calculation to fix vertical position in Firefox top: (input.offset().top - wrapper.offset().top) + 'px' })); buttons = self.buttons = btnContainer.find('.ui-spinner-button'); buttons.css({ width: buttonWidth - (box ? buttons.outerWidth() - buttons.width() : 0), height: height/2 - (box ? buttons.outerHeight() - buttons.height() : 0) }); upButton = buttons[0]; downButton = buttons[1]; // fix icon centering icons = buttons.find('.ui-icon'); icons.css({ marginLeft: (buttons.innerWidth() - icons.width()) / 2, marginTop: (buttons.innerHeight() - icons.height()) / 2 }); // set width of btnContainer to be the same as the buttons btnContainer.width(buttons.outerWidth()); if (showOn != 'always') { btnContainer.css('opacity', 0); } /* Event Bindings */ // bind hover events to show/hide buttons if (showOn == 'hover' || showOn == 'both') { buttons.add(input) .bind('mouseenter' + eventNamespace, function() { setHoverDelay(function() { hovered = true; if (!self.focused || (showOn == 'hover')) { // ignore focus flag if show on hover only self.showButtons(); } }); }) .bind('mouseleave' + eventNamespace, function hoverOut() { setHoverDelay(function() { hovered = false; if (!self.focused || (showOn == 'hover')) { // ignore focus flag if show on hover only self.hideButtons(); } }); }); } function mouseDown() { if (!options.disabled) { var input = self.element[0], dir = (this === upButton ? 1 : -1); input.focus(); input.select(); $(this).addClass(active); inMouseDown = true; self._startSpin(dir); } return false; } function mouseUp() { if (inMouseDown) { $(this).removeClass(active); self._stopSpin(); inMouseDown = false; } return false; } buttons.hover(function() { // ensure that both buttons have hover removed, sometimes they get left on self.buttons.removeClass(hover); if (!options.disabled) { $(this).addClass(hover); } }, function() { $(this).removeClass(hover); }) .mousedown(mouseDown) .mouseup(mouseUp) .mouseout(mouseUp); if (msie) { // fixes dbl click not firing second mouse down in IE buttons.dblclick(function() { if (!options.disabled) { // make sure any changes are posted self._change(); self._doSpin((this === upButton ? 1 : -1) * options.step); } return false; }) // fixes IE8 dbl click selection highlight .bind('selectstart', function() {return false;}); } input.bind('keydown' + eventNamespace, function(e) { var dir, large, limit, keyCode = e.keyCode; // shortcut for minimization if (e.ctrl || e.alt) { return true; } // ignore these events if (isSpecialKey(keyCode)) { inSpecialKey = true; } if (inKeyDown) { return false; } // only one direction at a time, and suppress invalid keys switch (keyCode) { case up: case pageUp: dir = 1; large = keyCode == pageUp; break; case down: case pageDown: dir = -1; large = keyCode == pageDown; break; case right: case left: dir = (keyCode == right) ^ rtl ? 1 : -1; break; case home: limit = self.options.min; if (limit !== null) { self._setValue(limit); } return false; case end: limit = self.options.max; limit = self.options.max; if (limit !== null) { self._setValue(limit); } return false; } if (dir) { // only process if dir was set above if (!inKeyDown && !options.disabled) { keyDir = dir; $(dir > 0 ? upButton : downButton).addClass(active); inKeyDown = true; self._startSpin(dir, large); } return false; } }) .bind('keyup' + eventNamespace, function(e) { if (e.ctrl || e.alt) { return true; } // ignore these events if (isSpecialKey(keyCode)) { inSpecialKey = false; } switch (e.keyCode) { case up: case right: case pageUp: case down: case left: case pageDown: buttons.removeClass(active); self._stopSpin(); inKeyDown = false; return false; } }) .bind('change' + eventNamespace, function() { self._change(); }) .bind('focus' + eventNamespace, function() { function selectAll() { self.element.select(); } // add delay for Chrome, but breaks IE8 if(msie) { selectAll(); } else { setTimeout(selectAll, 0); } self.focused = true; focusCtrl = self; if (!hovered && (showOn == 'focus' || showOn == 'both')) { // hovered will only be set if hover affects show self.showButtons(); } }) .bind('blur' + eventNamespace, function() { self.focused = false; if (!hovered && (showOn == 'focus' || showOn == 'both')) { // hovered will only be set if hover affects show self.hideButtons(); } }); function isSpecialKey(keyCode) { for (var i=0; i= '0') && (ch <= '9') || (ch == '-')) { return false; } if (((self.places > 0) && (ch == options.point)) || (ch == options.group)) { return false; } return true; } // used to delay start of hover show/hide by 100 milliseconds function setHoverDelay(callback) { if (hoverDelay) { // don't do anything if trying to set the same callback again if (callback === hoverDelayCallback) { return; } clearTimeout(hoverDelay); } function execute() { hoverDelay = 0; callback(); } hoverDelayCallback = callback; hoverDelay = setTimeout(execute, 100); } }, _procOptions: function(init) { var self = this, input = self.element, options = self.options, min = options.min, max = options.max, step = options.step, places = options.places, maxlength = -1, temp; // setup increment based on speed string if (options.increment == 'slow') { options.increment = [{count: 1, mult: 1, delay: 250}, {count: 3, mult: 1, delay: 100}, {count: 0, mult: 1, delay: 50}]; } else if (options.increment == 'fast') { options.increment = [{count: 1, mult: 1, delay: 250}, {count: 19, mult: 1, delay: 100}, {count: 80, mult: 1, delay: 20}, {count: 100, mult: 10, delay: 20}, {count: 0, mult: 100, delay: 20}]; } if ((min === null) && ((temp = input.attr('min')) !== null)) { min = parseFloat(temp); } if ((max === null) && ((temp = input.attr('max')) !== null)) { max = parseFloat(temp); } if (!step && ((temp = input.attr('step')) !== null)) { if (temp != 'any') { step = parseFloat(temp); options.largeStep *= step; } } options.step = step = step || options.defaultStep; // Process step for decimal places if none are specified if ((places === null) && ((temp = step + '').indexOf('.') != -1)) { places = temp.length - temp.indexOf('.') - 1; } self.places = places; if ((max !== null) && (min !== null)) { // ensure that min is less than or equal to max if (min > max) { min = max; } // set maxlength based on min/max maxlength = Math.max(Math.max(maxlength, options.format(max, places, input).length), options.format(min, places, input).length); } // only lookup input maxLength on init if (init) { self.inputMaxLength = input[0].maxLength; } temp = self.inputMaxLength; if (temp > 0) { maxlength = maxlength > 0 ? Math.min(temp, maxlength) : temp; temp = Math.pow(10, maxlength) - 1; if ((max === null) || (max > temp)) { max = temp; } temp = -(temp + 1) / 10 + 1; if ((min === null) || (min < temp)) { min = temp; } } if (maxlength > 0) { input.attr('maxlength', maxlength); } options.min = min; options.max = max; // ensures that current value meets constraints self._change(); input.unbind(mouseWheelEventName + eventNamespace); if (options.mouseWheel) { input.bind(mouseWheelEventName + eventNamespace, self._mouseWheel); } }, _mouseWheel: function(e) { var self = $.data(this, 'spinner'); if (!self.options.disabled && self.focused && (focusCtrl === self)) { // make sure changes are posted self._change(); self._doSpin(((e.wheelDelta || -e.detail) > 0 ? 1 : -1) * self.options.step); return false; } }, // sets an interval to call the _spin function _setTimer: function(delay, dir, large) { function fire() { self._spin(dir, large); } var self = this; self._stopSpin(); self.timer = setInterval(fire, delay); }, // stops the spin timer _stopSpin: function() { if (this.timer) { clearInterval(this.timer); this.timer = 0; } }, // performs first step, and starts the spin timer if increment is set _startSpin: function(dir, large) { // shortcuts var self = this, options = self.options, increment = options.increment; // make sure any changes are posted self._change(); self._doSpin(dir * (large ? self.options.largeStep : self.options.step)); if (increment && increment.length > 0) { self.counter = 0; self.incCounter = 0; self._setTimer(increment[0].delay, dir, large); } }, // called by timer for each step in the spin _spin: function(dir, large) { // shortcuts var self = this, increment = self.options.increment, curIncrement = increment[self.incCounter]; self._doSpin(dir * curIncrement.mult * (large ? self.options.largeStep : self.options.step)); self.counter++; if ((self.counter > curIncrement.count) && (self.incCounter < increment.length-1)) { self.counter = 0; curIncrement = increment[++self.incCounter]; self._setTimer(curIncrement.delay, dir, large); } }, // actually spins the timer by a step _doSpin: function(step) { // shortcut var self = this, value = self.curvalue; if (value === null) { value = (step > 0 ? self.options.min : self.options.max) || 0; } self._setValue(value + step); }, // Parse the value currently in the field _parseValue: function() { var value = this.element.val(); return value ? this.options.parse(value, this.element) : null; }, _validate: function(value) { var options = this.options, min = options.min, max = options.max; if ((value === null) && !options.allowNull) { value = this.curvalue !== null ? this.curvalue : min || max || 0; // must confirm not null in case just initializing and had blank value } if ((max !== null) && (value > max)) { return max; } else if ((min !== null) && (value < min)) { return min; } else { return value; } }, _change: function() { var self = this, // shortcut value = self._parseValue(), min = self.options.min, max = self.options.max; // don't reprocess if change was self triggered if (!self.selfChange) { if (isNaN(value)) { value = self.curvalue; } self._setValue(value, true); } }, // overrides _setData to force option parsing _setOption: function(key, value) { $.Widget.prototype._setOption.call(this, key, value); this._procOptions(); }, increment: function() { this._doSpin(this.options.step); }, decrement: function() { this._doSpin(-this.options.step); }, showButtons: function(immediate) { var btnContainer = this.btnContainer.stop(); if (immediate) { btnContainer.css('opacity', 1); } else { btnContainer.fadeTo('fast', 1); } }, hideButtons: function(immediate) { var btnContainer = this.btnContainer.stop(); if (immediate) { btnContainer.css('opacity', 0); } else { btnContainer.fadeTo('fast', 0); } this.buttons.removeClass(hover); }, // Set the value directly _setValue: function(value, suppressFireEvent) { var self = this; self.curvalue = value = self._validate(value); self.element.val(value !== null ? self.options.format(value, self.places, self.element) : ''); if (!suppressFireEvent) { self.selfChange = true; self.element.change(); self.selfChange = false; } }, // Set or retrieve the value value: function(newValue) { if (arguments.length) { this._setValue(newValue); // maintains chaining return this.element; } return this.curvalue; }, enable: function() { this.buttons.removeClass(disabled); this.element[0].disabled = false; $.Widget.prototype.enable.call(this); }, disable: function() { this.buttons.addClass(disabled) // in case hover class got left on .removeClass(hover); this.element[0].disabled = true; $.Widget.prototype.disable.call(this); }, destroy: function(target) { this.wrapper.remove(); this.element.unbind(eventNamespace).css({ width: this.oWidth, marginRight: this.oMargin }); $.Widget.prototype.destroy.call(this); } }); })( jQuery );




© 2015 - 2024 Weber Informatics LLC | Privacy Policy