diff --git a/demos/spinner/default.html b/demos/spinner/default.html new file mode 100644 index 000000000..d90b73f6d --- /dev/null +++ b/demos/spinner/default.html @@ -0,0 +1,65 @@ + + + + + jQuery UI Spinner - Default functionality + + + + + + + + + + + + +
+ +

+

+ +

+ + +

+ +

+ + +

+ +
+ +
+

+Default spinner. +

+
+ + + diff --git a/demos/spinner/donation.html b/demos/spinner/donation.html new file mode 100644 index 000000000..617570b12 --- /dev/null +++ b/demos/spinner/donation.html @@ -0,0 +1,74 @@ + + + + + jQuery UI Spinner - Default functionality + + + + + + + + + + + + +
+ +

+ + +

+

+ + +

+
+ +
+

+Example of a donation form, with currency selection and amout spinner. +

+
+ + + diff --git a/demos/spinner/hexadecimal.html b/demos/spinner/hexadecimal.html new file mode 100644 index 000000000..03952a62d --- /dev/null +++ b/demos/spinner/hexadecimal.html @@ -0,0 +1,37 @@ + + + + + jQuery UI Spinner - Hexadecimal + + + + + + + + + + + + +
+ +

+ + +

+
+ +
+

+Example of a hexadecimal spinner. +

+
+ + + diff --git a/demos/spinner/index.html b/demos/spinner/index.html new file mode 100644 index 000000000..6d7b980bd --- /dev/null +++ b/demos/spinner/index.html @@ -0,0 +1,20 @@ + + + + jQuery UI Spinner Demos + + + +
+

Examples

+ +
+ + diff --git a/demos/spinner/latlong.html b/demos/spinner/latlong.html new file mode 100644 index 000000000..6b955ba47 --- /dev/null +++ b/demos/spinner/latlong.html @@ -0,0 +1,60 @@ + + + + + jQuery UI Spinner - Map + + + + + + + + + + + + + + +
+ + + +
+ + + +
+ +
+ +
+

+Google Maps integration, using spinners to change latidude and longitude. +

+
+ + + diff --git a/demos/spinner/rtl.html b/demos/spinner/rtl.html new file mode 100644 index 000000000..52d7b1048 --- /dev/null +++ b/demos/spinner/rtl.html @@ -0,0 +1,36 @@ + + + + + jQuery UI Spinner - Default functionality + + + + + + + + + + + + +
+ +

+

+ +
+ +
+

+Default spinner. +

+
+ + + diff --git a/external/jquery.mousewheel-3.0.2.js b/external/jquery.mousewheel-3.0.2.js new file mode 100644 index 000000000..507ab005e --- /dev/null +++ b/external/jquery.mousewheel-3.0.2.js @@ -0,0 +1,60 @@ +/*! Copyright (c) 2009 Brandon Aaron (http://brandonaaron.net) + * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) + * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses. + * Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers. + * Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix. + * + * Version: 3.0.2 + * + * Requires: 1.2.2+ + */ + +(function($) { + +var types = ['DOMMouseScroll', 'mousewheel']; + +$.event.special.mousewheel = { + setup: function() { + if ( this.addEventListener ) + for ( var i=types.length; i; ) + this.addEventListener( types[--i], handler, false ); + else + this.onmousewheel = handler; + }, + + teardown: function() { + if ( this.removeEventListener ) + for ( var i=types.length; i; ) + this.removeEventListener( types[--i], handler, false ); + else + this.onmousewheel = null; + } +}; + +$.fn.extend({ + mousewheel: function(fn) { + return fn ? this.bind("mousewheel", fn) : this.trigger("mousewheel"); + }, + + unmousewheel: function(fn) { + return this.unbind("mousewheel", fn); + } +}); + + +function handler(event) { + var args = [].slice.call( arguments, 1 ), delta = 0, returnValue = true; + + event = $.event.fix(event || window.event); + event.type = "mousewheel"; + + if ( event.wheelDelta ) delta = event.wheelDelta/120; + if ( event.detail ) delta = -event.detail/3; + + // Add events and delta to the front of the arguments + args.unshift(event, delta); + + return $.event.handle.apply(this, args); +} + +})(jQuery); \ No newline at end of file diff --git a/themes/base/jquery.ui.base.css b/themes/base/jquery.ui.base.css index 7634fb61e..62d17c7e2 100644 --- a/themes/base/jquery.ui.base.css +++ b/themes/base/jquery.ui.base.css @@ -18,4 +18,5 @@ @import url("jquery.ui.resizable.css"); @import url("jquery.ui.selectable.css"); @import url("jquery.ui.slider.css"); +@import url("jquery.ui.spinner.css"); @import url("jquery.ui.tabs.css"); diff --git a/themes/base/jquery.ui.spinner.css b/themes/base/jquery.ui.spinner.css new file mode 100644 index 000000000..6d8bfa59b --- /dev/null +++ b/themes/base/jquery.ui.spinner.css @@ -0,0 +1,24 @@ +/* Spinner +----------------------------------*/ +.ui-spinner { position:relative; display: inline-block; overflow: hidden; padding: 0; vertical-align: middle; height: 1.8em; } +.ui-spinner-input { border: none; background: none; padding: 0; margin: .2em 0; vertical-align: middle; } +.ui-spinner-button { width: 16px; height: 50%; font-size: .5em; padding: 0; margin: 0; z-index: 100; text-align: center; vertical-align: middle; position: absolute; cursor: default; display: block; overflow: hidden; } +.ui-spinner a.ui-spinner-button { border-top: none; border-bottom: none; } /* more specificity required here to overide default borders */ +.ui-spinner .ui-icon { position: absolute; margin-top: -8px; top: 50%; left: 0; } /* vertical centre icon */ +.ui-spinner-up { top: 0; } +.ui-spinner-down { bottom: 0; } + +/* ltr (default) */ +.ui-spinner-ltr { direction: ltr; } +.ui-spinner-ltr .ui-spinner-input { float: left; margin-left: .4em; margin-right: 22px; } +.ui-spinner-ltr .ui-spinner-button { right: 0; } +.ui-spinner-ltr a.ui-spinner-button { border-right: none; } + +/* rtl */ +.ui-spinner-rtl { direction: rtl; } +.ui-spinner-rtl .ui-spinner-input { float: right; margin-left: 22px; margin-right: .4em; } +.ui-spinner-rtl .ui-spinner-button { left: 0; } +.ui-spinner-rtl a.ui-spinner-button { border-left: none; } + +/* TR overrides */ +div.ui-spinner { background: none; } diff --git a/ui/jquery.ui.spinner.js b/ui/jquery.ui.spinner.js new file mode 100644 index 000000000..c0167fd24 --- /dev/null +++ b/ui/jquery.ui.spinner.js @@ -0,0 +1,505 @@ +/* + * jQuery UI Spinner @VERSION + * + * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * http://docs.jquery.com/UI/Spinner + * + * Depends: + * jquery.ui.core.js + * jquery.ui.widget.js + */ +(function($) { + +// shortcut constants +var hover = 'ui-state-hover', + active = 'ui-state-active', + namespace = '.spinner', + buttonRegex = /hide|auto|fast|slow|(\d+)/, + uiSpinnerClasses = 'ui-spinner ui-state-default ui-widget ui-widget-content ui-corner-all '; + +$.widget('ui.spinner', { + options: { + currency: false, + dir: 'ltr', + groupSeparator: '', + incremental: true, + max: null, + min: null, + mouseWheel: true, + padding: 0, + page: 5, + precision: 0, + radix: 10, + radixPoint: '.', + spinnerClass: null, + step: null, + value: 0, + width: false + }, + + _create: function() { + this._initOptions(); + + this.value(this._parse(this.element.val() || this.options.value)); + + this._draw(); + + this._mousewheel(); + + this._aria(); + }, + _initOptions: function() { + var self = this, + options = self.options; + + // check for precision in stepping and set _precision as internal + var precision = parseInt(options.precision, 10); + + if (self._step().toString().indexOf('.') != -1 && precision === 0) { + var s = self._step().toString(); + precision = s.slice(s.indexOf('.')+1, s.length).length; + } + + // set currency options + if (options.currency) { + precision = 2; + options.radix = 10; + options.groupSeparator = options.groupSeparator || (options.radixPoint === ',' ? '' : ','); + } + options.precision = precision; + }, + _draw: function() { + var self = this, + options = self.options; + + var uiSpinner = self.element + .addClass('ui-spinner-input') + .attr('autocomplete', 'off') + .wrap(self._uiSpinnerHtml()) + .parent() + // add buttons + .append(self._buttonHtml()) + // add behaviours + .hover(function() { + if (!options.disabled) { + $(this).addClass(hover); + } + self.hovered = true; + }, function() { + $(this).removeClass(hover); + self.hovered = false; + }); + + // TODO: need a better way to exclude IE8 without resorting to $.browser.version + // fix inline-block issues for IE. Since IE8 supports inline-block we need to exclude it. + if (!$.support.opacity && uiSpinner.css('display') == 'inline-block' && $.browser.version < 8) { + uiSpinner.css('display', 'inline'); + } + + this.element + .bind('keydown'+namespace, function(event) { + return self._start(event) ? self._keydown(event) : false; + }) + .bind('keyup'+namespace, function(event) { + if (self.spinning) { + self._stop(event); + self._change(event); + } + }) + .bind('focus'+namespace, function() { + uiSpinner.addClass(active); + self.focused = true; + }) + .bind('blur'+namespace, function(event) { + self._value(self.element.val()); + if (!self.hovered) { + uiSpinner.removeClass(active); + } + self.focused = false; + }); + + // force width if passed through options + if (options.width) { + this.element.width(options.width); + } + + // disable spinner if element was already disabled + if (options.disabled) { + this.disable(); + } + + // button bindings + this.buttons = uiSpinner.find('.ui-spinner-button') + .bind('mousedown', function(event) { + if (self._start(event) === false) { + return false; + } + self._repeat(null, $(this).hasClass('ui-spinner-up') ? 1 : -1, event); + + if (!self.options.disabled) { + $(this).addClass(active); + uiSpinner.addClass(active); + } + }) + .bind('mouseup', function(event) { + if (self.counter == 1) { + self._spin(($(this).hasClass('ui-spinner-up') ? 1 : -1) * self._step(), event); + } + if (self.spinning) { + self._stop(event); + self._change(event); + } + $(this).removeClass(active); + }) + .hover(function() { + if (!self.options.disabled) { + $(this).addClass(hover); + } + }, function(event) { + $(this).removeClass(active + ' ' + hover); + if (self.timer && self.spinning) { + self._stop(event); + self._change(event); + } + }); + + self.uiSpinner = uiSpinner; + }, + _uiSpinnerHtml: function() { + return '
'; + }, + _buttonHtml: function() { + return '' + + ''; + }, + _start: function(event) { + if (!this.spinning && this._trigger('start', event, { value: this.value()}) !== false) { + if (!this.counter) { + this.counter = 1; + } + this.spinning = true; + return true; + } + return false; + }, + _spin: function(step, event) { + if (this.options.disabled) { + return; + } + if (!this.counter) { + this.counter = 1; + } + + var newVal = this._value() + step * (this.options.incremental && this.counter > 100 + ? this.counter > 200 + ? 100 + : 10 + : 1); + + // cancelable + if (this._trigger('spin', event, { value: newVal }) !== false) { + this._value(newVal); + this.counter++; + } + }, + _stop: function(event) { + this.counter = 0; + if (this.timer) { + window.clearInterval(this.timer); + } + this.element[0].focus(); + this.spinning = false; + this._trigger('stop', event); + }, + _change: function(event) { + this._trigger('change', event); + }, + _repeat: function(i, steps, event) { + var self = this; + i = i || 100; + + if (this.timer) { + window.clearInterval(this.timer); + } + + this.timer = window.setInterval(function() { + self._repeat(self.options.incremental && self.counter > 20 ? 20 : i, steps, event); + }, i); + + self._spin(steps*self._step(), event); + }, + _keydown: function(event) { + var o = this.options, + KEYS = $.ui.keyCode; + + switch (event.keyCode) { + case KEYS.UP: this._repeat(null, event.shiftKey ? o.page : 1, event); break; + case KEYS.DOWN: this._repeat(null, event.shiftKey ? -o.page : -1, event); break; + case KEYS.PAGE_UP: this._repeat(null, o.page, event); break; + case KEYS.PAGE_DOWN: this._repeat(null, -o.page, event); break; + + case KEYS.HOME: + case KEYS.END: + if (event.shiftKey) { + return true; + } + this._value(this['_' + (event.keyCode == KEYS.HOME ? 'min':'max')]()); + break; + + case KEYS.TAB: + case KEYS.BACKSPACE: + case KEYS.LEFT: + case KEYS.RIGHT: + case KEYS.PERIOD: + case KEYS.NUMPAD_DECIMAL: + case KEYS.NUMPAD_SUBTRACT: + return true; + + case KEYS.ENTER: + this.value(this.element.val()); + return true; + + default: + if ((event.keyCode >= 96 && event.keyCode <= 105) || // numeric keypad 0-9 + (new RegExp('[' + this._validChars() + ']', 'i').test(String.fromCharCode(event.keyCode)))) { + return true; + }; + } + + return false; + }, + _mousewheel: function() { + var self = this; + if ($.fn.mousewheel && self.options.mouseWheel) { + this.element.mousewheel(function(event, delta) { + delta = ($.browser.opera ? -delta / Math.abs(delta) : delta); + if (!self._start(event)) { + return false; + } + self._spin((delta > 0 ? 1 : -1) * self._step(), event); + if (self.timeout) { + window.clearTimeout(self.timeout); + } + self.timeout = window.setTimeout(function() { + if (self.spinning) { + self._stop(event); + self._change(event); + } + }, 400); + event.preventDefault(); + }); + } + }, + _value: function(newVal) { + if (!arguments.length) { + return this._parse(this.element.val()); + } + this._setOption('value', newVal); + }, + _getData: function(key) { + switch (key) { + case 'min': + case 'max': + case 'step': + return this['_'+key](); + break; + } + return this.options[key]; + }, + _setOption: function(key, value) { + switch (key) { + case 'value': + value = this._parse(value); + if (value < this._min()) { + value = this._min(); + } + if (value > this._max()) { + value = this._max(); + } + break; + case 'spinnerClass': + this.uiSpinner + .removeClass(this.options.spinnerClass) + .addClass(uiSpinnerClasses + value); + break; + } + + $.Widget.prototype._setOption.call( this, key, value ); + + this._afterSetData(key, value); + }, + _afterSetData: function(key, value) { + switch(key) { + case 'max': + case 'min': + case 'step': + if (value != null) { + // write attributes back to element if original exist + if (this.element.attr(key)) { + this.element.attr(key, value); + } + } + this._aria(); + break; + case 'width': + this.element.width(value); + break; + case 'precision': + case 'value': + this._format(this._parse(this.options.value)); + break; + } + }, + _aria: function() { + this.uiSpinner + .attr('aria-valuemin', this._min()) + .attr('aria-valuemax', this._max()) + .attr('aria-valuenow', this.value()); + }, + _validChars: function() { + var radix = parseInt(this.options.radix); + return '\\-\\' + this.options.radixPoint + (this.options.groupSeparator + ? '\\' + this.options.groupSeparator + :'') + (radix < 10 + ? '0-' + radix + : '0-9' + (radix > 10 + ? 'a-' + String.fromCharCode('a'.charCodeAt(0) + radix - 11) + :'')); + }, + _parse: function(val) { + if (typeof val == 'string') { + if (this.options.groupSeparator) { + val = val.replace(new RegExp('\\'+this.options.groupSeparator,'g'), ''); + } + val = val.replace(new RegExp('[^' + this._validChars() + ']', 'gi'), '').split(this.options.radixPoint); + result = parseInt(val[0] == '-' ? 0 : val[0] || 0, this.options.radix); + if (val.length > 1) { + result += parseInt(val[1], this.options.radix) / Math.pow(this.options.radix, val[1].length) * + // must test first character of val[0] for minus sign because -0 is parsed as 0 in result + (val[0].substr(0,1) == '-' ? -1 : 1); + } + val = result; + } + return isNaN(val) ? null : val; + }, + _format: function(num) { + var regex = /(\d+)(\d{3})/, + options = this.options, + sym = options.currency || '', + dec = options.precision, + radix = options.radix, + group = options.groupSeparator, + pt = options.radixPoint, + neg = num < 0 ? '-' : ''; + + for ( + num = ( + isNaN(num) + ? options.value + : radix === 10 + ? parseFloat(num, radix).toFixed(dec) + : parseInt(num, radix) + ).toString(radix).replace('.', pt); + regex.test(num) && group; + num = num.replace(regex, '$1'+group+'$2') + ); + + result = num.replace('-',''); + while (options.padding && (result.length < options.padding)) { + result = '0' + result; + } + this.element.val(neg + sym + result); + }, + _getOption: function(key, defaultValue) { + return this._parse(this.options[key] !== null + ? this.options[key] + : this.element.attr(key) + ? this.element.attr(key) + : defaultValue); + }, + _step: function(newVal) { + if (!arguments.length) { + return this._getOption('step', 1); + } + this._setOption('step', newVal); + }, + _min: function(newVal) { + if (!arguments.length) { + return this._getOption('min', -Number.MAX_VALUE); + } + this._setOption('min', newVal); + }, + _max: function(newVal) { + if (!arguments.length) { + return this._getOption('max', Number.MAX_VALUE); + } + this._setOption('max', newVal); + }, + + destroy: function() { + if ($.fn.mousewheel) { + this.element.unmousewheel(); + } + + this.element + .removeClass('ui-spinner-input') + .removeAttr('disabled') + .removeAttr('autocomplete') + .removeData('spinner') + .unbind(namespace); + + this.uiSpinner.replaceWith(this.element); + }, + enable: function() { + this.element + .removeAttr('disabled') + .siblings() + .removeAttr('disabled') + .parent() + .removeClass('ui-spinner-disabled ui-state-disabled'); + this.options.disabled = false; + }, + disable: function() { + this.element + .attr('disabled', true) + .siblings() + .attr('disabled', true) + .parent() + .addClass('ui-spinner-disabled ui-state-disabled'); + this.options.disabled = true; + }, + value: function(newVal) { + if (!arguments.length) { + return this._value(); + } + this._value(newVal); + }, + stepUp: function(steps) { + this._spin((steps || 1) * this._step(), null); + return this; + }, + stepDown: function(steps) { + this._spin((steps || 1) * -this._step(), null); + return this; + }, + pageUp: function(pages) { + return this.stepUp((pages || 1) * this.options.page); + }, + pageDown: function(pages) { + return this.stepDown((pages || 1) * this.options.page); + }, + + widget: function() { + return this.uiSpinner; + } +}); + +})(jQuery);