From f7d8a1ba570fd620d65b377ebe806eac0b578328 Mon Sep 17 00:00:00 2001 From: jzaefferer Date: Fri, 22 Oct 2010 06:16:12 +0200 Subject: Porting old spinner implementation to 1.8, dropping some baggage. --- demos/spinner/default.html | 65 +++++ demos/spinner/donation.html | 74 ++++++ demos/spinner/hexadecimal.html | 37 +++ demos/spinner/index.html | 20 ++ demos/spinner/latlong.html | 60 +++++ demos/spinner/rtl.html | 36 +++ external/jquery.mousewheel-3.0.2.js | 60 +++++ themes/base/jquery.ui.base.css | 1 + themes/base/jquery.ui.spinner.css | 24 ++ ui/jquery.ui.spinner.js | 505 ++++++++++++++++++++++++++++++++++++ 10 files changed, 882 insertions(+) create mode 100644 demos/spinner/default.html create mode 100644 demos/spinner/donation.html create mode 100644 demos/spinner/hexadecimal.html create mode 100644 demos/spinner/index.html create mode 100644 demos/spinner/latlong.html create mode 100644 demos/spinner/rtl.html create mode 100644 external/jquery.mousewheel-3.0.2.js create mode 100644 themes/base/jquery.ui.spinner.css create mode 100644 ui/jquery.ui.spinner.js 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); -- cgit v1.2.3 From af8ca4ed0e5063c5a84cd7ab77f525b98c6c62b7 Mon Sep 17 00:00:00 2001 From: jzaefferer Date: Fri, 22 Oct 2010 06:23:52 +0200 Subject: Importing spinner unit tests --- tests/unit/spinner/spinner.html | 39 +++ tests/unit/spinner/spinner_core.js | 357 ++++++++++++++++++++++++ tests/unit/spinner/spinner_defaults.js | 26 ++ tests/unit/spinner/spinner_events.js | 72 +++++ tests/unit/spinner/spinner_methods.js | 184 +++++++++++++ tests/unit/spinner/spinner_options.js | 482 +++++++++++++++++++++++++++++++++ 6 files changed, 1160 insertions(+) create mode 100644 tests/unit/spinner/spinner.html create mode 100644 tests/unit/spinner/spinner_core.js create mode 100644 tests/unit/spinner/spinner_defaults.js create mode 100644 tests/unit/spinner/spinner_events.js create mode 100644 tests/unit/spinner/spinner_methods.js create mode 100644 tests/unit/spinner/spinner_options.js diff --git a/tests/unit/spinner/spinner.html b/tests/unit/spinner/spinner.html new file mode 100644 index 000000000..df91c926e --- /dev/null +++ b/tests/unit/spinner/spinner.html @@ -0,0 +1,39 @@ + + + + jQuery UI Spinner Test Suite + + + + + + + + + + + + + + + + + + + + + + + +

jQuery UI Slider Test Suite

+

+

+
    +
+ +
+ +
+ + + diff --git a/tests/unit/spinner/spinner_core.js b/tests/unit/spinner/spinner_core.js new file mode 100644 index 000000000..2996ad128 --- /dev/null +++ b/tests/unit/spinner/spinner_core.js @@ -0,0 +1,357 @@ +/* + * spinner_core.js + */ + +var el, + options, + simulateKeyDownUp = function(el, kCode, shift) { + el.simulate("keydown",{keyCode:kCode, shiftKey: shift || false }) + .simulate("keyup",{keyCode:kCode, shiftKey: shift || false }); + }, + wrapper = function() { + return el.closest('.ui-spinner'); + }, + upButton = function() { + return wrapper().find('.ui-spinner-up'); + }, + downButton = function() { + return wrapper().find('.ui-spinner-down'); + }, + box = function() { + return $('.ui-spinner-input', wrapper()); + }; + +(function($) { + +// Spinner Tests +module("spinner: core"); + +test("init", function() { + expect(3); + + $("").appendTo('body').spinner().remove(); + ok(true, '.spinner() called on element'); + + $('').spinner().remove(); + ok(true, '.spinner() called on disconnected element'); + + el = $('').spinner(); + ok(el.hasClass('ui-spinner-input'), 'input gets ui-spinner-input class on init'); + +}); + +test("destroy", function() { + expect(3); + + $("").appendTo('body').spinner().spinner("destroy").remove(); + ok(true, '.spinner("destroy") called on element'); + + $('').spinner().spinner("destroy").remove(); + ok(true, '.spinner().spinner("destroy") called on disconnected element'); + + el = $('').spinner().spinner('destroy'); + ok(!el.hasClass('ui-spinner-input'), 'ui-spinner-input class removed on destroy'); +}); + +test("re-attach", function() { + expect(2); + + el = $("").spinner().spinner("destroy").spinner(); + ok(true, '.spinner().spinner("destroy").spinner() called on element'); + + el = $('').spinner().spinner("destroy").spinner().remove(); + ok(true, '.spinner().spinner("destroy").spinner() called on disconnected element'); + +}); + +test("keydown UP on input, increases value not greater than max", function() { + expect(3); + + el = $("#spin"); + options = { + max:100, + value:50, + step:10 + } + el.spinner(options); + + simulateKeyDownUp(el, $.ui.keyCode.UP); + + equals(el.val(), 60); + + for (i = 0; i<11; i++) { + simulateKeyDownUp(el, $.ui.keyCode.UP); + } + + equals(el.val(), 100); + + el.val(50); + + simulateKeyDownUp(el, $.ui.keyCode.UP); + + equals(el.val(), 60); +}); + +test("keydown DOWN on input, decreases value not less than min", function() { + expect(3); + + el = $("#spin"); + options = { + min:-100, + value:50, + step:10 + } + el.spinner(options); + + simulateKeyDownUp(el, $.ui.keyCode.DOWN); + + equals(el.val(), 40); + + for (i = 0; i<21; i++) { + simulateKeyDownUp(el, $.ui.keyCode.DOWN); + } + + equals(el.val(), -100); + + el.val(50); + + simulateKeyDownUp(el, $.ui.keyCode.DOWN); + + equals(el.val(), 40); + +}); + +test("keydown PGUP on input, increases value not greater than max", function() { + expect(3); + + el = $("#spin"); + options = { + max:100, + value:0, + step:10 + } + el.spinner(options); + + simulateKeyDownUp(el, $.ui.keyCode.PAGE_UP); + + equals(el.val(), 50); + + for (i = 0; i<5; i++) { + simulateKeyDownUp(el, $.ui.keyCode.PAGE_UP); + } + + equals(el.val(), 100); + + el.val(0); + + simulateKeyDownUp(el, $.ui.keyCode.PAGE_UP); + + equals(el.val(), 50); +}); + +test("keydown PGDN on input, decreases value not less than min", function() { + expect(3); + + el = $("#spin"); + options = { + min:-100, + value:0, + step:10 + } + el.spinner(options); + + simulateKeyDownUp(el, $.ui.keyCode.PAGE_DOWN); + + equals(el.val(), -50); + + for (i = 0; i<5; i++) { + simulateKeyDownUp(el, $.ui.keyCode.PAGE_DOWN); + } + + equals(el.val(), -100); + + el.val(0); + + simulateKeyDownUp(el, $.ui.keyCode.PAGE_DOWN); + + equals(el.val(), -50); +}); + +test("hold SHIFT and keydown UP, increments value but no greater than max", function() { + expect(2); + + el = $("#spin"); + options = { + max:100, + value:0, + step:10 + } + el.spinner(options); + + simulateKeyDownUp(el, $.ui.keyCode.UP, true); + + equals(el.val(), 50); + + for (i = 0; i<5; i++) { + simulateKeyDownUp(el, $.ui.keyCode.UP, true); + } + + equals(el.val(), 100); +}); + +test("hold SHIFT and keydown DOWN, decreases value but no less than min", function() { + expect(2); + + el = $("#spin"); + options = { + min:-100, + value:0, + step:10 + } + el.spinner(options); + + simulateKeyDownUp(el, $.ui.keyCode.DOWN, true); + + equals(el.val(), -50); + + for (i = 0; i<5; i++) { + simulateKeyDownUp(el, $.ui.keyCode.DOWN, true); + } + + equals(el.val(), -100); +}); + +test("keydown HOME on input, sets value to minimum", function() { + el = $("#spin"); + options = { + min:-100, + value:50, + step:10 + } + el.spinner(options); + + simulateKeyDownUp(el, $.ui.keyCode.HOME); + equals(el.val(), -100); + + el.spinner('option', 'min', -200); + + simulateKeyDownUp(el, $.ui.keyCode.HOME); + + equals(el.val(), -200); +}); + +test("keydown END on input, sets value to maximum", function() { + el = $("#spin"); + options = { + max:100, + value:50, + step:10 + } + el.spinner(options); + + simulateKeyDownUp(el, $.ui.keyCode.END); + equals(el.val(), 100); + + el.spinner('option', 'max', 200); + + simulateKeyDownUp(el, $.ui.keyCode.END); + + equals(el.val(), 200); +}); + +test("keydown LEFT on input has no effect", function() { + el = $("#spin"); + el.spinner(); + var value = el.val(); + + simulateKeyDownUp(el, $.ui.keyCode.LEFT); + equals(el.val(), value); + + for (i = 0; i<5; i++) { + simulateKeyDownUp(el, $.ui.keyCode.LEFT); + } + + equals(el.val(), value); +}); + +test("keydown RIGHT on input has no effect", function() { + expect(2); + + el = $("#spin"); + el.spinner(); + var value = el.val(); + + simulateKeyDownUp(el, $.ui.keyCode.RIGHT); + equals(el.val(), value); + + for (i = 0; i<5; i++) { + simulateKeyDownUp(el, $.ui.keyCode.RIGHT); + } + + equals(el.val(), value); +}); + +test("mouse click on buttons", function() { + expect(4); + + el = $("#spin").spinner(); + val = 0; + + $(".ui-spinner-up").trigger("mousedown").trigger("mouseup"); + + equals(el.val(), ++val, "mouse click to up"); + + $(".ui-spinner-down").trigger("mousedown").trigger("mouseup"); + + equals(el.val(), --val, "mouse click to down"); + + el.val(50); + + $(".ui-spinner-up").trigger("mousedown").trigger("mouseup"); + + equals(el.val(), 51); + + el.val(50); + + $(".ui-spinner-down").trigger("mousedown").trigger("mouseup"); + + equals(el.val(), 49); + +}); + +test("mouse wheel on input", function() { + ok(false, 'missing test - untested code is broken code'); +}); + +test("reading HTML5 attributes", function() { + expect(4); + + el = $('').spinner(); + + equals(el.spinner('option', 'value'), 5, 'value'); + equals(el.spinner('option', 'max'), 100, 'max'); + equals(el.spinner('option', 'min'), -100, 'min'); + equals(el.spinner('option', 'step'), 2, 'step'); +}); + +test("ARIA attributes", function() { + expect(7); + + el = $('#spin').spinner({ min: -5, max: 5, value: 2 }); + + equals(wrapper().attr('role'), 'spinbutton', 'role'); + equals(wrapper().attr('aria-valuemin'), -5, 'aria-valuemin'); + equals(wrapper().attr('aria-valuemax'), 5, 'aria-valuemax'); + equals(wrapper().attr('aria-valuenow'), 2, 'aria-valuenow'); + + el.spinner('stepUp'); + + equals(wrapper().attr('aria-valuenow'), 3, 'stepUp 1 step changes aria-valuenow'); + + el.spinner('option', { min: -10, max: 10 }); + + equals(wrapper().attr('aria-valuemin'), -10, 'min option changed aria-valuemin changes'); + equals(wrapper().attr('aria-valuemax'), 10, 'max option changed aria-valuemax changes'); +}); + +})(jQuery); diff --git a/tests/unit/spinner/spinner_defaults.js b/tests/unit/spinner/spinner_defaults.js new file mode 100644 index 000000000..94d3d67ab --- /dev/null +++ b/tests/unit/spinner/spinner_defaults.js @@ -0,0 +1,26 @@ +/* + * spinner_defaults.js + */ + +var spinner_defaults = { + buttons: 'show', + currency: false, + dir: 'ltr', + disabled: false, + 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 +}; + +commonWidgetTests('spinner', { defaults: spinner_defaults }); diff --git a/tests/unit/spinner/spinner_events.js b/tests/unit/spinner/spinner_events.js new file mode 100644 index 000000000..ae80801e6 --- /dev/null +++ b/tests/unit/spinner/spinner_events.js @@ -0,0 +1,72 @@ +/* + * spinner_events.js + */ +(function($) { + +module("spinner: events"); + +test("start", function() { + expect(1); + + var start = 0; + + el = $("#spin").spinner({ + start: function(){ + start++; + } + }); + + simulateKeyDownUp(el, $.ui.keyCode.UP); + + equals(start, 1, "Start triggered"); +}); + +test("spin", function() { + expect(1); + + var spin = 0; + + el = $("#spin").spinner({ + spin: function(){ + spin++; + } + }); + + simulateKeyDownUp(el, $.ui.keyCode.UP); + + equals(spin, 1, "Spin triggered"); +}); + +test("stop", function() { + expect(1); + + var stop = 0; + + el = $("#spin").spinner({ + stop: function(){ + stop++; + } + }); + + simulateKeyDownUp(el, $.ui.keyCode.DOWN); + + equals(stop, 1, "Stop triggered"); +}); + +test("change", function() { + expect(1); + + var start = spin = stop = change = 0; + + el = $("#spin").spinner({ + change: function(){ + change++; + } + }); + + simulateKeyDownUp(el, $.ui.keyCode.UP); + + equals(change, 1, "Change triggered"); +}); + +})(jQuery); diff --git a/tests/unit/spinner/spinner_methods.js b/tests/unit/spinner/spinner_methods.js new file mode 100644 index 000000000..faeab75f3 --- /dev/null +++ b/tests/unit/spinner/spinner_methods.js @@ -0,0 +1,184 @@ +/* + * spinner_methods.js + */ +(function($) { + +module("spinner: methods"); + +test("disable", function() { + expect(14); + + el = $("#spin").spinner({ disabled: false }); + var val = el.val(); + + ok(!wrapper().hasClass(".ui-spinner-disabled"), "before: wrapper does not have ui-spinner-disabled class"); + ok(!box().is(':disabled'), "before: input does not have disabled attribute"); + + el.spinner("disable"); + ok(wrapper().hasClass(".ui-spinner-disabled"), "after: wrapper has ui-spinner-disabled class"); + ok(box().is(':disabled'), "after: input has disabled attribute"); + + simulateKeyDownUp(el, $.ui.keyCode.UP); + equals(val, el.val(), "keyboard - value does not change on key UP"); + + simulateKeyDownUp(el, $.ui.keyCode.DOWN); + equals(val, el.val(), "keyboard - value does not change on key DOWN"); + + simulateKeyDownUp(el, $.ui.keyCode.PAGE_UP); + equals(val, el.val(), "keyboard - value does not change on key PGUP"); + + simulateKeyDownUp(el, $.ui.keyCode.PAGE_DOWN); + equals(val, el.val(), "keyboard - value does not change on key PGDN"); + + upButton().trigger('mousedown').trigger('mouseup'); + equals(val, el.val(), "mouse - value does not change on clicking up button"); + + downButton().trigger('mousedown').trigger('mouseup'); + equals(val, el.val(), "mouse - value does not change on clicking down button"); + + el.spinner('stepUp', 6); + equals(6, el.val(), "script - stepUp 6 steps changes value"); + + el.spinner('stepDown'); + equals(5, el.val(), "script - stepDown 1 step changes value"); + + el.spinner('pageUp'); + equals(10, el.val(), "script - pageUp 1 page changes value"); + + el.spinner('pageDown'); + equals(5, el.val(), "script - pageDown 1 page changes value"); + +}); + +test("enable", function() { + expect(14); + + el = $("#spin").spinner({ disabled: true }); + var val = el.val(); + + ok(wrapper().hasClass(".ui-spinner-disabled"), "before: wrapper has ui-spinner-disabled class"); + ok(box().is(':disabled'), "before: input has disabled attribute"); + + el.spinner("enable"); + + ok(!wrapper().hasClass(".ui-spinner-disabled"), "after: wrapper does not have ui-spinner-disabled class"); + ok(!box().is(':disabled'), "after: input does not have disabled attribute"); + + simulateKeyDownUp(el, $.ui.keyCode.UP); + equals(1, el.val(), "keyboard - value changes on key UP"); + + simulateKeyDownUp(el, $.ui.keyCode.DOWN); + equals(0, el.val(), "keyboard - value changes on key DOWN"); + + simulateKeyDownUp(el, $.ui.keyCode.PAGE_UP); + equals(5, el.val(), "keyboard - value changes on key PGUP"); + + simulateKeyDownUp(el, $.ui.keyCode.PAGE_DOWN); + equals(0, el.val(), "keyboard - value changes on key PGDN"); + + upButton().trigger('mousedown').trigger('mouseup'); + equals(1, el.val(), "mouse - value changes on clicking up button"); + + downButton().trigger('mousedown').trigger('mouseup'); + equals(0, el.val(), "mouse - value changes on clicking down button"); + + el.spinner('stepUp', 6); + equals(6, el.val(), "script - stepUp 6 steps changes value"); + + el.spinner('stepDown'); + equals(5, el.val(), "script - stepDown 1 step changes value"); + + el.spinner('pageUp'); + equals(10, el.val(), "script - pageUp 1 page changes value"); + + el.spinner('pageDown'); + equals(5, el.val(), "script - pageDown 1 page changes value"); + +}); + +test("pageDown", function() { + expect(4); + + el = $('#spin').spinner({ step: 2, page: 5, value: 0, min: -100 }); + + el.spinner('pageDown'); + equals(el.val(), -10, "pageDown 1 page"); + + el.spinner('pageDown', 3); + equals(el.val(), -40, "pageDown 3 pages"); + + el.val(-91).spinner('pageDown'); + equals(el.val(), -100, "value close to min and pageDown 1 page"); + + el.spinner('pageDown', 10); + equals(el.val(), -100, "value at min and pageDown 10 pages"); +}); + +test("pageUp", function() { + expect(4); + + el = $('#spin').spinner({ step: 2, page: 5, value: 0, max: 100 }); + + el.spinner('pageUp'); + equals(el.val(), 10, "pageUp 1 page"); + + el.spinner('pageUp', 3); + equals(el.val(), 40, "pageUp 3 pages"); + + el.val(91).spinner('pageUp'); + equals(el.val(), 100, "value close to max and pageUp 1 page"); + + el.spinner('pageUp', 10); + equals(el.val(), 100, "value at max and pageUp 10 pages"); + +}); + +test("stepDown", function() { + expect(4); + + el = $('#spin').spinner({ step: 2, page: 5, value: 0, min: -15 }); + + el.spinner('stepDown') + equals(el.val(), -2, "stepDown 1 step"); + + el.spinner('stepDown', 5) + equals(el.val(), -12, "stepDown 5 steps"); + + el.spinner('stepDown', 3); + equals(el.val(), -15, "close to min and stepDown 3 steps"); + + el.spinner('stepDown'); + equals(el.val(), -15, "at min and stepDown 1 step"); +}); + +test("stepUp", function() { + expect(4); + + el = $('#spin').spinner({ step: 2, page: 5, value: 0, max: 15 }); + + el.spinner('stepUp') + equals(el.val(), 2, "stepUp 1 steps"); + + el.spinner('stepUp', 5) + equals(el.val(), 12, "stepUp 5 steps"); + + el.spinner('stepUp', 3); + equals(el.val(), 15, "close to min and stepUp 3 steps"); + + el.spinner('stepUp'); + equals(el.val(), 15, "at min and stepUp 1 step"); + +}); + +test("value", function() { + expect(2); + + el = $('#spin').spinner({ value: 0 }); + + el.spinner('value', 10); + equals(el.val(), 10, "change value via value method"); + + equals(10, el.spinner('value'), "get value via value method"); +}); + +})(jQuery); diff --git a/tests/unit/spinner/spinner_options.js b/tests/unit/spinner/spinner_options.js new file mode 100644 index 000000000..b568d3cdd --- /dev/null +++ b/tests/unit/spinner/spinner_options.js @@ -0,0 +1,482 @@ +/* + * spinner_options.js + */ +(function($) { + +module("spinner: options"); + +test("buttons - show (default)", function() { + expect(4); + + el = $('#spin'); + el.spinner({ + buttons: 'show' + }); + + ok(upButton().is(':visible'), "show - before hover: up button visible"); + ok(downButton().is(':visible'), "show - before hover: down button visible"); + + el.trigger('mouseover'); + + ok(upButton().is(':visible'), "show - after hover: up button visible"); + ok(downButton().is(':visible'), "show - after hover: down button visible"); + + el.trigger('mouseout'); +}); + +test("buttons - hide", function() { + expect(4); + + el = $('#spin'); + el.spinner({ + buttons: 'hide' + }); + + ok(upButton().is(':hidden'), "hide - before hover: up button hidden"); + ok(downButton().is(':hidden'), "hide - before hover: down button hidden"); + + el.trigger('mouseover'); + + ok(upButton().is(':hidden'), "hide - after hover: up button hidden"); + ok(downButton().is(':hidden'), "hide - after hover: down button hidden"); + + el.trigger('mouseout'); +}); + +test("buttons - auto (hover)", function() { + expect(4); + + el = $('#spin'); + el.spinner({ + buttons: 'auto' + }); + + ok(upButton().is(':hidden'), "auto - before hover: up button hidden"); + ok(downButton().is(':hidden'), "auto - before hover: down button hidden"); + + el.trigger('mouseover'); + + ok(upButton().is(':visible'), "auto - after hover: up button visible"); + ok(downButton().is(':visible'), "auto - after hover: down button visible"); + + el.trigger('mouseout'); +}); + +test("buttons - auto (focus)", function() { + expect(4); + + el = $('#spin'); + el.spinner({ + buttons: 'auto' + }); + + ok(upButton().is(':hidden'), "auto - before focus: up button hidden"); + ok(downButton().is(':hidden'), "auto - before focus: down button hidden"); + + el.focus(); + + ok(upButton().is(':visible'), "auto - after focus: up button visible"); + ok(downButton().is(':visible'), "auto - after focus: down button visible"); + + el.trigger('mouseout'); +}); + +test("currency - single character currency symbol", function() { + expect(5); + + el = $("#spin"); + + options = { + currency:"$", + max:120, + min:-50, + step:0.3 + }; + + el.spinner(options); + + equals(el.val(), "$0.00", "start number"); + + simulateKeyDownUp(el, $.ui.keyCode.UP); + + equals(el.val(), "$0.30", "stepping 0.30"); + + simulateKeyDownUp(el, $.ui.keyCode.END); + + equals(el.val(), "$120.00", "End key to max"); + + simulateKeyDownUp(el, $.ui.keyCode.HOME); + + equals(el.val(), "-$50.00", "Home key to min"); + + for ( var i = 1 ; i<=120 ; i++ ) { + simulateKeyDownUp(el, $.ui.keyCode.UP); + } + + equals(el.val(), "-$14.00", "keydown 120 times"); +}); + +test("currency - combined character currency symbol", function() { + expect(2); + + el = $('#spin'); + + options = { + currency: 'HK$', + step: 1500.50, + value: 1000 + } + + el.spinner(options); + + equals(el.val(), "HK$1,000.00", "Hong Kong Dollar"); + + simulateKeyDownUp(el, $.ui.keyCode.UP); + + equals(el.val(), "HK$2,500.50", "Hong Kong Dollar step-up once"); +}); + +test("currency - space as group separator", function() { + expect(2); + + el = $('#spin'); + + options = { + currency: '$', + groupSeparator: ' ', + radixPoint: '.', + step: 1500.50, + value: 1000 + } + + el.spinner(options); + + equals(el.val(), "$1 000.00", "Australian Dollar"); + + simulateKeyDownUp(el, $.ui.keyCode.UP); + + equals(el.val(), "$2 500.50", "Australian Dollar step-up once"); +}); + +test("currency - apos as group separator", function() { + expect(2); + + el = $('#spin'); + options = { + currency: 'Fr ', + groupSeparator: "'", + radixPoint: '.', + step: 1500.50, + value: 1000 + } + el.spinner(options); + + equals(el.val(), "Fr 1'000.00", "Swiss Franc"); + + simulateKeyDownUp(el, $.ui.keyCode.UP); + + equals(el.val(), "Fr 2'500.50", "Swiss Franc step-up once"); +}); + +test("currency - period as group separator and comma as radixPoint", function() { + expect(2); + + el = $('#spin'); + options = { + currency: 'RUB', + groupSeparator: ".", + radixPoint: ',', + step: 1.5, + value: 1000 + } + el.spinner(options); + + equals(el.val(), "RUB1.000,00", "Russian Ruble"); + + simulateKeyDownUp(el, $.ui.keyCode.UP); + + equals(el.val(), "RUB1.001,50", "Russian Ruble step-up once"); +}); + +test("dir - left-to-right (default)", function() { + expect(3); + + el = $("#spin"); + el.spinner(); + + ok(upButton().position().left > box().position().left, 'input on left up button on right'); + ok(downButton().position().left > box().position().left, 'input on left down button on right'); + ok(wrapper().hasClass('ui-spinner-ltr'), 'container has correct text direction class setting'); +}); + +test("dir - right-to-left", function() { + expect(3); + + el = $("#spin"); + el.spinner({ dir: 'rtl' }); + + ok(upButton().position().left < box().position().left, 'input on right up button on left'); + ok(downButton().position().left < box().position().left, 'input on right down button on left'); + ok(wrapper().hasClass('ui-spinner-rtl'), 'container has correct text direction class setting'); +}); + +test("groupSeparator - comma separator (default)", function() { + expect(1); + + el = $('#spin'); + options = { + groupSeparator: ',', + value: 1000000 + }; + el.spinner(options); + equals(el.val(), "1,000,000", "value contains 2 commas separated by 3 digits"); +}); + +test("groupSeparator - space separator", function() { + expect(1); + + el = $('#spin'); + options = { + groupSeparator: ' ', + value: 1000000 + }; + el.spinner(options); + equals(el.val(), "1 000 000", "value contains 2 spaces separated by 3 digits"); +}); + +test("groupSeparator - apos separator", function() { + expect(1); + + el = $('#spin'); + options = { + groupSeparator: "'", + value: 1000000 + }; + el.spinner(options); + equals(el.val(), "1'000'000", "value contains apos separated by 3 digits"); +}); + +test("incremental - false (default)", function() { + expect(2); + + el = $("#spin").spinner({ incremental:false }); + + for ( var i = 1 ; i<=120 ; i++ ) { + el.simulate("keydown",{keyCode:$.ui.keyCode.UP}); + } + el.simulate("keyup",{keyCode:$.ui.keyCode.UP}); + + equals(el.val(), 120, "incremental false - keydown 120 times"); + + for ( var i = 1 ; i<=210 ; i++ ) { + el.simulate("keydown",{keyCode:$.ui.keyCode.DOWN}); + } + el.simulate("keyup",{keyCode:$.ui.keyCode.DOWN}); + + equals(el.val(), -90, "incremental false - keydown 210 times"); +}); + +test("incremental - true", function() { + expect(2); + + el.spinner('option', 'incremental', true ); + + for ( var i = 1 ; i<=120 ; i++ ) { + el.simulate("keydown",{keyCode:$.ui.keyCode.UP}); + } + el.simulate("keyup",{keyCode:$.ui.keyCode.UP}); + + equals(el.val(), 300, "incremental true - keydown 120 times (100+20*10)"); + + for ( var i = 1 ; i<=210 ; i++ ) { + el.simulate("keydown",{keyCode:$.ui.keyCode.DOWN}); + } + el.simulate("keyup",{keyCode:$.ui.keyCode.DOWN}); + + equals(el.val(), -1800, "incremental true - keydown 210 times (300-100-100*10-10*100)"); +}); + +test("max", function() { + expect(3); + + el = $("#spin").spinner({ max: 100, value: 1000 }); + equals(el.val(), 100, "max constrained if value option is greater"); + + el.spinner('value', 1000); + equals(el.val(), 100, "max constrained if value method is greater"); + + el.val(1000).blur(); + equals(el.val(), 100, "max constrained if manual entry"); +}); + +test("min", function() { + expect(3); + + el = $("#spin").spinner({ min: -100, value: -1000 }); + equals(el.val(), -100, "min constrained if value option is greater"); + + el.spinner('value', -1000); + equals(el.val(), -100, "min constrained if value method is greater"); + + el.val(-1000).blur(); + equals(el.val(), -100, "min constrained if manual entry"); +}); + +test("mouseWheel", function() { + ok(false, 'missing test - untested code is broken code'); +}); + +test("padding", function() { + expect(3); + + el = $('#spin').spinner({ padding: 5, value: 10 }); + + equals(el.val(), '00010', 'padded output'); + + el.spinner('option', 'padding', 4); + equals(el.val(), '0010', 'padded output'); + + el.spinner('value', 15); + equals(el.val(), '0015', 'padded output'); +}); + +test("page", function() { + expect(3); + + el = $("#spin").spinner({ step: 2, page:2.5 }); + + equals(el.val(), "0", "start number"); + + simulateKeyDownUp(el, $.ui.keyCode.PAGE_DOWN); + + equals(el.val(), "-5", "PAGE_DOWN on spinner once"); + + for ( var i = 1 ; i<=11 ; i++ ) { + simulateKeyDownUp(el, $.ui.keyCode.PAGE_UP); + } + + equals(el.val(), "50", "PAGE_UP 11 times on spinner"); +}); + +test("precision", function() { + expect(3); + + el = $("#spin").spinner({ precision: 4, value: 1.23456789 }); + + equals(el.val(), '1.2346', "4 decimal places"); + + el.spinner('option', 'precision', 2); + + equals(el.val(), '1.23', "2 decimal places"); + + el.spinner('option', 'precision', 6); + + equals(el.val(), '1.234568', "6 decimal places"); +}); + +test("radix", function() { + expect(2); + + el = $("#spin").spinner({ + radix: 16, + value: 10 + }); + + equals(el.val(), 'a', 'start value'); + + simulateKeyDownUp(el, $.ui.keyCode.UP); + + equals(el.val(), 'b', 'key UP on spinner') +}); + +test("radixPoint", function() { + el = $("#spin").spinner({ + radixPoint: ',', + value: 20.00, + precision: 2 + }); + + equals(el.val(), '20,00', 'comma radix point'); + + el.spinner('stepUp'); + + equals(el.val(), '21,00', 'step up one'); + + el.spinner('pageDown', 10); + + equals(el.val(), '-29,00', 'page down into negative space'); +}); + +test("spinnerClass", function() { + expect(3); + + el = $('#spin').spinner({spinnerClass: 'my-spinner-class'}); + ok(wrapper().hasClass('my-spinner-class'), 'spinner container has custom class'); + + el.spinner('option', 'spinnerClass', 'new-spinner-class'); + ok(!wrapper().hasClass('my-spinner-class'), 'spinner no longer has old custom class'); + ok(wrapper().hasClass('new-spinner-class'), 'spinner now has new custom class'); +}); + +test("step", function() { + expect(7); + + el = $("#spin").spinner({ step:0.7 }); + equals(el.val(), "0.0", "value initialized to"); + + simulateKeyDownUp(el, $.ui.keyCode.DOWN); + equals(el.val(), "-0.7", "DOWN 1 time with step: 0.7"); + + for ( var i = 0 ; i < 11 ; i++ ) { + simulateKeyDownUp(el, $.ui.keyCode.UP); + } + equals(el.val(), "7.0", "UP 11 times with step: 0.7"); + + el.spinner('option', 'step', 1); + for ( var i = 0 ; i < 3 ; i++ ) { + simulateKeyDownUp(el, $.ui.keyCode.UP); + } + equals(el.val(), "10.0", "UP 3 times with step: 1"); + + el.spinner('option', 'step', 2); + for ( var i = 0 ; i < 5 ; i++ ) { + simulateKeyDownUp(el, $.ui.keyCode.UP); + } + equals(el.val(), "20.0", "UP 5 times with step: 2"); + + el.spinner('value', '10.5'); + equals(el.val(), "10.5", "value reset to"); + + el.spinner('option', 'step', 2); + for ( var i = 0 ; i < 5 ; i++ ) { + simulateKeyDownUp(el, $.ui.keyCode.UP); + } + equals(el.val(), "20.5", "UP 5 times with step: 2"); + + +}); + +test("value", function() { + expect(2); + + el = $('#spin').spinner({ value: 100 }); + + equals(el.val(), 100, "starting value"); + + el.spinner('option', 'value', 1000); + + equals(el.val(), 1000, "value option changed and set as current value"); +}); + +test("width", function() { + expect(2); + + el = $('#spin').spinner({ width: 1000 }); + equals(el.width(), 1000, "spinner width initialized"); + + el.spinner('option', 'width', 500); + equals(el.width(), 500, "spinner width changed"); + +}); + +})(jQuery); -- cgit v1.2.3 From bc3d283d2c1547ac3d8b5aeb894b2e6290e2d3c6 Mon Sep 17 00:00:00 2001 From: jzaefferer Date: Sat, 23 Oct 2010 01:51:12 +0200 Subject: Replaced hexadecimal demo with decimal demo. Updated that and the currency demo (previously donation) to use globalization plugin with a select for three different cultures (probably more to come). Updated spinner widget to use Globalization when available, replacing all number formatting related options with a single numberformat option. --- demos/spinner/currency.html | 62 ++ demos/spinner/decimal.html | 58 ++ demos/spinner/donation.html | 74 --- demos/spinner/hexadecimal.html | 37 -- demos/spinner/index.html | 4 +- external/glob.de-DE.js | 55 ++ external/glob.ja-JP.js | 74 +++ external/glob.js | 1333 ++++++++++++++++++++++++++++++++++++++++ ui/jquery.ui.spinner.js | 174 ++---- 9 files changed, 1628 insertions(+), 243 deletions(-) create mode 100644 demos/spinner/currency.html create mode 100644 demos/spinner/decimal.html delete mode 100644 demos/spinner/donation.html delete mode 100644 demos/spinner/hexadecimal.html create mode 100644 external/glob.de-DE.js create mode 100644 external/glob.ja-JP.js create mode 100644 external/glob.js diff --git a/demos/spinner/currency.html b/demos/spinner/currency.html new file mode 100644 index 000000000..418dca228 --- /dev/null +++ b/demos/spinner/currency.html @@ -0,0 +1,62 @@ + + + + + jQuery UI Spinner - Default functionality + + + + + + + + + + + + + + + +
+ +

+ + +

+

+ + +

+
+ +
+

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

+
+ + + diff --git a/demos/spinner/decimal.html b/demos/spinner/decimal.html new file mode 100644 index 000000000..82d0fcc20 --- /dev/null +++ b/demos/spinner/decimal.html @@ -0,0 +1,58 @@ + + + + + jQuery UI Spinner - decimal + + + + + + + + + + + + + + + +
+

+ + +

+

+ + +

+
+ +
+

+ Example of a decimal spinner. Step is set to 0.01. +
The code handling the culture change reads the current spinner value, + then changes the culture, then sets the value again, resulting in an updated + formatting, based on the new culture. +

+
+ + + diff --git a/demos/spinner/donation.html b/demos/spinner/donation.html deleted file mode 100644 index 617570b12..000000000 --- a/demos/spinner/donation.html +++ /dev/null @@ -1,74 +0,0 @@ - - - - - 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 deleted file mode 100644 index 03952a62d..000000000 --- a/demos/spinner/hexadecimal.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - jQuery UI Spinner - Hexadecimal - - - - - - - - - - - - -
- -

- - -

-
- -
-

-Example of a hexadecimal spinner. -

-
- - - diff --git a/demos/spinner/index.html b/demos/spinner/index.html index 6d7b980bd..68716ac99 100644 --- a/demos/spinner/index.html +++ b/demos/spinner/index.html @@ -9,8 +9,8 @@

Examples

  • Default functionality
  • -
  • Donation
  • -
  • Hexadecimal
  • +
  • Decimal
  • +
  • Currency
  • Map
  • Mousewheel Disabled
  • RTL
  • diff --git a/external/glob.de-DE.js b/external/glob.de-DE.js new file mode 100644 index 000000000..d68b84cc8 --- /dev/null +++ b/external/glob.de-DE.js @@ -0,0 +1,55 @@ +(function($) { + var cultures = $.cultures, + en = cultures.en, + standard = en.calendars.standard, + culture = cultures["de-DE"] = $.extend(true, {}, en, { + name: "de-DE", + englishName: "German (Germany)", + nativeName: "Deutsch (Deutschland)", + language: "de", + numberFormat: { + ',': ".", + '.': ",", + percent: { + pattern: ["-n%","n%"], + ',': ".", + '.': "," + }, + currency: { + pattern: ["-n $","n $"], + ',': ".", + '.': ",", + symbol: "€" + } + }, + calendars: { + standard: $.extend(true, {}, standard, { + '/': ".", + firstDay: 1, + days: { + names: ["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"], + namesAbbr: ["So","Mo","Di","Mi","Do","Fr","Sa"], + namesShort: ["So","Mo","Di","Mi","Do","Fr","Sa"] + }, + months: { + names: ["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember",""], + namesAbbr: ["Jan","Feb","Mrz","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez",""] + }, + AM: null, + PM: null, + eras: [{"name":"n. Chr.","start":null,"offset":0}], + patterns: { + d: "dd.MM.yyyy", + D: "dddd, d. MMMM yyyy", + t: "HH:mm", + T: "HH:mm:ss", + f: "dddd, d. MMMM yyyy HH:mm", + F: "dddd, d. MMMM yyyy HH:mm:ss", + M: "dd MMMM", + Y: "MMMM yyyy" + } + }) + } + }, cultures["de-DE"]); + culture.calendar = culture.calendars.standard; +})(Globalization); \ No newline at end of file diff --git a/external/glob.ja-JP.js b/external/glob.ja-JP.js new file mode 100644 index 000000000..454d478cb --- /dev/null +++ b/external/glob.ja-JP.js @@ -0,0 +1,74 @@ +(function($) { + var cultures = $.cultures, + en = cultures.en, + standard = en.calendars.standard, + culture = cultures["ja-JP"] = $.extend(true, {}, en, { + name: "ja-JP", + englishName: "Japanese (Japan)", + nativeName: "日本語 (日本)", + language: "ja", + numberFormat: { + percent: { + pattern: ["-n%","n%"] + }, + currency: { + pattern: ["-$n","$n"], + decimals: 0, + symbol: "¥" + } + }, + calendars: { + standard: $.extend(true, {}, standard, { + days: { + names: ["日曜日","月曜日","火曜日","水曜日","木曜日","金曜日","土曜日"], + namesAbbr: ["日","月","火","水","木","金","土"], + namesShort: ["日","月","火","水","木","金","土"] + }, + months: { + names: ["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月",""], + namesAbbr: ["1","2","3","4","5","6","7","8","9","10","11","12",""] + }, + AM: ["午前","午前","午前"], + PM: ["午後","午後","午後"], + eras: [{"name":"西暦","start":null,"offset":0}], + patterns: { + d: "yyyy/MM/dd", + D: "yyyy'年'M'月'd'日'", + t: "H:mm", + T: "H:mm:ss", + f: "yyyy'年'M'月'd'日' H:mm", + F: "yyyy'年'M'月'd'日' H:mm:ss", + M: "M'月'd'日'", + Y: "yyyy'年'M'月'" + } + }), + Japanese: $.extend(true, {}, standard, { + name: "Japanese", + days: { + names: ["日曜日","月曜日","火曜日","水曜日","木曜日","金曜日","土曜日"], + namesAbbr: ["日","月","火","水","木","金","土"], + namesShort: ["日","月","火","水","木","金","土"] + }, + months: { + names: ["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月",""], + namesAbbr: ["1","2","3","4","5","6","7","8","9","10","11","12",""] + }, + AM: ["午前","午前","午前"], + PM: ["午後","午後","午後"], + eras: [{"name":"平成","start":null,"offset":1867},{"name":"昭和","start":-1812153600000,"offset":1911},{"name":"大正","start":-1357603200000,"offset":1925},{"name":"明治","start":60022080000,"offset":1988}], + twoDigitYearMax: 99, + patterns: { + d: "gg y/M/d", + D: "gg y'年'M'月'd'日'", + t: "H:mm", + T: "H:mm:ss", + f: "gg y'年'M'月'd'日' H:mm", + F: "gg y'年'M'月'd'日' H:mm:ss", + M: "M'月'd'日'", + Y: "gg y'年'M'月'" + } + }) + } + }, cultures["ja-JP"]); + culture.calendar = culture.calendars.standard; +})(Globalization); \ No newline at end of file diff --git a/external/glob.js b/external/glob.js new file mode 100644 index 000000000..8075ee2bd --- /dev/null +++ b/external/glob.js @@ -0,0 +1,1333 @@ +/* + * Globalization + * http://github.com/nje/jquery-glob + */ +(function() { + +var Globalization = {}, + localized = { en: {} }; +localized["default"] = localized.en; + +Globalization.extend = function( deep ) { + var target = arguments[ 1 ] || {}; + for ( var i = 2, l = arguments.length; i < l; i++ ) { + var source = arguments[ i ]; + if ( source ) { + for ( var field in source ) { + var sourceVal = source[ field ]; + if ( typeof sourceVal !== "undefined" ) { + if ( deep && (isObject( sourceVal ) || isArray( sourceVal )) ) { + var targetVal = target[ field ]; + // extend onto the existing value, or create a new one + targetVal = targetVal && (isObject( targetVal ) || isArray( targetVal )) + ? targetVal + : (isArray( sourceVal ) ? [] : {}); + target[ field ] = this.extend( true, targetVal, sourceVal ); + } + else { + target[ field ] = sourceVal; + } + } + } + } + } + return target; +} + +Globalization.findClosestCulture = function(name) { + var match; + if ( !name ) { + return this.culture || this.cultures["default"]; + } + if ( isString( name ) ) { + name = name.split( ',' ); + } + if ( isArray( name ) ) { + var lang, + cultures = this.cultures, + list = name, + i, l = list.length, + prioritized = []; + for ( i = 0; i < l; i++ ) { + name = trim( list[ i ] ); + var pri, parts = name.split( ';' ); + lang = trim( parts[ 0 ] ); + if ( parts.length === 1 ) { + pri = 1; + } + else { + name = trim( parts[ 1 ] ); + if ( name.indexOf("q=") === 0 ) { + name = name.substr( 2 ); + pri = parseFloat( name, 10 ); + pri = isNaN( pri ) ? 0 : pri; + } + else { + pri = 1; + } + } + prioritized.push( { lang: lang, pri: pri } ); + } + prioritized.sort(function(a, b) { + return a.pri < b.pri ? 1 : -1; + }); + for ( i = 0; i < l; i++ ) { + lang = prioritized[ i ].lang; + match = cultures[ lang ]; + // exact match? + if ( match ) { + return match; + } + } + for ( i = 0; i < l; i++ ) { + lang = prioritized[ i ].lang; + // for each entry try its neutral language + do { + var index = lang.lastIndexOf( "-" ); + if ( index === -1 ) { + break; + } + // strip off the last part. e.g. en-US => en + lang = lang.substr( 0, index ); + match = cultures[ lang ]; + if ( match ) { + return match; + } + } + while ( 1 ); + } + } + else if ( typeof name === 'object' ) { + return name; + } + return match || null; +} +Globalization.preferCulture = function(name) { + this.culture = this.findClosestCulture( name ) || this.cultures["default"]; +} +Globalization.localize = function(key, culture, value) { + if (typeof culture === 'string') { + culture = culture || "default"; + culture = this.cultures[ culture ] || { name: culture }; + } + var local = localized[ culture.name ]; + if ( arguments.length === 3 ) { + if ( !local) { + local = localized[ culture.name ] = {}; + } + local[ key ] = value; + } + else { + if ( local ) { + value = local[ key ]; + } + if ( typeof value === 'undefined' ) { + var language = localized[ culture.language ]; + if ( language ) { + value = language[ key ]; + } + if ( typeof value === 'undefined' ) { + value = localized["default"][ key ]; + } + } + } + return typeof value === "undefined" ? null : value; +} +Globalization.format = function(value, format, culture) { + culture = this.findClosestCulture( culture ); + if ( typeof value === "number" ) { + value = formatNumber( value, format, culture ); + } + else if ( value instanceof Date ) { + value = formatDate( value, format, culture ); + } + return value; +} +Globalization.parseInt = function(value, radix, culture) { + return Math.floor( this.parseFloat( value, radix, culture ) ); +} +Globalization.parseFloat = function(value, radix, culture) { + culture = this.findClosestCulture( culture ); + var ret = NaN, + nf = culture.numberFormat; + + // trim leading and trailing whitespace + value = trim( value ); + + // allow infinity or hexidecimal + if (regexInfinity.test(value)) { + ret = parseFloat(value, radix); + } + else if (!radix && regexHex.test(value)) { + ret = parseInt(value, 16); + } + else { + var signInfo = parseNegativePattern( value, nf, nf.pattern[0] ), + sign = signInfo[0], + num = signInfo[1]; + // determine sign and number + if ( sign === "" && nf.pattern[0] !== "-n" ) { + signInfo = parseNegativePattern( value, nf, "-n" ); + sign = signInfo[0]; + num = signInfo[1]; + } + sign = sign || "+"; + // determine exponent and number + var exponent, + intAndFraction, + exponentPos = num.indexOf( 'e' ); + if ( exponentPos < 0 ) exponentPos = num.indexOf( 'E' ); + if ( exponentPos < 0 ) { + intAndFraction = num; + exponent = null; + } + else { + intAndFraction = num.substr( 0, exponentPos ); + exponent = num.substr( exponentPos + 1 ); + } + // determine decimal position + var integer, + fraction, + decSep = nf['.'], + decimalPos = intAndFraction.indexOf( decSep ); + if ( decimalPos < 0 ) { + integer = intAndFraction; + fraction = null; + } + else { + integer = intAndFraction.substr( 0, decimalPos ); + fraction = intAndFraction.substr( decimalPos + decSep.length ); + } + // handle groups (e.g. 1,000,000) + var groupSep = nf[","]; + integer = integer.split(groupSep).join(''); + var altGroupSep = groupSep.replace(/\u00A0/g, " "); + if ( groupSep !== altGroupSep ) { + integer = integer.split(altGroupSep).join(''); + } + // build a natively parsable number string + var p = sign + integer; + if ( fraction !== null ) { + p += '.' + fraction; + } + if ( exponent !== null ) { + // exponent itself may have a number patternd + var expSignInfo = parseNegativePattern( exponent, nf, "-n" ); + p += 'e' + (expSignInfo[0] || "+") + expSignInfo[1]; + } + if ( regexParseFloat.test( p ) ) { + ret = parseFloat( p ); + } + } + return ret; +} +Globalization.parseDate = function(value, formats, culture) { + culture = this.findClosestCulture( culture ); + + var date, prop, patterns; + if ( formats ) { + if ( typeof formats === "string" ) { + formats = [ formats ]; + } + if ( formats.length ) { + for ( var i = 0, l = formats.length; i < l; i++ ) { + var format = formats[ i ]; + if ( format ) { + date = parseExact( value, format, culture ); + if ( date ) { + break; + } + } + } + } + } + else { + patterns = culture.calendar.patterns; + for ( prop in patterns ) { + date = parseExact( value, patterns[prop], culture ); + if ( date ) { + break; + } + } + } + return date || null; +} + +// 1. When defining a culture, all fields are required except the ones stated as optional. +// 2. You can use Globalization.extend to copy an existing culture and provide only the differing values, +// a good practice since most cultures do not differ too much from the 'default' culture. +// DO use the 'default' culture if you do this, as it is the only one that definitely +// exists. +// 3. Other plugins may add to the culture information provided by extending it. However, +// that plugin may extend it prior to the culture being defined, or after. Therefore, +// do not overwrite values that already exist when defining the baseline for a culture, +// by extending your culture object with the existing one. +// 4. Each culture should have a ".calendars" object with at least one calendar named "standard" +// which serves as the default calendar in use by that culture. +// 5. Each culture should have a ".calendar" object which is the current calendar being used, +// it may be dynamically changed at any time to one of the calendars in ".calendars". + +// To define a culture, use the following pattern, which handles defining the culture based +// on the 'default culture, extending it with the existing culture if it exists, and defining +// it if it does not exist. +// Globalization.cultures.foo = Globalization.extend(true, Globalization.extend(true, {}, Globalization.cultures['default'], fooCulture), Globalization.cultures.foo) + +var cultures = Globalization.cultures = Globalization.cultures || {}; +var en = cultures["default"] = cultures.en = Globalization.extend(true, { + // A unique name for the culture in the form - + name: "en", + // the name of the culture in the english language + englishName: "English", + // the name of the culture in its own language + nativeName: "English", + // whether the culture uses right-to-left text + isRTL: false, + // 'language' is used for so-called "specific" cultures. + // For example, the culture "es-CL" means "Spanish, in Chili". + // It represents the Spanish-speaking culture as it is in Chili, + // which might have different formatting rules or even translations + // than Spanish in Spain. A "neutral" culture is one that is not + // specific to a region. For example, the culture "es" is the generic + // Spanish culture, which may be a more generalized version of the language + // that may or may not be what a specific culture expects. + // For a specific culture like "es-CL", the 'language' field refers to the + // neutral, generic culture information for the language it is using. + // This is not always a simple matter of the string before the dash. + // For example, the "zh-Hans" culture is netural (Simplified Chinese). + // And the 'zh-SG' culture is Simplified Chinese in Singapore, whose lanugage + // field is "zh-CHS", not "zh". + // This field should be used to navigate from a specific culture to it's + // more general, neutral culture. If a culture is already as general as it + // can get, the language may refer to itself. + language: "en", + // numberFormat defines general number formatting rules, like the digits in + // each grouping, the group separator, and how negative numbers are displayed. + numberFormat: { + // [negativePattern] + // Note, numberFormat.pattern has no 'positivePattern' unlike percent and currency, + // but is still defined as an array for consistency with them. + // negativePattern: one of "(n)|-n|- n|n-|n -" + pattern: ["-n"], + // number of decimal places normally shown + decimals: 2, + // string that separates number groups, as in 1,000,000 + ',': ",", + // string that separates a number from the fractional portion, as in 1.99 + '.': ".", + // array of numbers indicating the size of each number group. + // TODO: more detailed description and example + groupSizes: [3], + // symbol used for positive numbers + '+': "+", + // symbol used for negative numbers + '-': "-", + percent: { + // [negativePattern, positivePattern] + // negativePattern: one of "-n %|-n%|-%n|%-n|%n-|n-%|n%-|-% n|n %-|% n-|% -n|n- %" + // positivePattern: one of "n %|n%|%n|% n" + pattern: ["-n %","n %"], + // number of decimal places normally shown + decimals: 2, + // array of numbers indicating the size of each number group. + // TODO: more detailed description and example + groupSizes: [3], + // string that separates number groups, as in 1,000,000 + ',': ",", + // string that separates a number from the fractional portion, as in 1.99 + '.': ".", + // symbol used to represent a percentage + symbol: "%" + }, + currency: { + // [negativePattern, positivePattern] + // negativePattern: one of "($n)|-$n|$-n|$n-|(n$)|-n$|n-$|n$-|-n $|-$ n|n $-|$ n-|$ -n|n- $|($ n)|(n $)" + // positivePattern: one of "$n|n$|$ n|n $" + pattern: ["($n)","$n"], + // number of decimal places normally shown + decimals: 2, + // array of numbers indicating the size of each number group. + // TODO: more detailed description and example + groupSizes: [3], + // string that separates number groups, as in 1,000,000 + ',': ",", + // string that separates a number from the fractional portion, as in 1.99 + '.': ".", + // symbol used to represent currency + symbol: "$" + } + }, + // calendars defines all the possible calendars used by this culture. + // There should be at least one defined with name 'standard', and is the default + // calendar used by the culture. + // A calendar contains information about how dates are formatted, information about + // the calendar's eras, a standard set of the date formats, + // translations for day and month names, and if the calendar is not based on the Gregorian + // calendar, conversion functions to and from the Gregorian calendar. + calendars: { + standard: { + // name that identifies the type of calendar this is + name: "Gregorian_USEnglish", + // separator of parts of a date (e.g. '/' in 11/05/1955) + '/': "/", + // separator of parts of a time (e.g. ':' in 05:44 PM) + ':': ":", + // the first day of the week (0 = Sunday, 1 = Monday, etc) + firstDay: 0, + days: { + // full day names + names: ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"], + // abbreviated day names + namesAbbr: ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"], + // shortest day names + namesShort: ["Su","Mo","Tu","We","Th","Fr","Sa"] + }, + months: { + // full month names (13 months for lunar calendards -- 13th month should be "" if not lunar) + names: ["January","February","March","April","May","June","July","August","September","October","November","December",""], + // abbreviated month names + namesAbbr: ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec",""] + }, + // AM and PM designators in one of these forms: + // The usual view, and the upper and lower case versions + // [standard,lowercase,uppercase] + // The culture does not use AM or PM (likely all standard date formats use 24 hour time) + // null + AM: ["AM", "am", "AM"], + PM: ["PM", "pm", "PM"], + eras: [ + // eras in reverse chronological order. + // name: the name of the era in this culture (e.g. A.D., C.E.) + // start: when the era starts in ticks (gregorian, gmt), null if it is the earliest supported era. + // offset: offset in years from gregorian calendar + { "name": "A.D.", "start": null, "offset": 0 } + ], + // when a two digit year is given, it will never be parsed as a four digit + // year greater than this year (in the appropriate era for the culture) + // Set it as a full year (e.g. 2029) or use an offset format starting from + // the current year: "+19" would correspond to 2029 if the current year 2010. + twoDigitYearMax: 2029, + // set of predefined date and time patterns used by the culture + // these represent the format someone in this culture would expect + // to see given the portions of the date that are shown. + patterns: { + // short date pattern + d: "M/d/yyyy", + // long date pattern + D: "dddd, MMMM dd, yyyy", + // short time pattern + t: "h:mm tt", + // long time pattern + T: "h:mm:ss tt", + // long date, short time pattern + f: "dddd, MMMM dd, yyyy h:mm tt", + // long date, long time pattern + F: "dddd, MMMM dd, yyyy h:mm:ss tt", + // month/day pattern + M: "MMMM dd", + // month/year pattern + Y: "yyyy MMMM", + // S is a sortable format that does not vary by culture + S: "yyyy\u0027-\u0027MM\u0027-\u0027dd\u0027T\u0027HH\u0027:\u0027mm\u0027:\u0027ss" + } + // optional fields for each calendar: + /* + monthsGenitive: + Same as months but used when the day preceeds the month. + Omit if the culture has no genitive distinction in month names. + For an explaination of genitive months, see http://blogs.msdn.com/michkap/archive/2004/12/25/332259.aspx + convert: + Allows for the support of non-gregorian based calendars. This convert object is used to + to convert a date to and from a gregorian calendar date to handle parsing and formatting. + The two functions: + fromGregorian(date) + Given the date as a parameter, return an array with parts [year, month, day] + corresponding to the non-gregorian based year, month, and day for the calendar. + toGregorian(year, month, day) + Given the non-gregorian year, month, and day, return a new Date() object + set to the corresponding date in the gregorian calendar. + */ + } + } +}, cultures.en); +en.calendar = en.calendar || en.calendars.standard; + +var regexTrim = /^\s+|\s+$/g, + regexInfinity = /^[+-]?infinity$/i, + regexHex = /^0x[a-f0-9]+$/i, + regexParseFloat = /^[+-]?\d*\.?\d*(e[+-]?\d+)?$/, + toString = Object.prototype.toString; + +function startsWith(value, pattern) { + return value.indexOf( pattern ) === 0; +} + +function endsWith(value, pattern) { + return value.substr( value.length - pattern.length ) === pattern; +} + +function trim(value) { + return (value+"").replace( regexTrim, "" ); +} + +function zeroPad(str, count, left) { + for (var l=str.length; l < count; l++) { + str = (left ? ('0' + str) : (str + '0')); + } + return str; +} + +function isArray(obj) { + return toString.call(obj) === "[object Array]"; +} + +function isString(obj) { + return toString.call(obj) === "[object String]"; +} + +function isObject(obj) { + return toString.call(obj) === "[object Object]"; +} + +function arrayIndexOf( array, item ) { + if ( array.indexOf ) { + return array.indexOf( item ); + } + for ( var i = 0, length = array.length; i < length; i++ ) { + if ( array[ i ] === item ) { + return i; + } + } + return -1; +} + +// *************************************** Numbers *************************************** + +function expandNumber(number, precision, formatInfo) { + var groupSizes = formatInfo.groupSizes, + curSize = groupSizes[ 0 ], + curGroupIndex = 1, + factor = Math.pow( 10, precision ), + rounded = Math.round( number * factor ) / factor; + if ( !isFinite(rounded) ) { + rounded = number; + } + number = rounded; + + var numberString = number+"", + right = "", + split = numberString.split(/e/i), + exponent = split.length > 1 ? parseInt( split[ 1 ], 10 ) : 0; + numberString = split[ 0 ]; + split = numberString.split( "." ); + numberString = split[ 0 ]; + right = split.length > 1 ? split[ 1 ] : ""; + + var l; + if ( exponent > 0 ) { + right = zeroPad( right, exponent, false ); + numberString += right.slice( 0, exponent ); + right = right.substr( exponent ); + } + else if ( exponent < 0 ) { + exponent = -exponent; + numberString = zeroPad( numberString, exponent + 1 ); + right = numberString.slice( -exponent, numberString.length ) + right; + numberString = numberString.slice( 0, -exponent ); + } + + if ( precision > 0 ) { + right = formatInfo['.'] + + ((right.length > precision) ? right.slice( 0, precision ) : zeroPad( right, precision )); + } + else { + right = ""; + } + + var stringIndex = numberString.length - 1, + sep = formatInfo[","], + ret = ""; + + while ( stringIndex >= 0 ) { + if ( curSize === 0 || curSize > stringIndex ) { + return numberString.slice( 0, stringIndex + 1 ) + ( ret.length ? ( sep + ret + right ) : right ); + } + ret = numberString.slice( stringIndex - curSize + 1, stringIndex + 1 ) + ( ret.length ? ( sep + ret ) : "" ); + + stringIndex -= curSize; + + if ( curGroupIndex < groupSizes.length ) { + curSize = groupSizes[ curGroupIndex ]; + curGroupIndex++; + } + } + return numberString.slice( 0, stringIndex + 1 ) + sep + ret + right; +} + + +function parseNegativePattern(value, nf, negativePattern) { + var neg = nf["-"], + pos = nf["+"], + ret; + switch (negativePattern) { + case "n -": + neg = ' ' + neg; + pos = ' ' + pos; + // fall through + case "n-": + if ( endsWith( value, neg ) ) { + ret = [ '-', value.substr( 0, value.length - neg.length ) ]; + } + else if ( endsWith( value, pos ) ) { + ret = [ '+', value.substr( 0, value.length - pos.length ) ]; + } + break; + case "- n": + neg += ' '; + pos += ' '; + // fall through + case "-n": + if ( startsWith( value, neg ) ) { + ret = [ '-', value.substr( neg.length ) ]; + } + else if ( startsWith(value, pos) ) { + ret = [ '+', value.substr( pos.length ) ]; + } + break; + case "(n)": + if ( startsWith( value, '(' ) && endsWith( value, ')' ) ) { + ret = [ '-', value.substr( 1, value.length - 2 ) ]; + } + break; + } + return ret || [ '', value ]; +} + +function formatNumber(value, format, culture) { + if ( !format || format === 'i' ) { + return culture.name.length ? value.toLocaleString() : value.toString(); + } + format = format || "D"; + + var nf = culture.numberFormat, + number = Math.abs(value), + precision = -1, + pattern; + if (format.length > 1) precision = parseInt( format.slice( 1 ), 10 ); + + var current = format.charAt( 0 ).toUpperCase(), + formatInfo; + + switch (current) { + case "D": + pattern = 'n'; + if (precision !== -1) { + number = zeroPad( ""+number, precision, true ); + } + if (value < 0) number = -number; + break; + case "N": + formatInfo = nf; + // fall through + case "C": + formatInfo = formatInfo || nf.currency; + // fall through + case "P": + formatInfo = formatInfo || nf.percent; + pattern = value < 0 ? formatInfo.pattern[0] : (formatInfo.pattern[1] || "n"); + if (precision === -1) precision = formatInfo.decimals; + number = expandNumber( number * (current === "P" ? 100 : 1), precision, formatInfo ); + break; + default: + throw "Bad number format specifier: " + current; + } + + var patternParts = /n|\$|-|%/g, + ret = ""; + for (;;) { + var index = patternParts.lastIndex, + ar = patternParts.exec(pattern); + + ret += pattern.slice( index, ar ? ar.index : pattern.length ); + + if (!ar) { + break; + } + + switch (ar[0]) { + case "n": + ret += number; + break; + case "$": + ret += nf.currency.symbol; + break; + case "-": + // don't make 0 negative + if ( /[1-9]/.test( number ) ) { + ret += nf["-"]; + } + break; + case "%": + ret += nf.percent.symbol; + break; + } + } + + return ret; +} + +// *************************************** Dates *************************************** + +function outOfRange(value, low, high) { + return value < low || value > high; +} + +function expandYear(cal, year) { + // expands 2-digit year into 4 digits. + var now = new Date(), + era = getEra(now); + if ( year < 100 ) { + var twoDigitYearMax = cal.twoDigitYearMax; + twoDigitYearMax = typeof twoDigitYearMax === 'string' ? new Date().getFullYear() % 100 + parseInt( twoDigitYearMax, 10 ) : twoDigitYearMax; + var curr = getEraYear( now, cal, era ); + year += curr - ( curr % 100 ); + if ( year > twoDigitYearMax ) { + year -= 100; + } + } + return year; +} + +function getEra(date, eras) { + if ( !eras ) return 0; + var start, ticks = date.getTime(); + for ( var i = 0, l = eras.length; i < l; i++ ) { + start = eras[ i ].start; + if ( start === null || ticks >= start ) { + return i; + } + } + return 0; +} + +function toUpper(value) { + // 'he-IL' has non-breaking space in weekday names. + return value.split( "\u00A0" ).join(' ').toUpperCase(); +} + +function toUpperArray(arr) { + var results = []; + for ( var i = 0, l = arr.length; i < l; i++ ) { + results[i] = toUpper(arr[i]); + } + return results; +} + +function getEraYear(date, cal, era, sortable) { + var year = date.getFullYear(); + if ( !sortable && cal.eras ) { + // convert normal gregorian year to era-shifted gregorian + // year by subtracting the era offset + year -= cal.eras[ era ].offset; + } + return year; +} + +function getDayIndex(cal, value, abbr) { + var ret, + days = cal.days, + upperDays = cal._upperDays; + if ( !upperDays ) { + cal._upperDays = upperDays = [ + toUpperArray( days.names ), + toUpperArray( days.namesAbbr ), + toUpperArray( days.namesShort ) + ]; + } + value = toUpper( value ); + if ( abbr ) { + ret = arrayIndexOf( upperDays[ 1 ], value ); + if ( ret === -1 ) { + ret = arrayIndexOf( upperDays[ 2 ], value ); + } + } + else { + ret = arrayIndexOf( upperDays[ 0 ], value ); + } + return ret; +} + +function getMonthIndex(cal, value, abbr) { + var months = cal.months, + monthsGen = cal.monthsGenitive || cal.months, + upperMonths = cal._upperMonths, + upperMonthsGen = cal._upperMonthsGen; + if ( !upperMonths ) { + cal._upperMonths = upperMonths = [ + toUpperArray( months.names ), + toUpperArray( months.namesAbbr ), + ]; + cal._upperMonthsGen = upperMonthsGen = [ + toUpperArray( monthsGen.names ), + toUpperArray( monthsGen.namesAbbr ) + ]; + } + value = toUpper( value ); + var i = arrayIndexOf( abbr ? upperMonths[ 1 ] : upperMonths[ 0 ], value ); + if ( i < 0 ) { + i = arrayIndexOf( abbr ? upperMonthsGen[ 1 ] : upperMonthsGen[ 0 ], value ); + } + return i; +} + +function appendPreOrPostMatch(preMatch, strings) { + // appends pre- and post- token match strings while removing escaped characters. + // Returns a single quote count which is used to determine if the token occurs + // in a string literal. + var quoteCount = 0, + escaped = false; + for ( var i = 0, il = preMatch.length; i < il; i++ ) { + var c = preMatch.charAt( i ); + switch ( c ) { + case '\'': + if ( escaped ) { + strings.push( "'" ); + } + else { + quoteCount++; + } + escaped = false; + break; + case '\\': + if ( escaped ) { + strings.push( "\\" ); + } + escaped = !escaped; + break; + default: + strings.push( c ); + escaped = false; + break; + } + } + return quoteCount; +} + +function expandFormat(cal, format) { + // expands unspecified or single character date formats into the full pattern. + format = format || "F"; + var pattern, + patterns = cal.patterns, + len = format.length; + if ( len === 1 ) { + pattern = patterns[ format ]; + if ( !pattern ) { + throw "Invalid date format string '" + format + "'."; + } + format = pattern; + } + else if ( len === 2 && format.charAt(0) === "%" ) { + // %X escape format -- intended as a custom format string that is only one character, not a built-in format. + format = format.charAt( 1 ); + } + return format; +} + +function getParseRegExp(cal, format) { + // converts a format string into a regular expression with groups that + // can be used to extract date fields from a date string. + // check for a cached parse regex. + var re = cal._parseRegExp; + if ( !re ) { + cal._parseRegExp = re = {}; + } + else { + var reFormat = re[ format ]; + if ( reFormat ) { + return reFormat; + } + } + + // expand single digit formats, then escape regular expression characters. + var expFormat = expandFormat( cal, format ).replace( /([\^\$\.\*\+\?\|\[\]\(\)\{\}])/g, "\\\\$1" ), + regexp = ["^"], + groups = [], + index = 0, + quoteCount = 0, + tokenRegExp = getTokenRegExp(), + match; + + // iterate through each date token found. + while ( (match = tokenRegExp.exec( expFormat )) !== null ) { + var preMatch = expFormat.slice( index, match.index ); + index = tokenRegExp.lastIndex; + + // don't replace any matches that occur inside a string literal. + quoteCount += appendPreOrPostMatch( preMatch, regexp ); + if ( quoteCount % 2 ) { + regexp.push( match[ 0 ] ); + continue; + } + + // add a regex group for the token. + var m = match[ 0 ], + len = m.length, + add; + switch ( m ) { + case 'dddd': case 'ddd': + case 'MMMM': case 'MMM': + case 'gg': case 'g': + add = "(\\D+)"; + break; + case 'tt': case 't': + add = "(\\D*)"; + break; + case 'yyyy': + case 'fff': + case 'ff': + case 'f': + add = "(\\d{" + len + "})"; + break; + case 'dd': case 'd': + case 'MM': case 'M': + case 'yy': case 'y': + case 'HH': case 'H': + case 'hh': case 'h': + case 'mm': case 'm': + case 'ss': case 's': + add = "(\\d\\d?)"; + break; + case 'zzz': + add = "([+-]?\\d\\d?:\\d{2})"; + break; + case 'zz': case 'z': + add = "([+-]?\\d\\d?)"; + break; + case '/': + add = "(\\" + cal["/"] + ")"; + break; + default: + throw "Invalid date format pattern '" + m + "'."; + break; + } + if ( add ) { + regexp.push( add ); + } + groups.push( match[ 0 ] ); + } + appendPreOrPostMatch( expFormat.slice( index ), regexp ); + regexp.push( "$" ); + + // allow whitespace to differ when matching formats. + var regexpStr = regexp.join( '' ).replace( /\s+/g, "\\s+" ), + parseRegExp = {'regExp': regexpStr, 'groups': groups}; + + // cache the regex for this format. + return re[ format ] = parseRegExp; +} + +function getTokenRegExp() { + // regular expression for matching date and time tokens in format strings. + return /\/|dddd|ddd|dd|d|MMMM|MMM|MM|M|yyyy|yy|y|hh|h|HH|H|mm|m|ss|s|tt|t|fff|ff|f|zzz|zz|z|gg|g/g; +} + +function parseExact(value, format, culture) { + // try to parse the date string by matching against the format string + // while using the specified culture for date field names. + value = trim( value ); + var cal = culture.calendar, + // convert date formats into regular expressions with groupings. + // use the regexp to determine the input format and extract the date fields. + parseInfo = getParseRegExp(cal, format), + match = new RegExp(parseInfo.regExp).exec(value); + if (match === null) { + return null; + } + // found a date format that matches the input. + var groups = parseInfo.groups, + era = null, year = null, month = null, date = null, weekDay = null, + hour = 0, hourOffset, min = 0, sec = 0, msec = 0, tzMinOffset = null, + pmHour = false; + // iterate the format groups to extract and set the date fields. + for ( var j = 0, jl = groups.length; j < jl; j++ ) { + var matchGroup = match[ j + 1 ]; + if ( matchGroup ) { + var current = groups[ j ], + clength = current.length, + matchInt = parseInt( matchGroup, 10 ); + switch ( current ) { + case 'dd': case 'd': + // Day of month. + date = matchInt; + // check that date is generally in valid range, also checking overflow below. + if ( outOfRange( date, 1, 31 ) ) return null; + break; + case 'MMM': + case 'MMMM': + month = getMonthIndex( cal, matchGroup, clength === 3 ); + if ( outOfRange( month, 0, 11 ) ) return null; + break; + case 'M': case 'MM': + // Month. + month = matchInt - 1; + if ( outOfRange( month, 0, 11 ) ) return null; + break; + case 'y': case 'yy': + case 'yyyy': + year = clength < 4 ? expandYear( cal, matchInt ) : matchInt; + if ( outOfRange( year, 0, 9999 ) ) return null; + break; + case 'h': case 'hh': + // Hours (12-hour clock). + hour = matchInt; + if ( hour === 12 ) hour = 0; + if ( outOfRange( hour, 0, 11 ) ) return null; + break; + case 'H': case 'HH': + // Hours (24-hour clock). + hour = matchInt; + if ( outOfRange( hour, 0, 23 ) ) return null; + break; + case 'm': case 'mm': + // Minutes. + min = matchInt; + if ( outOfRange( min, 0, 59 ) ) return null; + break; + case 's': case 'ss': + // Seconds. + sec = matchInt; + if ( outOfRange( sec, 0, 59 ) ) return null; + break; + case 'tt': case 't': + // AM/PM designator. + // see if it is standard, upper, or lower case PM. If not, ensure it is at least one of + // the AM tokens. If not, fail the parse for this format. + pmHour = cal.PM && ( matchGroup === cal.PM[0] || matchGroup === cal.PM[1] || matchGroup === cal.PM[2] ); + if ( !pmHour && ( !cal.AM || (matchGroup !== cal.AM[0] && matchGroup !== cal.AM[1] && matchGroup !== cal.AM[2]) ) ) return null; + break; + case 'f': + // Deciseconds. + case 'ff': + // Centiseconds. + case 'fff': + // Milliseconds. + msec = matchInt * Math.pow( 10, 3-clength ); + if ( outOfRange( msec, 0, 999 ) ) return null; + break; + case 'ddd': + // Day of week. + case 'dddd': + // Day of week. + weekDay = getDayIndex( cal, matchGroup, clength === 3 ); + if ( outOfRange( weekDay, 0, 6 ) ) return null; + break; + case 'zzz': + // Time zone offset in +/- hours:min. + var offsets = matchGroup.split( /:/ ); + if ( offsets.length !== 2 ) return null; + hourOffset = parseInt( offsets[ 0 ], 10 ); + if ( outOfRange( hourOffset, -12, 13 ) ) return null; + var minOffset = parseInt( offsets[ 1 ], 10 ); + if ( outOfRange( minOffset, 0, 59 ) ) return null; + tzMinOffset = (hourOffset * 60) + (startsWith( matchGroup, '-' ) ? -minOffset : minOffset); + break; + case 'z': case 'zz': + // Time zone offset in +/- hours. + hourOffset = matchInt; + if ( outOfRange( hourOffset, -12, 13 ) ) return null; + tzMinOffset = hourOffset * 60; + break; + case 'g': case 'gg': + var eraName = matchGroup; + if ( !eraName || !cal.eras ) return null; + eraName = trim( eraName.toLowerCase() ); + for ( var i = 0, l = cal.eras.length; i < l; i++ ) { + if ( eraName === cal.eras[ i ].name.toLowerCase() ) { + era = i; + break; + } + } + // could not find an era with that name + if ( era === null ) return null; + break; + } + } + } + var result = new Date(), defaultYear, convert = cal.convert; + defaultYear = convert ? convert.fromGregorian( result )[ 0 ] : result.getFullYear(); + if ( year === null ) { + year = defaultYear; + } + else if ( cal.eras ) { + // year must be shifted to normal gregorian year + // but not if year was not specified, its already normal gregorian + // per the main if clause above. + year += cal.eras[ (era || 0) ].offset; + } + // set default day and month to 1 and January, so if unspecified, these are the defaults + // instead of the current day/month. + if ( month === null ) { + month = 0; + } + if ( date === null ) { + date = 1; + } + // now have year, month, and date, but in the culture's calendar. + // convert to gregorian if necessary + if ( convert ) { + result = convert.toGregorian( year, month, date ); + // conversion failed, must be an invalid match + if ( result === null ) return null; + } + else { + // have to set year, month and date together to avoid overflow based on current date. + result.setFullYear( year, month, date ); + // check to see if date overflowed for specified month (only checked 1-31 above). + if ( result.getDate() !== date ) return null; + // invalid day of week. + if ( weekDay !== null && result.getDay() !== weekDay ) { + return null; + } + } + // if pm designator token was found make sure the hours fit the 24-hour clock. + if ( pmHour && hour < 12 ) { + hour += 12; + } + result.setHours( hour, min, sec, msec ); + if ( tzMinOffset !== null ) { + // adjust timezone to utc before applying local offset. + var adjustedMin = result.getMinutes() - ( tzMinOffset + result.getTimezoneOffset() ); + // Safari limits hours and minutes to the range of -127 to 127. We need to use setHours + // to ensure both these fields will not exceed this range. adjustedMin will range + // somewhere between -1440 and 1500, so we only need to split this into hours. + result.setHours( result.getHours() + parseInt( adjustedMin / 60, 10 ), adjustedMin % 60 ); + } + return result; +} + +function formatDate(value, format, culture) { + var cal = culture.calendar, + convert = cal.convert; + if ( !format || !format.length || format === 'i' ) { + var ret; + if ( culture && culture.name.length ) { + if ( convert ) { + // non-gregorian calendar, so we cannot use built-in toLocaleString() + ret = formatDate( value, cal.patterns.F, culture ); + } + else { + var eraDate = new Date( value.getTime() ), + era = getEra( value, cal.eras ); + eraDate.setFullYear( getEraYear( value, cal, era ) ); + ret = eraDate.toLocaleString(); + } + } + else { + ret = value.toString(); + } + return ret; + } + + var eras = cal.eras, + sortable = format === "s"; + format = expandFormat( cal, format ); + + // Start with an empty string + ret = []; + var hour, + zeros = ['0','00','000'], + foundDay, + checkedDay, + dayPartRegExp = /([^d]|^)(d|dd)([^d]|$)/g, + quoteCount = 0, + tokenRegExp = getTokenRegExp(), + converted; + + function padZeros(num, c) { + var r, s = num+''; + if ( c > 1 && s.length < c ) { + r = ( zeros[ c - 2 ] + s); + return r.substr( r.length - c, c ); + } + else { + r = s; + } + return r; + } + + function hasDay() { + if ( foundDay || checkedDay ) { + return foundDay; + } + foundDay = dayPartRegExp.test( format ); + checkedDay = true; + return foundDay; + } + + function getPart( date, part ) { + if ( converted ) { + return converted[ part ]; + } + switch ( part ) { + case 0: return date.getFullYear(); + case 1: return date.getMonth(); + case 2: return date.getDate(); + } + } + + if ( !sortable && convert ) { + converted = convert.fromGregorian( value ); + } + + for (;;) { + // Save the current index + var index = tokenRegExp.lastIndex, + // Look for the next pattern + ar = tokenRegExp.exec( format ); + + // Append the text before the pattern (or the end of the string if not found) + var preMatch = format.slice( index, ar ? ar.index : format.length ); + quoteCount += appendPreOrPostMatch( preMatch, ret ); + + if ( !ar ) { + break; + } + + // do not replace any matches that occur inside a string literal. + if ( quoteCount % 2 ) { + ret.push( ar[ 0 ] ); + continue; + } + + var current = ar[ 0 ], + clength = current.length; + + switch ( current ) { + case "ddd": + //Day of the week, as a three-letter abbreviation + case "dddd": + // Day of the week, using the full name + names = (clength === 3) ? cal.days.namesAbbr : cal.days.names; + ret.push( names[ value.getDay() ] ); + break; + case "d": + // Day of month, without leading zero for single-digit days + case "dd": + // Day of month, with leading zero for single-digit days + foundDay = true; + ret.push( padZeros( getPart( value, 2 ), clength ) ); + break; + case "MMM": + // Month, as a three-letter abbreviation + case "MMMM": + // Month, using the full name + var part = getPart( value, 1 ); + ret.push( (cal.monthsGenitive && hasDay()) + ? cal.monthsGenitive[ clength === 3 ? "namesAbbr" : "names" ][ part ] + : cal.months[ clength === 3 ? "namesAbbr" : "names" ][ part ] ); + break; + case "M": + // Month, as digits, with no leading zero for single-digit months + case "MM": + // Month, as digits, with leading zero for single-digit months + ret.push( padZeros( getPart( value, 1 ) + 1, clength ) ); + break; + case "y": + // Year, as two digits, but with no leading zero for years less than 10 + case "yy": + // Year, as two digits, with leading zero for years less than 10 + case "yyyy": + // Year represented by four full digits + part = converted ? converted[ 0 ] : getEraYear( value, cal, getEra( value, eras ), sortable ); + if ( clength < 4 ) { + part = part % 100; + } + ret.push( padZeros( part, clength ) ); + break; + case "h": + // Hours with no leading zero for single-digit hours, using 12-hour clock + case "hh": + // Hours with leading zero for single-digit hours, using 12-hour clock + hour = value.getHours() % 12; + if ( hour === 0 ) hour = 12; + ret.push( padZeros( hour, clength ) ); + break; + case "H": + // Hours with no leading zero for single-digit hours, using 24-hour clock + case "HH": + // Hours with leading zero for single-digit hours, using 24-hour clock + ret.push( padZeros( value.getHours(), clength ) ); + break; + case "m": + // Minutes with no leading zero for single-digit minutes + case "mm": + // Minutes with leading zero for single-digit minutes + ret.push( padZeros( value.getMinutes(), clength ) ); + break; + case "s": + // Seconds with no leading zero for single-digit seconds + case "ss": + // Seconds with leading zero for single-digit seconds + ret.push( padZeros(value .getSeconds(), clength ) ); + break; + case "t": + // One character am/pm indicator ("a" or "p") + case "tt": + // Multicharacter am/pm indicator + part = value.getHours() < 12 ? (cal.AM ? cal.AM[0] : " ") : (cal.PM ? cal.PM[0] : " "); + ret.push( clength === 1 ? part.charAt( 0 ) : part ); + break; + case "f": + // Deciseconds + case "ff": + // Centiseconds + case "fff": + // Milliseconds + ret.push( padZeros( value.getMilliseconds(), 3 ).substr( 0, clength ) ); + break; + case "z": + // Time zone offset, no leading zero + case "zz": + // Time zone offset with leading zero + hour = value.getTimezoneOffset() / 60; + ret.push( (hour <= 0 ? '+' : '-') + padZeros( Math.floor( Math.abs( hour ) ), clength ) ); + break; + case "zzz": + // Time zone offset with leading zero + hour = value.getTimezoneOffset() / 60; + ret.push( (hour <= 0 ? '+' : '-') + padZeros( Math.floor( Math.abs( hour ) ), 2 ) + + // Hard coded ":" separator, rather than using cal.TimeSeparator + // Repeated here for consistency, plus ":" was already assumed in date parsing. + ":" + padZeros( Math.abs( value.getTimezoneOffset() % 60 ), 2 ) ); + break; + case "g": + case "gg": + if ( cal.eras ) { + ret.push( cal.eras[ getEra(value, eras) ].name ); + } + break; + case "/": + ret.push( cal["/"] ); + break; + default: + throw "Invalid date format pattern '" + current + "'."; + break; + } + } + return ret.join( '' ); +} + +// EXPORTS + +window.Globalization = Globalization; + +//jQuery.findClosestCulture = Globalization.findClosestCulture; +//jQuery.culture = Globalization.culture; +//jQuery.cultures = Globalization.cultures +//jQuery.preferCulture = Globalization.preferCulture +//jQuery.localize = Globalization.localize +//jQuery.format = Globalization.format +//jQuery.parseInt = Globalization.parseInt +//jQuery.parseFloat = Globalization.parseFloat +//jQuery.parseDate = Globalization.parseDate + +})(); diff --git a/ui/jquery.ui.spinner.js b/ui/jquery.ui.spinner.js index c0167fd24..4fc77389b 100644 --- a/ui/jquery.ui.spinner.js +++ b/ui/jquery.ui.spinner.js @@ -17,60 +17,29 @@ 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, + numberformat: "n", padding: 0, page: 5, - precision: 0, - radix: 10, - radixPoint: '.', spinnerClass: null, step: null, - value: 0, - width: false + value: 0 }, _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; @@ -101,7 +70,10 @@ $.widget('ui.spinner', { this.element .bind('keydown'+namespace, function(event) { - return self._start(event) ? self._keydown(event) : false; + if (self._start(event)) { + return self._keydown(event); + } + return true; }) .bind('keyup'+namespace, function(event) { if (self.spinning) { @@ -114,18 +86,13 @@ $.widget('ui.spinner', { self.focused = true; }) .bind('blur'+namespace, function(event) { - self._value(self.element.val()); + 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(); @@ -133,6 +100,7 @@ $.widget('ui.spinner', { // button bindings this.buttons = uiSpinner.find('.ui-spinner-button') + .attr("tabIndex", -1) .bind('mousedown', function(event) { if (self._start(event) === false) { return false; @@ -198,7 +166,7 @@ $.widget('ui.spinner', { this.counter = 1; } - var newVal = this._value() + step * (this.options.incremental && this.counter > 100 + var newVal = this.value() + step * (this.options.incremental && this.counter > 100 ? this.counter > 200 ? 100 : 10 @@ -206,7 +174,7 @@ $.widget('ui.spinner', { // cancelable if (this._trigger('spin', event, { value: newVal }) !== false) { - this._value(newVal); + this.value(newVal); this.counter++; } }, @@ -227,10 +195,10 @@ $.widget('ui.spinner', { i = i || 100; if (this.timer) { - window.clearInterval(this.timer); + window.clearTimeout(this.timer); } - this.timer = window.setInterval(function() { + this.timer = window.setTimeout(function() { self._repeat(self.options.incremental && self.counter > 20 ? 20 : i, steps, event); }, i); @@ -241,40 +209,32 @@ $.widget('ui.spinner', { 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.UP: + this._repeat(null, event.shiftKey ? o.page : 1, event); + return false; + case KEYS.DOWN: + this._repeat(null, event.shiftKey ? -o.page : -1, event); + return false; + case KEYS.PAGE_UP: + this._repeat(null, o.page, event); + return false; + case KEYS.PAGE_DOWN: + this._repeat(null, -o.page, event); + return false; - 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: + case KEYS.HOME: + case KEYS.END: + if (event.shiftKey) { return true; + } + this.value(this['_' + (event.keyCode == KEYS.HOME ? 'min' : 'max')]()); + return false; - 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; - }; + case KEYS.ENTER: + this.value(this.element.val()); } - return false; + return true; }, _mousewheel: function() { var self = this; @@ -298,7 +258,7 @@ $.widget('ui.spinner', { }); } }, - _value: function(newVal) { + value: function(newVal) { if (!arguments.length) { return this._parse(this.element.val()); } @@ -349,10 +309,6 @@ $.widget('ui.spinner', { } this._aria(); break; - case 'width': - this.element.width(value); - break; - case 'precision': case 'value': this._format(this._parse(this.options.value)); break; @@ -364,59 +320,23 @@ $.widget('ui.spinner', { .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) { + var input = val; if (typeof val == 'string') { - if (this.options.groupSeparator) { - val = val.replace(new RegExp('\\'+this.options.groupSeparator,'g'), ''); + // special case for currency formatting until Globalization handles currencies + if (this.options.numberformat == "C" && window.Globalization) { + // parseFloat should accept number format, including currency + var culture = Globalization.culture || Globalization.cultures['default']; + val = val.replace(culture.numberFormat.currency.symbol, ""); } - 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; + val = window.Globalization ? Globalization.parseFloat(val) : +val; } + console.log("input", input, "parsed", val) 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); + this.element.val( window.Globalization ? Globalization.format(num, this.options.numberformat) : num ); }, _getOption: function(key, defaultValue) { return this._parse(this.options[key] !== null @@ -476,12 +396,6 @@ $.widget('ui.spinner', { .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; -- cgit v1.2.3 From eebf8c31fff4653cfcbb273f7da5fa7177217dca Mon Sep 17 00:00:00 2001 From: jzaefferer Date: Sat, 23 Oct 2010 02:06:25 +0200 Subject: Using button widget for the spinner buttons. Can still use some improvements, dropping custom theme stuff in favor of button styles. --- ui/jquery.ui.spinner.js | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/ui/jquery.ui.spinner.js b/ui/jquery.ui.spinner.js index 4fc77389b..f4227b8f6 100644 --- a/ui/jquery.ui.spinner.js +++ b/ui/jquery.ui.spinner.js @@ -101,16 +101,18 @@ $.widget('ui.spinner', { // button bindings this.buttons = uiSpinner.find('.ui-spinner-button') .attr("tabIndex", -1) + .button() + .first() + .removeClass("ui-corner-all") + .end() + .last() + .removeClass("ui-corner-all") + .end() .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) { @@ -120,19 +122,22 @@ $.widget('ui.spinner', { self._stop(event); self._change(event); } - $(this).removeClass(active); }) - .hover(function() { - if (!self.options.disabled) { - $(this).addClass(hover); + .bind("mouseenter", function() { + // button will add ui-state-active if mouse was down while mouseleave and kept down + if ($(this).hasClass("ui-state-active")) { + if (self._start(event) === false) { + return false; + } + self._repeat(null, $(this).hasClass('ui-spinner-up') ? 1 : -1, event); } - }, function(event) { - $(this).removeClass(active + ' ' + hover); + }) + .bind("mouseleave", function() { if (self.timer && self.spinning) { self._stop(event); self._change(event); } - }); + }) self.uiSpinner = uiSpinner; }, @@ -143,9 +148,9 @@ $.widget('ui.spinner', { '">'; }, _buttonHtml: function() { - return '' + - ''; }, _start: function(event) { -- cgit v1.2.3 From 02d8f914b68f73537dcaca272087b544b366bb2b Mon Sep 17 00:00:00 2001 From: jzaefferer Date: Sun, 24 Oct 2010 15:52:26 +0200 Subject: Spinner: Some code and test cleanup --- tests/unit/spinner/spinner.html | 1 + tests/unit/spinner/spinner_defaults.js | 11 ++--------- tests/unit/spinner/spinner_options.js | 11 ----------- ui/jquery.ui.spinner.js | 3 +-- 4 files changed, 4 insertions(+), 22 deletions(-) diff --git a/tests/unit/spinner/spinner.html b/tests/unit/spinner/spinner.html index df91c926e..954e82ea5 100644 --- a/tests/unit/spinner/spinner.html +++ b/tests/unit/spinner/spinner.html @@ -9,6 +9,7 @@ + diff --git a/tests/unit/spinner/spinner_defaults.js b/tests/unit/spinner/spinner_defaults.js index 94d3d67ab..7eb3f0a17 100644 --- a/tests/unit/spinner/spinner_defaults.js +++ b/tests/unit/spinner/spinner_defaults.js @@ -3,24 +3,17 @@ */ var spinner_defaults = { - buttons: 'show', - currency: false, dir: 'ltr', disabled: false, - groupSeparator: '', incremental: true, max: null, min: null, mouseWheel: true, - padding: 0, + numberformat: "n", page: 5, - precision: 0, - radix: 10, - radixPoint: '.', spinnerClass: null, step: null, - value: 0, - width: false + value: 0 }; commonWidgetTests('spinner', { defaults: spinner_defaults }); diff --git a/tests/unit/spinner/spinner_options.js b/tests/unit/spinner/spinner_options.js index b568d3cdd..c3bcac820 100644 --- a/tests/unit/spinner/spinner_options.js +++ b/tests/unit/spinner/spinner_options.js @@ -468,15 +468,4 @@ test("value", function() { equals(el.val(), 1000, "value option changed and set as current value"); }); -test("width", function() { - expect(2); - - el = $('#spin').spinner({ width: 1000 }); - equals(el.width(), 1000, "spinner width initialized"); - - el.spinner('option', 'width', 500); - equals(el.width(), 500, "spinner width changed"); - -}); - })(jQuery); diff --git a/ui/jquery.ui.spinner.js b/ui/jquery.ui.spinner.js index f4227b8f6..02bb41ac0 100644 --- a/ui/jquery.ui.spinner.js +++ b/ui/jquery.ui.spinner.js @@ -27,7 +27,6 @@ $.widget('ui.spinner', { min: null, mouseWheel: true, numberformat: "n", - padding: 0, page: 5, spinnerClass: null, step: null, @@ -35,7 +34,7 @@ $.widget('ui.spinner', { }, _create: function() { - this.value(this._parse(this.element.val() || this.options.value)); + this.value(this.element.val() || this.options.value); this._draw(); this._mousewheel(); this._aria(); -- cgit v1.2.3 From 1be2a79076e4da314ca6317082fbd8c03e7f9d63 Mon Sep 17 00:00:00 2001 From: jzaefferer Date: Sun, 24 Oct 2010 15:57:58 +0200 Subject: Spinner: More cleanup --- tests/unit/spinner/spinner_defaults.js | 1 - tests/unit/spinner/spinner_options.js | 303 +-------------------------------- ui/jquery.ui.spinner.js | 35 ++-- 3 files changed, 13 insertions(+), 326 deletions(-) diff --git a/tests/unit/spinner/spinner_defaults.js b/tests/unit/spinner/spinner_defaults.js index 7eb3f0a17..a85891970 100644 --- a/tests/unit/spinner/spinner_defaults.js +++ b/tests/unit/spinner/spinner_defaults.js @@ -11,7 +11,6 @@ var spinner_defaults = { mouseWheel: true, numberformat: "n", page: 5, - spinnerClass: null, step: null, value: 0 }; diff --git a/tests/unit/spinner/spinner_options.js b/tests/unit/spinner/spinner_options.js index c3bcac820..4e280ef6a 100644 --- a/tests/unit/spinner/spinner_options.js +++ b/tests/unit/spinner/spinner_options.js @@ -5,197 +5,8 @@ module("spinner: options"); -test("buttons - show (default)", function() { - expect(4); - - el = $('#spin'); - el.spinner({ - buttons: 'show' - }); - - ok(upButton().is(':visible'), "show - before hover: up button visible"); - ok(downButton().is(':visible'), "show - before hover: down button visible"); - - el.trigger('mouseover'); - - ok(upButton().is(':visible'), "show - after hover: up button visible"); - ok(downButton().is(':visible'), "show - after hover: down button visible"); - - el.trigger('mouseout'); -}); - -test("buttons - hide", function() { - expect(4); - - el = $('#spin'); - el.spinner({ - buttons: 'hide' - }); - - ok(upButton().is(':hidden'), "hide - before hover: up button hidden"); - ok(downButton().is(':hidden'), "hide - before hover: down button hidden"); - - el.trigger('mouseover'); - - ok(upButton().is(':hidden'), "hide - after hover: up button hidden"); - ok(downButton().is(':hidden'), "hide - after hover: down button hidden"); - - el.trigger('mouseout'); -}); - -test("buttons - auto (hover)", function() { - expect(4); - - el = $('#spin'); - el.spinner({ - buttons: 'auto' - }); - - ok(upButton().is(':hidden'), "auto - before hover: up button hidden"); - ok(downButton().is(':hidden'), "auto - before hover: down button hidden"); - - el.trigger('mouseover'); - - ok(upButton().is(':visible'), "auto - after hover: up button visible"); - ok(downButton().is(':visible'), "auto - after hover: down button visible"); - - el.trigger('mouseout'); -}); - -test("buttons - auto (focus)", function() { - expect(4); - - el = $('#spin'); - el.spinner({ - buttons: 'auto' - }); - - ok(upButton().is(':hidden'), "auto - before focus: up button hidden"); - ok(downButton().is(':hidden'), "auto - before focus: down button hidden"); - - el.focus(); - - ok(upButton().is(':visible'), "auto - after focus: up button visible"); - ok(downButton().is(':visible'), "auto - after focus: down button visible"); - - el.trigger('mouseout'); -}); - -test("currency - single character currency symbol", function() { - expect(5); - - el = $("#spin"); - - options = { - currency:"$", - max:120, - min:-50, - step:0.3 - }; - - el.spinner(options); - - equals(el.val(), "$0.00", "start number"); - - simulateKeyDownUp(el, $.ui.keyCode.UP); - - equals(el.val(), "$0.30", "stepping 0.30"); - - simulateKeyDownUp(el, $.ui.keyCode.END); - - equals(el.val(), "$120.00", "End key to max"); - - simulateKeyDownUp(el, $.ui.keyCode.HOME); - - equals(el.val(), "-$50.00", "Home key to min"); - - for ( var i = 1 ; i<=120 ; i++ ) { - simulateKeyDownUp(el, $.ui.keyCode.UP); - } - - equals(el.val(), "-$14.00", "keydown 120 times"); -}); - -test("currency - combined character currency symbol", function() { - expect(2); - - el = $('#spin'); - - options = { - currency: 'HK$', - step: 1500.50, - value: 1000 - } - - el.spinner(options); - - equals(el.val(), "HK$1,000.00", "Hong Kong Dollar"); - - simulateKeyDownUp(el, $.ui.keyCode.UP); - - equals(el.val(), "HK$2,500.50", "Hong Kong Dollar step-up once"); -}); - -test("currency - space as group separator", function() { - expect(2); - - el = $('#spin'); - - options = { - currency: '$', - groupSeparator: ' ', - radixPoint: '.', - step: 1500.50, - value: 1000 - } - - el.spinner(options); - - equals(el.val(), "$1 000.00", "Australian Dollar"); - - simulateKeyDownUp(el, $.ui.keyCode.UP); - - equals(el.val(), "$2 500.50", "Australian Dollar step-up once"); -}); - -test("currency - apos as group separator", function() { - expect(2); - - el = $('#spin'); - options = { - currency: 'Fr ', - groupSeparator: "'", - radixPoint: '.', - step: 1500.50, - value: 1000 - } - el.spinner(options); - - equals(el.val(), "Fr 1'000.00", "Swiss Franc"); - - simulateKeyDownUp(el, $.ui.keyCode.UP); - - equals(el.val(), "Fr 2'500.50", "Swiss Franc step-up once"); -}); - -test("currency - period as group separator and comma as radixPoint", function() { - expect(2); - - el = $('#spin'); - options = { - currency: 'RUB', - groupSeparator: ".", - radixPoint: ',', - step: 1.5, - value: 1000 - } - el.spinner(options); - - equals(el.val(), "RUB1.000,00", "Russian Ruble"); - - simulateKeyDownUp(el, $.ui.keyCode.UP); - - equals(el.val(), "RUB1.001,50", "Russian Ruble step-up once"); +test("numberformat", function() { + ok( false, "tests for numberformat!"); }); test("dir - left-to-right (default)", function() { @@ -220,42 +31,6 @@ test("dir - right-to-left", function() { ok(wrapper().hasClass('ui-spinner-rtl'), 'container has correct text direction class setting'); }); -test("groupSeparator - comma separator (default)", function() { - expect(1); - - el = $('#spin'); - options = { - groupSeparator: ',', - value: 1000000 - }; - el.spinner(options); - equals(el.val(), "1,000,000", "value contains 2 commas separated by 3 digits"); -}); - -test("groupSeparator - space separator", function() { - expect(1); - - el = $('#spin'); - options = { - groupSeparator: ' ', - value: 1000000 - }; - el.spinner(options); - equals(el.val(), "1 000 000", "value contains 2 spaces separated by 3 digits"); -}); - -test("groupSeparator - apos separator", function() { - expect(1); - - el = $('#spin'); - options = { - groupSeparator: "'", - value: 1000000 - }; - el.spinner(options); - equals(el.val(), "1'000'000", "value contains apos separated by 3 digits"); -}); - test("incremental - false (default)", function() { expect(2); @@ -326,20 +101,6 @@ test("mouseWheel", function() { ok(false, 'missing test - untested code is broken code'); }); -test("padding", function() { - expect(3); - - el = $('#spin').spinner({ padding: 5, value: 10 }); - - equals(el.val(), '00010', 'padded output'); - - el.spinner('option', 'padding', 4); - equals(el.val(), '0010', 'padded output'); - - el.spinner('value', 15); - equals(el.val(), '0015', 'padded output'); -}); - test("page", function() { expect(3); @@ -358,66 +119,6 @@ test("page", function() { equals(el.val(), "50", "PAGE_UP 11 times on spinner"); }); -test("precision", function() { - expect(3); - - el = $("#spin").spinner({ precision: 4, value: 1.23456789 }); - - equals(el.val(), '1.2346', "4 decimal places"); - - el.spinner('option', 'precision', 2); - - equals(el.val(), '1.23', "2 decimal places"); - - el.spinner('option', 'precision', 6); - - equals(el.val(), '1.234568', "6 decimal places"); -}); - -test("radix", function() { - expect(2); - - el = $("#spin").spinner({ - radix: 16, - value: 10 - }); - - equals(el.val(), 'a', 'start value'); - - simulateKeyDownUp(el, $.ui.keyCode.UP); - - equals(el.val(), 'b', 'key UP on spinner') -}); - -test("radixPoint", function() { - el = $("#spin").spinner({ - radixPoint: ',', - value: 20.00, - precision: 2 - }); - - equals(el.val(), '20,00', 'comma radix point'); - - el.spinner('stepUp'); - - equals(el.val(), '21,00', 'step up one'); - - el.spinner('pageDown', 10); - - equals(el.val(), '-29,00', 'page down into negative space'); -}); - -test("spinnerClass", function() { - expect(3); - - el = $('#spin').spinner({spinnerClass: 'my-spinner-class'}); - ok(wrapper().hasClass('my-spinner-class'), 'spinner container has custom class'); - - el.spinner('option', 'spinnerClass', 'new-spinner-class'); - ok(!wrapper().hasClass('my-spinner-class'), 'spinner no longer has old custom class'); - ok(wrapper().hasClass('new-spinner-class'), 'spinner now has new custom class'); -}); - test("step", function() { expect(7); diff --git a/ui/jquery.ui.spinner.js b/ui/jquery.ui.spinner.js index 02bb41ac0..a9238a738 100644 --- a/ui/jquery.ui.spinner.js +++ b/ui/jquery.ui.spinner.js @@ -28,7 +28,6 @@ $.widget('ui.spinner', { mouseWheel: true, numberformat: "n", page: 5, - spinnerClass: null, step: null, value: 0 }, @@ -141,10 +140,7 @@ $.widget('ui.spinner', { self.uiSpinner = uiSpinner; }, _uiSpinnerHtml: function() { - return '
    '; + return '
    '; }, _buttonHtml: function() { return ' + diff --git a/tests/unit/spinner/spinner_defaults.js b/tests/unit/spinner/spinner_defaults.js index a85891970..aa88fd649 100644 --- a/tests/unit/spinner/spinner_defaults.js +++ b/tests/unit/spinner/spinner_defaults.js @@ -12,7 +12,7 @@ var spinner_defaults = { numberformat: "n", page: 5, step: null, - value: 0 + value: null }; commonWidgetTests('spinner', { defaults: spinner_defaults }); diff --git a/tests/unit/spinner/spinner_options.js b/tests/unit/spinner/spinner_options.js index 4e280ef6a..48fea3203 100644 --- a/tests/unit/spinner/spinner_options.js +++ b/tests/unit/spinner/spinner_options.js @@ -157,15 +157,30 @@ test("step", function() { }); -test("value", function() { - expect(2); - - el = $('#spin').spinner({ value: 100 }); - +test("value, default, specified in markup", function() { + var el = $('#spin2').spinner(); + equals(el.val(), 2, "starting value"); +}); + +test("value, default, nothing specified", function() { + var el = $('#spin').spinner(); + equals(el.val(), 0, "starting value"); +}); + +test("value, override", function() { + var el = $('#spin').spinner({ value: 100 }); equals(el.val(), 100, "starting value"); - +}); + +test("value, override markup", function() { + var el = $('#spin2').spinner({ value: 100 }); + equals(el.val(), 100, "starting value"); +}); + +test("value, override later", function() { + var el = $('#spin').spinner(); + equals(el.val(), 0, "starting value"); el.spinner('option', 'value', 1000); - equals(el.val(), 1000, "value option changed and set as current value"); }); diff --git a/ui/jquery.ui.spinner.js b/ui/jquery.ui.spinner.js index a9238a738..70dd7adac 100644 --- a/ui/jquery.ui.spinner.js +++ b/ui/jquery.ui.spinner.js @@ -29,11 +29,11 @@ $.widget('ui.spinner', { numberformat: "n", page: 5, step: null, - value: 0 + value: null }, _create: function() { - this.value(this.element.val() || this.options.value); + this.value(this.options.value !== null ? this.options.value : this.element.val()); this._draw(); this._mousewheel(); this._aria(); -- cgit v1.2.3 From 44ca830ee850a6771221b799ff1e0fbafc9e7a13 Mon Sep 17 00:00:00 2001 From: jzaefferer Date: Mon, 25 Oct 2010 18:06:26 +0200 Subject: Slider: More codecleanup. Inlining defaults for min/max, removing page (use 10*step instead), removing mouseWheel (just use when available) --- ui/jquery.ui.spinner.js | 109 ++++++++++++++++-------------------------------- 1 file changed, 37 insertions(+), 72 deletions(-) diff --git a/ui/jquery.ui.spinner.js b/ui/jquery.ui.spinner.js index 70dd7adac..022e7f7da 100644 --- a/ui/jquery.ui.spinner.js +++ b/ui/jquery.ui.spinner.js @@ -17,18 +17,16 @@ var hover = 'ui-state-hover', active = 'ui-state-active', namespace = '.spinner', - uiSpinnerClasses = 'ui-spinner ui-state-default ui-widget ui-widget-content ui-corner-all '; + pageModifier = 10; $.widget('ui.spinner', { options: { dir: 'ltr', incremental: true, - max: null, - min: null, - mouseWheel: true, + max: Number.MAX_VALUE, + min: -Number.MAX_VALUE, numberformat: "n", - page: 5, - step: null, + step: 1, value: null }, @@ -114,7 +112,7 @@ $.widget('ui.spinner', { }) .bind('mouseup', function(event) { if (self.counter == 1) { - self._spin(($(this).hasClass('ui-spinner-up') ? 1 : -1) * self._step(), event); + self._spin(($(this).hasClass('ui-spinner-up') ? 1 : -1) * self.options.step, event); } if (self.spinning) { self._stop(event); @@ -140,7 +138,7 @@ $.widget('ui.spinner', { self.uiSpinner = uiSpinner; }, _uiSpinnerHtml: function() { - return '
    '; + return '
    '; }, _buttonHtml: function() { return '
    '; + return '
    '; }, _buttonHtml: function() { - return '
    ' + - ''; + return '' + + ''; }, _start: function(event) { if (!this.spinning && this._trigger('start', event, { value: this.value()}) !== false) { -- cgit v1.2.3 From 8da1a75177e9ee50beec6e47fed344f9579dbb97 Mon Sep 17 00:00:00 2001 From: jzaefferer Date: Tue, 26 Oct 2010 10:36:46 +0200 Subject: Spiner: Tune mousewheel spinning --- ui/jquery.ui.spinner.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/jquery.ui.spinner.js b/ui/jquery.ui.spinner.js index 4cfbe62e1..c13fe9407 100644 --- a/ui/jquery.ui.spinner.js +++ b/ui/jquery.ui.spinner.js @@ -241,7 +241,7 @@ $.widget('ui.spinner', { self._stop(event); self._change(event); } - }, 400); + }, 50); event.preventDefault(); }); }, -- cgit v1.2.3 From 0a1da016ffc5081ad1124aa74af778053d58ecb2 Mon Sep 17 00:00:00 2001 From: jzaefferer Date: Tue, 26 Oct 2010 10:53:03 +0200 Subject: Slider: Update to latest mousewheel plugin --- demos/spinner/currency.html | 2 +- demos/spinner/decimal.html | 2 +- demos/spinner/default.html | 2 +- demos/spinner/latlong.html | 2 +- demos/spinner/rtl.html | 2 +- external/jquery.mousewheel-3.0.2.js | 60 ---------------------------- external/jquery.mousewheel-3.0.4.js | 78 +++++++++++++++++++++++++++++++++++++ tests/unit/spinner/spinner.html | 4 +- 8 files changed, 85 insertions(+), 67 deletions(-) delete mode 100644 external/jquery.mousewheel-3.0.2.js create mode 100644 external/jquery.mousewheel-3.0.4.js diff --git a/demos/spinner/currency.html b/demos/spinner/currency.html index 418dca228..0909f4ef5 100644 --- a/demos/spinner/currency.html +++ b/demos/spinner/currency.html @@ -5,7 +5,7 @@ jQuery UI Spinner - Default functionality - + diff --git a/demos/spinner/decimal.html b/demos/spinner/decimal.html index 82d0fcc20..c47251552 100644 --- a/demos/spinner/decimal.html +++ b/demos/spinner/decimal.html @@ -5,7 +5,7 @@ jQuery UI Spinner - decimal - + diff --git a/demos/spinner/default.html b/demos/spinner/default.html index d90b73f6d..b0f62308d 100644 --- a/demos/spinner/default.html +++ b/demos/spinner/default.html @@ -5,7 +5,7 @@ jQuery UI Spinner - Default functionality - + diff --git a/demos/spinner/latlong.html b/demos/spinner/latlong.html index 6b955ba47..7c0c9b21a 100644 --- a/demos/spinner/latlong.html +++ b/demos/spinner/latlong.html @@ -6,7 +6,7 @@ - + diff --git a/demos/spinner/rtl.html b/demos/spinner/rtl.html index 52d7b1048..93a2022ff 100644 --- a/demos/spinner/rtl.html +++ b/demos/spinner/rtl.html @@ -5,7 +5,7 @@ jQuery UI Spinner - Default functionality - + diff --git a/external/jquery.mousewheel-3.0.2.js b/external/jquery.mousewheel-3.0.2.js deleted file mode 100644 index 507ab005e..000000000 --- a/external/jquery.mousewheel-3.0.2.js +++ /dev/null @@ -1,60 +0,0 @@ -/*! 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/external/jquery.mousewheel-3.0.4.js b/external/jquery.mousewheel-3.0.4.js new file mode 100644 index 000000000..dbf8f4b0f --- /dev/null +++ b/external/jquery.mousewheel-3.0.4.js @@ -0,0 +1,78 @@ +/*! Copyright (c) 2010 Brandon Aaron (http://brandonaaron.net) + * Licensed under the MIT License (LICENSE.txt). + * + * 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. + * Thanks to: Seamus Leahy for adding deltaX and deltaY + * + * Version: 3.0.4 + * + * 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 orgEvent = event || window.event, args = [].slice.call( arguments, 1 ), delta = 0, returnValue = true, deltaX = 0, deltaY = 0; + event = $.event.fix(orgEvent); + event.type = "mousewheel"; + + // Old school scrollwheel delta + if ( event.wheelDelta ) { delta = event.wheelDelta/120; } + if ( event.detail ) { delta = -event.detail/3; } + + // New school multidimensional scroll (touchpads) deltas + deltaY = delta; + + // Gecko + if ( orgEvent.axis !== undefined && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) { + deltaY = 0; + deltaX = -1*delta; + } + + // Webkit + if ( orgEvent.wheelDeltaY !== undefined ) { deltaY = orgEvent.wheelDeltaY/120; } + if ( orgEvent.wheelDeltaX !== undefined ) { deltaX = -1*orgEvent.wheelDeltaX/120; } + + // Add event and delta to the front of the arguments + args.unshift(event, delta, deltaX, deltaY); + + return $.event.handle.apply(this, args); +} + +})(jQuery); \ No newline at end of file diff --git a/tests/unit/spinner/spinner.html b/tests/unit/spinner/spinner.html index a9902a251..5868a4590 100644 --- a/tests/unit/spinner/spinner.html +++ b/tests/unit/spinner/spinner.html @@ -6,7 +6,7 @@ - + @@ -33,7 +33,7 @@
    - +
    -- cgit v1.2.3 From e442dd352895b5e7c605d6baeb825a954db4b6af Mon Sep 17 00:00:00 2001 From: jzaefferer Date: Tue, 26 Oct 2010 10:54:28 +0200 Subject: Slider: Simplifying mousewheel binding, remove Opera workaround --- ui/jquery.ui.spinner.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ui/jquery.ui.spinner.js b/ui/jquery.ui.spinner.js index c13fe9407..2c2da22d1 100644 --- a/ui/jquery.ui.spinner.js +++ b/ui/jquery.ui.spinner.js @@ -224,11 +224,8 @@ $.widget('ui.spinner', { return true; }, _mousewheel: function() { - if (!$.fn.mousewheel) - return; var self = this; - this.element.mousewheel(function(event, delta) { - delta = ($.browser.opera ? -delta / Math.abs(delta) : delta); + this.element.bind("mousewheel", function(event, delta) { if (!self._start(event)) { return false; } -- cgit v1.2.3 From 2df094527ec12fe9ebec938c27661eda3e999e63 Mon Sep 17 00:00:00 2001 From: jzaefferer Date: Tue, 26 Oct 2010 12:14:45 +0200 Subject: Spinner: Changed default for numberformat to null. Fixed disabled option to not prevent changing value via step and page methods. OVerhauled testsuite. --- demos/spinner/decimal.html | 3 +- tests/unit/spinner/spinner.html | 1 + tests/unit/spinner/spinner_core.js | 304 +++++++-------------------------- tests/unit/spinner/spinner_defaults.js | 11 +- tests/unit/spinner/spinner_events.js | 16 +- tests/unit/spinner/spinner_methods.js | 91 ++++------ tests/unit/spinner/spinner_options.js | 119 +++++-------- ui/jquery.ui.spinner.js | 80 ++++++--- 8 files changed, 200 insertions(+), 425 deletions(-) diff --git a/demos/spinner/decimal.html b/demos/spinner/decimal.html index c47251552..ae45da1e7 100644 --- a/demos/spinner/decimal.html +++ b/demos/spinner/decimal.html @@ -17,7 +17,8 @@ + diff --git a/tests/unit/spinner/spinner_core.js b/tests/unit/spinner/spinner_core.js index 2996ad128..5a29b411a 100644 --- a/tests/unit/spinner/spinner_core.js +++ b/tests/unit/spinner/spinner_core.js @@ -2,332 +2,151 @@ * spinner_core.js */ -var el, - options, - simulateKeyDownUp = function(el, kCode, shift) { - el.simulate("keydown",{keyCode:kCode, shiftKey: shift || false }) - .simulate("keyup",{keyCode:kCode, shiftKey: shift || false }); - }, - wrapper = function() { - return el.closest('.ui-spinner'); - }, - upButton = function() { - return wrapper().find('.ui-spinner-up'); - }, - downButton = function() { - return wrapper().find('.ui-spinner-down'); - }, - box = function() { - return $('.ui-spinner-input', wrapper()); - }; +var simulateKeyDownUp = function(el, kCode, shift) { + el.simulate("keydown",{keyCode:kCode, shiftKey: shift || false }) + .simulate("keyup",{keyCode:kCode, shiftKey: shift || false }); +}; (function($) { // Spinner Tests module("spinner: core"); -test("init", function() { - expect(3); - - $("").appendTo('body').spinner().remove(); - ok(true, '.spinner() called on element'); - - $('').spinner().remove(); - ok(true, '.spinner() called on disconnected element'); - - el = $('').spinner(); - ok(el.hasClass('ui-spinner-input'), 'input gets ui-spinner-input class on init'); - -}); - test("destroy", function() { - expect(3); - - $("").appendTo('body').spinner().spinner("destroy").remove(); - ok(true, '.spinner("destroy") called on element'); - - $('').spinner().spinner("destroy").remove(); - ok(true, '.spinner().spinner("destroy") called on disconnected element'); - - el = $('').spinner().spinner('destroy'); - ok(!el.hasClass('ui-spinner-input'), 'ui-spinner-input class removed on destroy'); -}); - -test("re-attach", function() { - expect(2); - - el = $("").spinner().spinner("destroy").spinner(); - ok(true, '.spinner().spinner("destroy").spinner() called on element'); - - el = $('').spinner().spinner("destroy").spinner().remove(); - ok(true, '.spinner().spinner("destroy").spinner() called on disconnected element'); - + var beforeHtml = $("#spin").parent().html(); + var afterHtml = $("#spin").spinner().spinner("destroy").parent().html(); + // Opera 9 outputs role="" instead of removing the attribute like everyone else + /* + if ($.browser.opera) { + afterHtml = afterHtml.replace(/ role=""/g, ""); + } + */ + equal( afterHtml, beforeHtml, "before/after html should be the same" ); }); test("keydown UP on input, increases value not greater than max", function() { - expect(3); - - el = $("#spin"); - options = { + var el = $("#spin").spinner({ max:100, value:50, step:10 - } - el.spinner(options); + }); simulateKeyDownUp(el, $.ui.keyCode.UP); - equals(el.val(), 60); for (i = 0; i<11; i++) { simulateKeyDownUp(el, $.ui.keyCode.UP); } - equals(el.val(), 100); el.val(50); - simulateKeyDownUp(el, $.ui.keyCode.UP); - equals(el.val(), 60); }); test("keydown DOWN on input, decreases value not less than min", function() { - expect(3); - - el = $("#spin"); - options = { + var el = $("#spin").spinner({ min:-100, value:50, step:10 - } - el.spinner(options); + }); simulateKeyDownUp(el, $.ui.keyCode.DOWN); - equals(el.val(), 40); for (i = 0; i<21; i++) { simulateKeyDownUp(el, $.ui.keyCode.DOWN); } - equals(el.val(), -100); el.val(50); - simulateKeyDownUp(el, $.ui.keyCode.DOWN); - equals(el.val(), 40); - }); test("keydown PGUP on input, increases value not greater than max", function() { - expect(3); + var el = $("#spin").spinner({ + max: 500, + value: 0, + step: 10 + }); - el = $("#spin"); - options = { - max:100, - value:0, - step:10 - } - el.spinner(options); - simulateKeyDownUp(el, $.ui.keyCode.PAGE_UP); - - equals(el.val(), 50); + equal(el.val(), 100); for (i = 0; i<5; i++) { simulateKeyDownUp(el, $.ui.keyCode.PAGE_UP); } + equal(el.val(), 500); - equals(el.val(), 100); - el.val(0); - simulateKeyDownUp(el, $.ui.keyCode.PAGE_UP); - - equals(el.val(), 50); + equals(el.val(), 100); }); test("keydown PGDN on input, decreases value not less than min", function() { - expect(3); - - el = $("#spin"); - options = { - min:-100, + var el = $("#spin").spinner({ + min:-500, value:0, step:10 - } - el.spinner(options); + }); simulateKeyDownUp(el, $.ui.keyCode.PAGE_DOWN); - - equals(el.val(), -50); + equals(el.val(), -100); for (i = 0; i<5; i++) { simulateKeyDownUp(el, $.ui.keyCode.PAGE_DOWN); } - - equals(el.val(), -100); + equals(el.val(), -500); el.val(0); - simulateKeyDownUp(el, $.ui.keyCode.PAGE_DOWN); - - equals(el.val(), -50); -}); - -test("hold SHIFT and keydown UP, increments value but no greater than max", function() { - expect(2); - - el = $("#spin"); - options = { - max:100, - value:0, - step:10 - } - el.spinner(options); - - simulateKeyDownUp(el, $.ui.keyCode.UP, true); - - equals(el.val(), 50); - - for (i = 0; i<5; i++) { - simulateKeyDownUp(el, $.ui.keyCode.UP, true); - } - - equals(el.val(), 100); -}); - -test("hold SHIFT and keydown DOWN, decreases value but no less than min", function() { - expect(2); - - el = $("#spin"); - options = { - min:-100, - value:0, - step:10 - } - el.spinner(options); - - simulateKeyDownUp(el, $.ui.keyCode.DOWN, true); - - equals(el.val(), -50); - - for (i = 0; i<5; i++) { - simulateKeyDownUp(el, $.ui.keyCode.DOWN, true); - } - - equals(el.val(), -100); -}); - -test("keydown HOME on input, sets value to minimum", function() { - el = $("#spin"); - options = { - min:-100, - value:50, - step:10 - } - el.spinner(options); - - simulateKeyDownUp(el, $.ui.keyCode.HOME); equals(el.val(), -100); - - el.spinner('option', 'min', -200); - - simulateKeyDownUp(el, $.ui.keyCode.HOME); - - equals(el.val(), -200); -}); - -test("keydown END on input, sets value to maximum", function() { - el = $("#spin"); - options = { - max:100, - value:50, - step:10 - } - el.spinner(options); - - simulateKeyDownUp(el, $.ui.keyCode.END); - equals(el.val(), 100); - - el.spinner('option', 'max', 200); - - simulateKeyDownUp(el, $.ui.keyCode.END); - - equals(el.val(), 200); -}); - -test("keydown LEFT on input has no effect", function() { - el = $("#spin"); - el.spinner(); - var value = el.val(); - - simulateKeyDownUp(el, $.ui.keyCode.LEFT); - equals(el.val(), value); - - for (i = 0; i<5; i++) { - simulateKeyDownUp(el, $.ui.keyCode.LEFT); - } - - equals(el.val(), value); -}); - -test("keydown RIGHT on input has no effect", function() { - expect(2); - - el = $("#spin"); - el.spinner(); - var value = el.val(); - - simulateKeyDownUp(el, $.ui.keyCode.RIGHT); - equals(el.val(), value); - - for (i = 0; i<5; i++) { - simulateKeyDownUp(el, $.ui.keyCode.RIGHT); - } - - equals(el.val(), value); }); test("mouse click on buttons", function() { - expect(4); - - el = $("#spin").spinner(); - val = 0; + var el = $("#spin").spinner(), + val = 0; $(".ui-spinner-up").trigger("mousedown").trigger("mouseup"); - equals(el.val(), ++val, "mouse click to up"); $(".ui-spinner-down").trigger("mousedown").trigger("mouseup"); - equals(el.val(), --val, "mouse click to down"); el.val(50); - $(".ui-spinner-up").trigger("mousedown").trigger("mouseup"); - equals(el.val(), 51); el.val(50); - $(".ui-spinner-down").trigger("mousedown").trigger("mouseup"); - equals(el.val(), 49); - }); test("mouse wheel on input", function() { - ok(false, 'missing test - untested code is broken code'); + expect(3); + + var el = $("#spin").spinner(); + el.trigger("mousewheel", 1); + equal(el.val(), 1); + + // mousewheel handler uses a timeout, need to accomodate that + stop(); + setTimeout(function() { + el.trigger("mousewheel", -1); + equal(el.val(), 0); + + setTimeout(function() { + el.trigger("mousewheel", -1); + equal(el.val(), -1); + start(); + }, 100); + }, 100); + }); test("reading HTML5 attributes", function() { - expect(4); - - el = $('').spinner(); - + var el = $('').spinner(); equals(el.spinner('option', 'value'), 5, 'value'); equals(el.spinner('option', 'max'), 100, 'max'); equals(el.spinner('option', 'min'), -100, 'min'); @@ -335,23 +154,22 @@ test("reading HTML5 attributes", function() { }); test("ARIA attributes", function() { - expect(7); - - el = $('#spin').spinner({ min: -5, max: 5, value: 2 }); + var el = $('#spin').spinner({ min: -5, max: 5, value: 2 }), + wrapper = el.spinner("widget"); - equals(wrapper().attr('role'), 'spinbutton', 'role'); - equals(wrapper().attr('aria-valuemin'), -5, 'aria-valuemin'); - equals(wrapper().attr('aria-valuemax'), 5, 'aria-valuemax'); - equals(wrapper().attr('aria-valuenow'), 2, 'aria-valuenow'); + equals(wrapper.attr('role'), 'spinbutton', 'role'); + equals(wrapper.attr('aria-valuemin'), -5, 'aria-valuemin'); + equals(wrapper.attr('aria-valuemax'), 5, 'aria-valuemax'); + equals(wrapper.attr('aria-valuenow'), 2, 'aria-valuenow'); el.spinner('stepUp'); - equals(wrapper().attr('aria-valuenow'), 3, 'stepUp 1 step changes aria-valuenow'); + equals(wrapper.attr('aria-valuenow'), 3, 'stepUp 1 step changes aria-valuenow'); el.spinner('option', { min: -10, max: 10 }); - equals(wrapper().attr('aria-valuemin'), -10, 'min option changed aria-valuemin changes'); - equals(wrapper().attr('aria-valuemax'), 10, 'max option changed aria-valuemax changes'); + equals(wrapper.attr('aria-valuemin'), -10, 'min option changed aria-valuemin changes'); + equals(wrapper.attr('aria-valuemax'), 10, 'max option changed aria-valuemax changes'); }); })(jQuery); diff --git a/tests/unit/spinner/spinner_defaults.js b/tests/unit/spinner/spinner_defaults.js index aa88fd649..0b3368c00 100644 --- a/tests/unit/spinner/spinner_defaults.js +++ b/tests/unit/spinner/spinner_defaults.js @@ -3,15 +3,12 @@ */ var spinner_defaults = { - dir: 'ltr', disabled: false, incremental: true, - max: null, - min: null, - mouseWheel: true, - numberformat: "n", - page: 5, - step: null, + max: Number.MAX_VALUE, + min: -Number.MAX_VALUE, + numberformat: null, + step: 1, value: null }; diff --git a/tests/unit/spinner/spinner_events.js b/tests/unit/spinner/spinner_events.js index ae80801e6..1a006a3dc 100644 --- a/tests/unit/spinner/spinner_events.js +++ b/tests/unit/spinner/spinner_events.js @@ -6,11 +6,9 @@ module("spinner: events"); test("start", function() { - expect(1); - var start = 0; - el = $("#spin").spinner({ + var el = $("#spin").spinner({ start: function(){ start++; } @@ -22,11 +20,9 @@ test("start", function() { }); test("spin", function() { - expect(1); - var spin = 0; - el = $("#spin").spinner({ + var el = $("#spin").spinner({ spin: function(){ spin++; } @@ -38,11 +34,9 @@ test("spin", function() { }); test("stop", function() { - expect(1); - var stop = 0; - el = $("#spin").spinner({ + var el = $("#spin").spinner({ stop: function(){ stop++; } @@ -54,11 +48,9 @@ test("stop", function() { }); test("change", function() { - expect(1); - var start = spin = stop = change = 0; - el = $("#spin").spinner({ + var el = $("#spin").spinner({ change: function(){ change++; } diff --git a/tests/unit/spinner/spinner_methods.js b/tests/unit/spinner/spinner_methods.js index faeab75f3..fb58edbdf 100644 --- a/tests/unit/spinner/spinner_methods.js +++ b/tests/unit/spinner/spinner_methods.js @@ -6,17 +6,16 @@ module("spinner: methods"); test("disable", function() { - expect(14); - - el = $("#spin").spinner({ disabled: false }); - var val = el.val(); + var el = $("#spin").spinner({ disabled: false }), + val = el.val(), + wrapper = $("#spin").spinner("widget"); - ok(!wrapper().hasClass(".ui-spinner-disabled"), "before: wrapper does not have ui-spinner-disabled class"); - ok(!box().is(':disabled'), "before: input does not have disabled attribute"); + ok(!wrapper.hasClass("ui-spinner-disabled"), "before: wrapper does not have ui-spinner-disabled class"); + ok(!el.is(':disabled'), "before: input does not have disabled attribute"); el.spinner("disable"); - ok(wrapper().hasClass(".ui-spinner-disabled"), "after: wrapper has ui-spinner-disabled class"); - ok(box().is(':disabled'), "after: input has disabled attribute"); + ok(wrapper.hasClass("ui-spinner-disabled"), "after: wrapper has ui-spinner-disabled class"); + ok(el.is(':disabled'), "after: input has disabled attribute"); simulateKeyDownUp(el, $.ui.keyCode.UP); equals(val, el.val(), "keyboard - value does not change on key UP"); @@ -30,10 +29,10 @@ test("disable", function() { simulateKeyDownUp(el, $.ui.keyCode.PAGE_DOWN); equals(val, el.val(), "keyboard - value does not change on key PGDN"); - upButton().trigger('mousedown').trigger('mouseup'); + wrapper.find(":ui-button").first().trigger('mousedown').trigger('mouseup'); equals(val, el.val(), "mouse - value does not change on clicking up button"); - downButton().trigger('mousedown').trigger('mouseup'); + wrapper.find(":ui-button").last().trigger('mousedown').trigger('mouseup'); equals(val, el.val(), "mouse - value does not change on clicking down button"); el.spinner('stepUp', 6); @@ -43,7 +42,7 @@ test("disable", function() { equals(5, el.val(), "script - stepDown 1 step changes value"); el.spinner('pageUp'); - equals(10, el.val(), "script - pageUp 1 page changes value"); + equals(15, el.val(), "script - pageUp 1 page changes value"); el.spinner('pageDown'); equals(5, el.val(), "script - pageDown 1 page changes value"); @@ -51,61 +50,31 @@ test("disable", function() { }); test("enable", function() { - expect(14); + var el = $("#spin").spinner({ disabled: true }) + val = el.val(), + wrapper = el.spinner("widget"); - el = $("#spin").spinner({ disabled: true }); - var val = el.val(); - - ok(wrapper().hasClass(".ui-spinner-disabled"), "before: wrapper has ui-spinner-disabled class"); - ok(box().is(':disabled'), "before: input has disabled attribute"); + ok(wrapper.hasClass("ui-spinner-disabled"), "before: wrapper has ui-spinner-disabled class"); + ok(el.is(':disabled'), "before: input has disabled attribute"); el.spinner("enable"); - ok(!wrapper().hasClass(".ui-spinner-disabled"), "after: wrapper does not have ui-spinner-disabled class"); - ok(!box().is(':disabled'), "after: input does not have disabled attribute"); - - simulateKeyDownUp(el, $.ui.keyCode.UP); - equals(1, el.val(), "keyboard - value changes on key UP"); - - simulateKeyDownUp(el, $.ui.keyCode.DOWN); - equals(0, el.val(), "keyboard - value changes on key DOWN"); - - simulateKeyDownUp(el, $.ui.keyCode.PAGE_UP); - equals(5, el.val(), "keyboard - value changes on key PGUP"); - - simulateKeyDownUp(el, $.ui.keyCode.PAGE_DOWN); - equals(0, el.val(), "keyboard - value changes on key PGDN"); - - upButton().trigger('mousedown').trigger('mouseup'); - equals(1, el.val(), "mouse - value changes on clicking up button"); - - downButton().trigger('mousedown').trigger('mouseup'); - equals(0, el.val(), "mouse - value changes on clicking down button"); - - el.spinner('stepUp', 6); - equals(6, el.val(), "script - stepUp 6 steps changes value"); - - el.spinner('stepDown'); - equals(5, el.val(), "script - stepDown 1 step changes value"); - - el.spinner('pageUp'); - equals(10, el.val(), "script - pageUp 1 page changes value"); - - el.spinner('pageDown'); - equals(5, el.val(), "script - pageDown 1 page changes value"); - + ok(!wrapper.hasClass(".ui-spinner-disabled"), "after: wrapper does not have ui-spinner-disabled class"); + ok(!el.is(':disabled'), "after: input does not have disabled attribute"); }); test("pageDown", function() { - expect(4); - - el = $('#spin').spinner({ step: 2, page: 5, value: 0, min: -100 }); + var el = $('#spin').spinner({ + step: 2, + value: 0, + min: -100 + }); el.spinner('pageDown'); - equals(el.val(), -10, "pageDown 1 page"); + equals(el.val(), -20, "pageDown 1 page"); el.spinner('pageDown', 3); - equals(el.val(), -40, "pageDown 3 pages"); + equals(el.val(), -80, "pageDown 3 pages"); el.val(-91).spinner('pageDown'); equals(el.val(), -100, "value close to min and pageDown 1 page"); @@ -115,15 +84,17 @@ test("pageDown", function() { }); test("pageUp", function() { - expect(4); - - el = $('#spin').spinner({ step: 2, page: 5, value: 0, max: 100 }); + var el = $('#spin').spinner({ + step: 2, + value: 0, + max: 100 + }); el.spinner('pageUp'); - equals(el.val(), 10, "pageUp 1 page"); + equals(el.val(), 20, "pageUp 1 page"); el.spinner('pageUp', 3); - equals(el.val(), 40, "pageUp 3 pages"); + equals(el.val(), 80, "pageUp 3 pages"); el.val(91).spinner('pageUp'); equals(el.val(), 100, "value close to max and pageUp 1 page"); diff --git a/tests/unit/spinner/spinner_options.js b/tests/unit/spinner/spinner_options.js index 48fea3203..3db7e5c55 100644 --- a/tests/unit/spinner/spinner_options.js +++ b/tests/unit/spinner/spinner_options.js @@ -5,36 +5,33 @@ module("spinner: options"); -test("numberformat", function() { - ok( false, "tests for numberformat!"); +test("numberformat, number", function() { + var el = $("#spin").spinner({ + value: "1", + numberformat: "n" + }); + equal(el.val(), "1.00"); }); -test("dir - left-to-right (default)", function() { - expect(3); - - el = $("#spin"); - el.spinner(); - - ok(upButton().position().left > box().position().left, 'input on left up button on right'); - ok(downButton().position().left > box().position().left, 'input on left down button on right'); - ok(wrapper().hasClass('ui-spinner-ltr'), 'container has correct text direction class setting'); +test("numberformat, number, simple", function() { + var el = $("#spin").spinner({ + value: "1", + numberformat: "n0" + }); + equal(el.val(), "1"); }); -test("dir - right-to-left", function() { - expect(3); - - el = $("#spin"); - el.spinner({ dir: 'rtl' }); - - ok(upButton().position().left < box().position().left, 'input on right up button on left'); - ok(downButton().position().left < box().position().left, 'input on right down button on left'); - ok(wrapper().hasClass('ui-spinner-rtl'), 'container has correct text direction class setting'); +test("numberformat, currency", function() { + var el = $("#spin").spinner({ + value: "1", + numberformat: "C" + }); + equal(el.val(), "$1.00"); }); +/* TODO figure out how to test this properly test("incremental - false (default)", function() { - expect(2); - - el = $("#spin").spinner({ incremental:false }); + var el = $("#spin").spinner({ incremental:false }); for ( var i = 1 ; i<=120 ; i++ ) { el.simulate("keydown",{keyCode:$.ui.keyCode.UP}); @@ -51,10 +48,8 @@ test("incremental - false (default)", function() { equals(el.val(), -90, "incremental false - keydown 210 times"); }); -test("incremental - true", function() { - expect(2); - - el.spinner('option', 'incremental', true ); +test("incremental - true (default)", function() { + var el = $("#spin").spinner(); for ( var i = 1 ; i<=120 ; i++ ) { el.simulate("keydown",{keyCode:$.ui.keyCode.UP}); @@ -70,11 +65,10 @@ test("incremental - true", function() { equals(el.val(), -1800, "incremental true - keydown 210 times (300-100-100*10-10*100)"); }); +*/ test("max", function() { - expect(3); - - el = $("#spin").spinner({ max: 100, value: 1000 }); + var el = $("#spin").spinner({ max: 100, value: 1000 }); equals(el.val(), 100, "max constrained if value option is greater"); el.spinner('value', 1000); @@ -85,9 +79,7 @@ test("max", function() { }); test("min", function() { - expect(3); - - el = $("#spin").spinner({ min: -100, value: -1000 }); + var el = $("#spin").spinner({ min: -100, value: -1000 }); equals(el.val(), -100, "min constrained if value option is greater"); el.spinner('value', -1000); @@ -97,53 +89,14 @@ test("min", function() { equals(el.val(), -100, "min constrained if manual entry"); }); -test("mouseWheel", function() { - ok(false, 'missing test - untested code is broken code'); -}); - -test("page", function() { - expect(3); - - el = $("#spin").spinner({ step: 2, page:2.5 }); - - equals(el.val(), "0", "start number"); - - simulateKeyDownUp(el, $.ui.keyCode.PAGE_DOWN); - - equals(el.val(), "-5", "PAGE_DOWN on spinner once"); - - for ( var i = 1 ; i<=11 ; i++ ) { - simulateKeyDownUp(el, $.ui.keyCode.PAGE_UP); - } - - equals(el.val(), "50", "PAGE_UP 11 times on spinner"); -}); - -test("step", function() { - expect(7); - - el = $("#spin").spinner({ step:0.7 }); - equals(el.val(), "0.0", "value initialized to"); - - simulateKeyDownUp(el, $.ui.keyCode.DOWN); - equals(el.val(), "-0.7", "DOWN 1 time with step: 0.7"); - - for ( var i = 0 ; i < 11 ; i++ ) { - simulateKeyDownUp(el, $.ui.keyCode.UP); - } - equals(el.val(), "7.0", "UP 11 times with step: 0.7"); - - el.spinner('option', 'step', 1); - for ( var i = 0 ; i < 3 ; i++ ) { - simulateKeyDownUp(el, $.ui.keyCode.UP); - } - equals(el.val(), "10.0", "UP 3 times with step: 1"); +test("step, 2", function() { + var el = $("#spin").spinner({ step: 2 }); + equals(el.val(), "0", "value initialized to"); - el.spinner('option', 'step', 2); for ( var i = 0 ; i < 5 ; i++ ) { simulateKeyDownUp(el, $.ui.keyCode.UP); } - equals(el.val(), "20.0", "UP 5 times with step: 2"); + equals(el.val(), "10", "UP 5 times with step: 2"); el.spinner('value', '10.5'); equals(el.val(), "10.5", "value reset to"); @@ -153,8 +106,22 @@ test("step", function() { simulateKeyDownUp(el, $.ui.keyCode.UP); } equals(el.val(), "20.5", "UP 5 times with step: 2"); +}); +test("step, 0.7", function() { + var el = $("#spin").spinner({ + step: 0.7, + numberformat: "n1" + }); + equals(el.val(), "0.0", "value initialized to"); + + simulateKeyDownUp(el, $.ui.keyCode.DOWN); + equals(el.val(), "-0.7", "DOWN 1 time with step: 0.7"); + for ( var i = 0 ; i < 11 ; i++ ) { + simulateKeyDownUp(el, $.ui.keyCode.UP); + } + equals(el.val(), "7.0", "UP 11 times with step: 0.7"); }); test("value, default, specified in markup", function() { diff --git a/ui/jquery.ui.spinner.js b/ui/jquery.ui.spinner.js index 2c2da22d1..09d056f50 100644 --- a/ui/jquery.ui.spinner.js +++ b/ui/jquery.ui.spinner.js @@ -24,13 +24,27 @@ $.widget('ui.spinner', { incremental: true, max: Number.MAX_VALUE, min: -Number.MAX_VALUE, - numberformat: "n", + numberformat: null, step: 1, value: null }, - _create: function() { - this.value(this.options.value !== null ? this.options.value : this.element.val()); + _create: function() { + // html5 attributes initalization + // TODO refactor + var min = this.element.attr("min"); + if (typeof min == "string" && min.length > 0) { + this.options.min = this._parse(min); + } + var max = this.element.attr("max"); + if (typeof max == "string" && max.length > 0) { + this.options.max = this._parse(max); + } + var step = this.element.attr("step"); + if (typeof step == "string" && step.length > 0) { + this.options.step = this._parse(step); + } + this.value(this.options.value !== null ? this.options.value : this.element.val() || 0); this._draw(); this._mousewheel(); this._aria(); @@ -39,7 +53,7 @@ $.widget('ui.spinner', { var self = this, options = self.options; - var uiSpinner = self.element + var uiSpinner = this.uiSpinner = self.element .addClass('ui-spinner-input') .attr('autocomplete', 'off') .wrap(self._uiSpinnerHtml()) @@ -65,12 +79,18 @@ $.widget('ui.spinner', { this.element .bind('keydown'+namespace, function(event) { + if (self.options.disabled) { + return; + } if (self._start(event)) { return self._keydown(event); } return true; }) .bind('keyup'+namespace, function(event) { + if (self.options.disabled) { + return; + } if (self.spinning) { self._stop(event); self._change(event); @@ -88,11 +108,6 @@ $.widget('ui.spinner', { self.focused = false; }); - // disable spinner if element was already disabled - if (options.disabled) { - this.disable(); - } - // button bindings this.buttons = uiSpinner.find('.ui-spinner-button') .attr("tabIndex", -1) @@ -103,13 +118,19 @@ $.widget('ui.spinner', { .last() .removeClass("ui-corner-all") .end() - .bind('mousedown', function(event) { + .bind('mousedown', function(event) { + if (self.options.disabled) { + return; + } if (self._start(event) === false) { return false; } self._repeat(null, $(this).hasClass('ui-spinner-up') ? 1 : -1, event); }) .bind('mouseup', function(event) { + if (self.options.disabled) { + return; + } if (self.counter == 1) { self._spin(($(this).hasClass('ui-spinner-up') ? 1 : -1) * self.options.step, event); } @@ -119,6 +140,9 @@ $.widget('ui.spinner', { } }) .bind("mouseenter", function() { + if (self.options.disabled) { + return; + } // button will add ui-state-active if mouse was down while mouseleave and kept down if ($(this).hasClass("ui-state-active")) { if (self._start(event) === false) { @@ -132,9 +156,12 @@ $.widget('ui.spinner', { self._stop(event); self._change(event); } - }) + }); - self.uiSpinner = uiSpinner; + // disable spinner if element was already disabled + if (options.disabled) { + this.disable(); + } }, _uiSpinnerHtml: function() { return '
    '; @@ -154,9 +181,6 @@ $.widget('ui.spinner', { return false; }, _spin: function(step, event) { - if (this.options.disabled) { - return; - } if (!this.counter) { this.counter = 1; } @@ -176,7 +200,7 @@ $.widget('ui.spinner', { _stop: function(event) { this.counter = 0; if (this.timer) { - window.clearInterval(this.timer); + window.clearTimeout(this.timer); } this.element[0].focus(); this.spinning = false; @@ -226,6 +250,9 @@ $.widget('ui.spinner', { _mousewheel: function() { var self = this; this.element.bind("mousewheel", function(event, delta) { + if (self.options.disabled) { + return; + } if (!self._start(event)) { return false; } @@ -233,12 +260,13 @@ $.widget('ui.spinner', { if (self.timeout) { window.clearTimeout(self.timeout); } + // TODO can we implement that without a timeout? self.timeout = window.setTimeout(function() { if (self.spinning) { self._stop(event); self._change(event); } - }, 50); + }, 13); event.preventDefault(); }); }, @@ -286,11 +314,13 @@ $.widget('ui.spinner', { break; case 'value': this._format(this._parse(this.options.value)); + this._aria(); break; } }, _aria: function() { - this.uiSpinner + // TODO remove check, as soon as this method doesn't get called anymore before uiSpinner is initalized + this.uiSpinner && this.uiSpinner .attr('aria-valuemin', this.options.min) .attr('aria-valuemax', this.options.max) .attr('aria-valuenow', this.value()); @@ -305,13 +335,13 @@ $.widget('ui.spinner', { var culture = Globalization.culture || Globalization.cultures['default']; val = val.replace(culture.numberFormat.currency.symbol, ""); } - val = window.Globalization ? Globalization.parseFloat(val) : +val; + val = window.Globalization && this.options.numberformat ? Globalization.parseFloat(val) : +val; } console.log("input", input, "parsed", val) return isNaN(val) ? null : val; }, _format: function(num) { - this.element.val( window.Globalization ? Globalization.format(num, this.options.numberformat) : num ); + this.element.val( window.Globalization && this.options.numberformat ? Globalization.format(num, this.options.numberformat) : num ); }, destroy: function() { @@ -331,20 +361,18 @@ $.widget('ui.spinner', { enable: function() { this.element .removeAttr('disabled') - .siblings() - .removeAttr('disabled') .parent() .removeClass('ui-spinner-disabled ui-state-disabled'); this.options.disabled = false; + this.buttons.button("enable"); }, disable: function() { this.element .attr('disabled', true) - .siblings() - .attr('disabled', true) .parent() .addClass('ui-spinner-disabled ui-state-disabled'); this.options.disabled = true; + this.buttons.button("disable"); }, stepUp: function(steps) { this._spin((steps || 1) * this.options.step, null); @@ -355,10 +383,10 @@ $.widget('ui.spinner', { return this; }, pageUp: function(pages) { - return this.stepUp((pages || 1) * this.options.page); + return this.stepUp((pages || 1) * pageModifier); }, pageDown: function(pages) { - return this.stepDown((pages || 1) * this.options.page); + return this.stepDown((pages || 1) * pageModifier); }, widget: function() { -- cgit v1.2.3 From 134205840bb89b2a473cc003769d9a9a515157db Mon Sep 17 00:00:00 2001 From: jzaefferer Date: Tue, 26 Oct 2010 12:21:22 +0200 Subject: Slider: Some more test suite improvements --- tests/unit/spinner/spinner_core.js | 8 ++------ tests/unit/spinner/spinner_events.js | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/tests/unit/spinner/spinner_core.js b/tests/unit/spinner/spinner_core.js index 5a29b411a..6e3edafc8 100644 --- a/tests/unit/spinner/spinner_core.js +++ b/tests/unit/spinner/spinner_core.js @@ -13,14 +13,10 @@ var simulateKeyDownUp = function(el, kCode, shift) { module("spinner: core"); test("destroy", function() { + // cheat a bit to get IE6 to pass + $("#spin").val(0); var beforeHtml = $("#spin").parent().html(); var afterHtml = $("#spin").spinner().spinner("destroy").parent().html(); - // Opera 9 outputs role="" instead of removing the attribute like everyone else - /* - if ($.browser.opera) { - afterHtml = afterHtml.replace(/ role=""/g, ""); - } - */ equal( afterHtml, beforeHtml, "before/after html should be the same" ); }); diff --git a/tests/unit/spinner/spinner_events.js b/tests/unit/spinner/spinner_events.js index 1a006a3dc..c0771ca2f 100644 --- a/tests/unit/spinner/spinner_events.js +++ b/tests/unit/spinner/spinner_events.js @@ -48,7 +48,7 @@ test("stop", function() { }); test("change", function() { - var start = spin = stop = change = 0; + var change = 0; var el = $("#spin").spinner({ change: function(){ -- cgit v1.2.3 From f667bb3c8d8c95a0c9493a68b1e6549ba43fcd89 Mon Sep 17 00:00:00 2001 From: jzaefferer Date: Tue, 26 Oct 2010 12:44:32 +0200 Subject: Slider: Some more refactoring and cleanup --- tests/unit/spinner/spinner_core.js | 12 +-- ui/jquery.ui.spinner.js | 197 ++++++++++++++++++------------------- 2 files changed, 104 insertions(+), 105 deletions(-) diff --git a/tests/unit/spinner/spinner_core.js b/tests/unit/spinner/spinner_core.js index 6e3edafc8..88603708f 100644 --- a/tests/unit/spinner/spinner_core.js +++ b/tests/unit/spinner/spinner_core.js @@ -35,7 +35,7 @@ test("keydown UP on input, increases value not greater than max", function() { } equals(el.val(), 100); - el.val(50); + el.spinner("value", 50); simulateKeyDownUp(el, $.ui.keyCode.UP); equals(el.val(), 60); }); @@ -55,7 +55,7 @@ test("keydown DOWN on input, decreases value not less than min", function() { } equals(el.val(), -100); - el.val(50); + el.spinner("value", 50); simulateKeyDownUp(el, $.ui.keyCode.DOWN); equals(el.val(), 40); }); @@ -75,7 +75,7 @@ test("keydown PGUP on input, increases value not greater than max", function() { } equal(el.val(), 500); - el.val(0); + el.spinner("value", 0); simulateKeyDownUp(el, $.ui.keyCode.PAGE_UP); equals(el.val(), 100); }); @@ -95,7 +95,7 @@ test("keydown PGDN on input, decreases value not less than min", function() { } equals(el.val(), -500); - el.val(0); + el.spinner("value", 0); simulateKeyDownUp(el, $.ui.keyCode.PAGE_DOWN); equals(el.val(), -100); }); @@ -110,11 +110,11 @@ test("mouse click on buttons", function() { $(".ui-spinner-down").trigger("mousedown").trigger("mouseup"); equals(el.val(), --val, "mouse click to down"); - el.val(50); + el.spinner("value", 50); $(".ui-spinner-up").trigger("mousedown").trigger("mouseup"); equals(el.val(), 51); - el.val(50); + el.spinner("value", 50); $(".ui-spinner-down").trigger("mousedown").trigger("mouseup"); equals(el.val(), 49); }); diff --git a/ui/jquery.ui.spinner.js b/ui/jquery.ui.spinner.js index 09d056f50..d671bdf48 100644 --- a/ui/jquery.ui.spinner.js +++ b/ui/jquery.ui.spinner.js @@ -14,10 +14,7 @@ (function($) { // shortcut constants -var hover = 'ui-state-hover', - active = 'ui-state-active', - namespace = '.spinner', - pageModifier = 10; +var pageModifier = 10; $.widget('ui.spinner', { options: { @@ -49,6 +46,7 @@ $.widget('ui.spinner', { this._mousewheel(); this._aria(); }, + _draw: function() { var self = this, options = self.options; @@ -63,11 +61,11 @@ $.widget('ui.spinner', { // add behaviours .hover(function() { if (!options.disabled) { - $(this).addClass(hover); + $(this).addClass('ui-state-hover'); } self.hovered = true; }, function() { - $(this).removeClass(hover); + $(this).removeClass('ui-state-hover'); self.hovered = false; }); @@ -78,7 +76,7 @@ $.widget('ui.spinner', { } this.element - .bind('keydown'+namespace, function(event) { + .bind('keydown.spinner', function(event) { if (self.options.disabled) { return; } @@ -87,7 +85,7 @@ $.widget('ui.spinner', { } return true; }) - .bind('keyup'+namespace, function(event) { + .bind('keyup.spinner', function(event) { if (self.options.disabled) { return; } @@ -96,14 +94,14 @@ $.widget('ui.spinner', { self._change(event); } }) - .bind('focus'+namespace, function() { - uiSpinner.addClass(active); + .bind('focus.spinner', function() { + uiSpinner.addClass('ui-state-active'); self.focused = true; }) - .bind('blur'+namespace, function(event) { + .bind('blur.spinner', function(event) { self.value(self.element.val()); if (!self.hovered) { - uiSpinner.removeClass(active); + uiSpinner.removeClass('ui-state-active'); } self.focused = false; }); @@ -163,66 +161,7 @@ $.widget('ui.spinner', { this.disable(); } }, - _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.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.clearTimeout(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.clearTimeout(this.timer); - } - - this.timer = window.setTimeout(function() { - self._repeat(self.options.incremental && self.counter > 20 ? 20 : i, steps, event); - }, i); - - self._spin(steps*self.options.step, event); - }, + _keydown: function(event) { var o = this.options, KEYS = $.ui.keyCode; @@ -247,9 +186,10 @@ $.widget('ui.spinner', { return true; }, + _mousewheel: function() { var self = this; - this.element.bind("mousewheel", function(event, delta) { + this.element.bind("mousewheel.spinner", function(event, delta) { if (self.options.disabled) { return; } @@ -270,22 +210,75 @@ $.widget('ui.spinner', { event.preventDefault(); }); }, - value: function(newVal) { - if (!arguments.length) { - return this._parse(this.element.val()); + + _uiSpinnerHtml: function() { + return '
    '; + }, + + _buttonHtml: function() { + return '' + + ''; + }, + + _start: function(event) { + if (!this.spinning && this._trigger('start', event, { value: this.options.value}) !== false) { + if (!this.counter) { + this.counter = 1; + } + this.spinning = true; + return true; } - this._setOption('value', newVal); + return false; }, - _getData: function(key) { - switch (key) { - case 'min': - case 'max': - case 'step': - return this['_'+key](); - break; + + // TODO tune repeat behaviour, see http://jsbin.com/aruki4/2 for reference + _repeat: function(i, steps, event) { + var self = this; + i = i || 100; + + if (this.timer) { + window.clearTimeout(this.timer); + } + + this.timer = window.setTimeout(function() { + self._repeat(self.options.incremental && self.counter > 20 ? 20 : i, steps, event); + }, i); + + self._spin(steps*self.options.step, event); + }, + + _spin: function(step, event) { + if (!this.counter) { + this.counter = 1; + } + + var newVal = this.options.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++; } - return this.options[key]; }, + + _stop: function(event) { + this.counter = 0; + if (this.timer) { + window.clearTimeout(this.timer); + } + this.element[0].focus(); + this.spinning = false; + this._trigger('stop', event); + }, + + _change: function(event) { + this._trigger('change', event); + }, + _setOption: function(key, value) { if (key == 'value') { value = this._parse(value); @@ -299,31 +292,24 @@ $.widget('ui.spinner', { $.Widget.prototype._setOption.call( this, key, value ); this._afterSetData(key, value); }, + _afterSetData: function(key, value) { switch(key) { + case 'value': + this._format(this.options.value); 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 'value': - this._format(this._parse(this.options.value)); - this._aria(); - break; } }, + _aria: function() { // TODO remove check, as soon as this method doesn't get called anymore before uiSpinner is initalized this.uiSpinner && this.uiSpinner .attr('aria-valuemin', this.options.min) .attr('aria-valuemax', this.options.max) - .attr('aria-valuenow', this.value()); + .attr('aria-valuenow', this.options.value); }, _parse: function(val) { @@ -337,9 +323,9 @@ $.widget('ui.spinner', { } val = window.Globalization && this.options.numberformat ? Globalization.parseFloat(val) : +val; } - console.log("input", input, "parsed", val) return isNaN(val) ? null : val; }, + _format: function(num) { this.element.val( window.Globalization && this.options.numberformat ? Globalization.format(num, this.options.numberformat) : num ); }, @@ -354,10 +340,11 @@ $.widget('ui.spinner', { .removeAttr('disabled') .removeAttr('autocomplete') .removeData('spinner') - .unbind(namespace); + .unbind(".spinner"); this.uiSpinner.replaceWith(this.element); }, + enable: function() { this.element .removeAttr('disabled') @@ -366,6 +353,7 @@ $.widget('ui.spinner', { this.options.disabled = false; this.buttons.button("enable"); }, + disable: function() { this.element .attr('disabled', true) @@ -374,21 +362,32 @@ $.widget('ui.spinner', { this.options.disabled = true; this.buttons.button("disable"); }, + stepUp: function(steps) { this._spin((steps || 1) * this.options.step, null); return this; }, + stepDown: function(steps) { this._spin((steps || 1) * -this.options.step, null); return this; }, + pageUp: function(pages) { return this.stepUp((pages || 1) * pageModifier); }, + pageDown: function(pages) { return this.stepDown((pages || 1) * pageModifier); }, + value: function(newVal) { + if (!arguments.length) { + return this.options.value; + } + this._setOption('value', newVal); + }, + widget: function() { return this.uiSpinner; } -- cgit v1.2.3 From 30d431b08a86e92db817985c34f5f2bb7d0274c2 Mon Sep 17 00:00:00 2001 From: jzaefferer Date: Tue, 26 Oct 2010 12:53:34 +0200 Subject: Slider: Improve destroy method --- ui/jquery.ui.spinner.js | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/ui/jquery.ui.spinner.js b/ui/jquery.ui.spinner.js index d671bdf48..29772904b 100644 --- a/ui/jquery.ui.spinner.js +++ b/ui/jquery.ui.spinner.js @@ -331,18 +331,12 @@ $.widget('ui.spinner', { }, destroy: function() { - if ($.fn.mousewheel) { - this.element.unmousewheel(); - } - this.element .removeClass('ui-spinner-input') .removeAttr('disabled') - .removeAttr('autocomplete') - .removeData('spinner') - .unbind(".spinner"); - - this.uiSpinner.replaceWith(this.element); + .removeAttr('autocomplete'); + $.Widget.prototype.destroy.call( this ); + this.uiSpinner.replaceWith(this.element); }, enable: function() { -- cgit v1.2.3 From 720e9d383c3f1385da425e18bc6932f31dce1ebb Mon Sep 17 00:00:00 2001 From: jzaefferer Date: Tue, 26 Oct 2010 12:56:40 +0200 Subject: Slider: Cleanup public methods --- ui/jquery.ui.spinner.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ui/jquery.ui.spinner.js b/ui/jquery.ui.spinner.js index 29772904b..9597efb73 100644 --- a/ui/jquery.ui.spinner.js +++ b/ui/jquery.ui.spinner.js @@ -359,20 +359,18 @@ $.widget('ui.spinner', { stepUp: function(steps) { this._spin((steps || 1) * this.options.step, null); - return this; }, stepDown: function(steps) { this._spin((steps || 1) * -this.options.step, null); - return this; }, pageUp: function(pages) { - return this.stepUp((pages || 1) * pageModifier); + this.stepUp((pages || 1) * pageModifier); }, pageDown: function(pages) { - return this.stepDown((pages || 1) * pageModifier); + this.stepDown((pages || 1) * pageModifier); }, value: function(newVal) { -- cgit v1.2.3 From d91cd61f79bf048539ac9443ae3d8ffcbe55ef90 Mon Sep 17 00:00:00 2001 From: jzaefferer Date: Wed, 27 Oct 2010 16:38:00 +0200 Subject: Spinner: More refactorings and cleanups, and heavy improvement on the incremental-logic, including a delay of 500ms after the first increment and before continous increments --- demos/spinner/currency.html | 1 - ui/jquery.ui.spinner.js | 121 ++++++++++++++++++-------------------------- 2 files changed, 50 insertions(+), 72 deletions(-) diff --git a/demos/spinner/currency.html b/demos/spinner/currency.html index 0909f4ef5..6ecd43c1e 100644 --- a/demos/spinner/currency.html +++ b/demos/spinner/currency.html @@ -18,7 +18,6 @@ $(function() { $("#currency").change(function() { var current = $("#spinner").spinner("value"); - console.log("before!!", current) Globalization.preferCulture($(this).val()); $("#spinner").spinner("value", current); }) diff --git a/ui/jquery.ui.spinner.js b/ui/jquery.ui.spinner.js index 9597efb73..b6cf615da 100644 --- a/ui/jquery.ui.spinner.js +++ b/ui/jquery.ui.spinner.js @@ -27,8 +27,14 @@ $.widget('ui.spinner', { }, _create: function() { - // html5 attributes initalization - // TODO refactor + this._draw(); + this._markupOptions(); + this._mousewheel(); + this._aria(); + }, + + _markupOptions: function() { + // TODO refactor and read only when the related option is null (set default to null, init Number.MAX_VALUE only when nothing is specified) var min = this.element.attr("min"); if (typeof min == "string" && min.length > 0) { this.options.min = this._parse(min); @@ -42,9 +48,6 @@ $.widget('ui.spinner', { this.options.step = this._parse(step); } this.value(this.options.value !== null ? this.options.value : this.element.val() || 0); - this._draw(); - this._mousewheel(); - this._aria(); }, _draw: function() { @@ -69,7 +72,7 @@ $.widget('ui.spinner', { self.hovered = false; }); - // TODO: need a better way to exclude IE8 without resorting to $.browser.version + // TODO: move to theme, ask FG how // 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'); @@ -110,12 +113,7 @@ $.widget('ui.spinner', { this.buttons = uiSpinner.find('.ui-spinner-button') .attr("tabIndex", -1) .button() - .first() - .removeClass("ui-corner-all") - .end() - .last() - .removeClass("ui-corner-all") - .end() + .removeClass("ui-corner-all") .bind('mousedown', function(event) { if (self.options.disabled) { return; @@ -129,9 +127,6 @@ $.widget('ui.spinner', { if (self.options.disabled) { return; } - if (self.counter == 1) { - self._spin(($(this).hasClass('ui-spinner-up') ? 1 : -1) * self.options.step, event); - } if (self.spinning) { self._stop(event); self._change(event); @@ -150,7 +145,7 @@ $.widget('ui.spinner', { } }) .bind("mouseleave", function() { - if (self.timer && self.spinning) { + if (self.spinning) { self._stop(event); self._change(event); } @@ -197,11 +192,9 @@ $.widget('ui.spinner', { return false; } self._spin((delta > 0 ? 1 : -1) * self.options.step, event); - if (self.timeout) { - window.clearTimeout(self.timeout); - } + clearTimeout(self.timeout); // TODO can we implement that without a timeout? - self.timeout = window.setTimeout(function() { + self.timeout = setTimeout(function() { if (self.spinning) { self._stop(event); self._change(event); @@ -231,20 +224,16 @@ $.widget('ui.spinner', { return false; }, - // TODO tune repeat behaviour, see http://jsbin.com/aruki4/2 for reference _repeat: function(i, steps, event) { var self = this; - i = i || 100; + i = i || 500; - if (this.timer) { - window.clearTimeout(this.timer); - } - - this.timer = window.setTimeout(function() { - self._repeat(self.options.incremental && self.counter > 20 ? 20 : i, steps, event); + clearTimeout(this.timer); + this.timer = setTimeout(function() { + self._repeat(40, steps, event); }, i); - self._spin(steps*self.options.step, event); + self._spin(steps * self.options.step, event); }, _spin: function(step, event) { @@ -252,13 +241,16 @@ $.widget('ui.spinner', { this.counter = 1; } - var newVal = this.options.value + step * (this.options.incremental && this.counter > 100 - ? this.counter > 200 - ? 100 - : 10 - : 1); + // TODO refactor, maybe figure out some non-linear math + var newVal = this.options.value + step * (this.options.incremental && + this.counter > 20 + ? this.counter > 100 + ? this.counter > 200 + ? 100 + : 10 + : 2 + : 1); - // cancelable if (this._trigger('spin', event, { value: newVal }) !== false) { this.value(newVal); this.counter++; @@ -288,28 +280,32 @@ $.widget('ui.spinner', { if (value > this.options.max) { value = this.options.max; } - } + } + if (key == 'disabled') { + if (value) { + this.element.attr("disabled", true); + this.buttons.button("disable"); + } else { + this.element.removeAttr("disabled"); + this.buttons.button("enable"); + } + } $.Widget.prototype._setOption.call( this, key, value ); - this._afterSetData(key, value); }, - _afterSetData: function(key, value) { - switch(key) { - case 'value': - this._format(this.options.value); - case 'max': - case 'min': - case 'step': - this._aria(); + _setOptions: function( options ) { + $.Widget.prototype._setOptions.call( this, options ); + if ( "value" in options ) { + this._format( this.options.value ); } + this._aria(); }, _aria: function() { - // TODO remove check, as soon as this method doesn't get called anymore before uiSpinner is initalized - this.uiSpinner && this.uiSpinner - .attr('aria-valuemin', this.options.min) - .attr('aria-valuemax', this.options.max) - .attr('aria-valuenow', this.options.value); + this.uiSpinner + .attr('aria-valuemin', this.options.min) + .attr('aria-valuemax', this.options.max) + .attr('aria-valuenow', this.options.value); }, _parse: function(val) { @@ -327,6 +323,7 @@ $.widget('ui.spinner', { }, _format: function(num) { + var num = this.options.value; this.element.val( window.Globalization && this.options.numberformat ? Globalization.format(num, this.options.numberformat) : num ); }, @@ -339,30 +336,12 @@ $.widget('ui.spinner', { this.uiSpinner.replaceWith(this.element); }, - enable: function() { - this.element - .removeAttr('disabled') - .parent() - .removeClass('ui-spinner-disabled ui-state-disabled'); - this.options.disabled = false; - this.buttons.button("enable"); - }, - - disable: function() { - this.element - .attr('disabled', true) - .parent() - .addClass('ui-spinner-disabled ui-state-disabled'); - this.options.disabled = true; - this.buttons.button("disable"); - }, - stepUp: function(steps) { - this._spin((steps || 1) * this.options.step, null); + this._spin((steps || 1) * this.options.step); }, stepDown: function(steps) { - this._spin((steps || 1) * -this.options.step, null); + this._spin((steps || 1) * -this.options.step); }, pageUp: function(pages) { @@ -377,7 +356,7 @@ $.widget('ui.spinner', { if (!arguments.length) { return this.options.value; } - this._setOption('value', newVal); + this.option('value', newVal); }, widget: function() { -- cgit v1.2.3 From 78872dca42bad0e58a8e0bd34c415c036d97ebe4 Mon Sep 17 00:00:00 2001 From: jzaefferer Date: Wed, 27 Oct 2010 17:45:32 +0200 Subject: Spinner: Improved mousewheel event handling --- ui/jquery.ui.spinner.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/ui/jquery.ui.spinner.js b/ui/jquery.ui.spinner.js index b6cf615da..349dc73c9 100644 --- a/ui/jquery.ui.spinner.js +++ b/ui/jquery.ui.spinner.js @@ -183,23 +183,26 @@ $.widget('ui.spinner', { }, _mousewheel: function() { + // need the delta normalization that mousewheel plugin provides + if (!$.fn.mousewheel) { + return; + } var self = this; this.element.bind("mousewheel.spinner", function(event, delta) { if (self.options.disabled) { return; } - if (!self._start(event)) { + if (!self.spinning && !self._start(event)) { return false; } self._spin((delta > 0 ? 1 : -1) * self.options.step, event); clearTimeout(self.timeout); - // TODO can we implement that without a timeout? self.timeout = setTimeout(function() { if (self.spinning) { self._stop(event); self._change(event); } - }, 13); + }, 100); event.preventDefault(); }); }, @@ -214,7 +217,7 @@ $.widget('ui.spinner', { }, _start: function(event) { - if (!this.spinning && this._trigger('start', event, { value: this.options.value}) !== false) { + if (!this.spinning && this._trigger('start', event) !== false) { if (!this.counter) { this.counter = 1; } -- cgit v1.2.3 From 9fa6edcbec6c3bcbab1c761417cf14b6f030b2cb Mon Sep 17 00:00:00 2001 From: jzaefferer Date: Wed, 27 Oct 2010 17:47:49 +0200 Subject: Spinner: Visual test page for events --- tests/visual/spinner/spinner.html | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 tests/visual/spinner/spinner.html diff --git a/tests/visual/spinner/spinner.html b/tests/visual/spinner/spinner.html new file mode 100644 index 000000000..637e87d71 --- /dev/null +++ b/tests/visual/spinner/spinner.html @@ -0,0 +1,36 @@ + + + + Spinner Visual Test Page + + + + + + + + + + + + +

    + + \ No newline at end of file -- cgit v1.2.3 From 183fb69abf505ae76c5fff4f113c2b7ccd391863 Mon Sep 17 00:00:00 2001 From: jzaefferer Date: Wed, 27 Oct 2010 17:51:55 +0200 Subject: Slider: Add spinner to demo index --- demos/index.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/demos/index.html b/demos/index.html index 427d5b019..1f5989f48 100644 --- a/demos/index.html +++ b/demos/index.html @@ -23,6 +23,7 @@ + @@ -269,6 +270,7 @@
    Dialog
    Progressbar
    Slider
    +
    Spinner
    Tabs
    Effects
    Color Animation
    -- cgit v1.2.3 From 23157be9c3bcea5766729face92e68d11b5bfe85 Mon Sep 17 00:00:00 2001 From: jzaefferer Date: Thu, 28 Oct 2010 17:32:01 +0200 Subject: Spinner: Refactored reading htlm5 attributes option init --- tests/unit/spinner/spinner_defaults.js | 6 +++--- ui/jquery.ui.spinner.js | 30 ++++++++++++++---------------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/tests/unit/spinner/spinner_defaults.js b/tests/unit/spinner/spinner_defaults.js index 0b3368c00..2f5d77585 100644 --- a/tests/unit/spinner/spinner_defaults.js +++ b/tests/unit/spinner/spinner_defaults.js @@ -5,10 +5,10 @@ var spinner_defaults = { disabled: false, incremental: true, - max: Number.MAX_VALUE, - min: -Number.MAX_VALUE, + max: null, + min: null, numberformat: null, - step: 1, + step: null, value: null }; diff --git a/ui/jquery.ui.spinner.js b/ui/jquery.ui.spinner.js index 349dc73c9..87e611ede 100644 --- a/ui/jquery.ui.spinner.js +++ b/ui/jquery.ui.spinner.js @@ -19,10 +19,10 @@ var pageModifier = 10; $.widget('ui.spinner', { options: { incremental: true, - max: Number.MAX_VALUE, - min: -Number.MAX_VALUE, + max: null, + min: null, numberformat: null, - step: 1, + step: null, value: null }, @@ -34,19 +34,17 @@ $.widget('ui.spinner', { }, _markupOptions: function() { - // TODO refactor and read only when the related option is null (set default to null, init Number.MAX_VALUE only when nothing is specified) - var min = this.element.attr("min"); - if (typeof min == "string" && min.length > 0) { - this.options.min = this._parse(min); - } - var max = this.element.attr("max"); - if (typeof max == "string" && max.length > 0) { - this.options.max = this._parse(max); - } - var step = this.element.attr("step"); - if (typeof step == "string" && step.length > 0) { - this.options.step = this._parse(step); - } + var _this = this; + $.each({ + min: -Number.MAX_VALUE, + max: Number.MAX_VALUE, + step: 1 + }, function(attr, defaultValue) { + if (_this.options[attr] === null) { + var value = _this.element.attr(attr); + _this.options[attr] = typeof value == "string" && value.length > 0 ? _this._parse(value) : defaultValue; + } + }); this.value(this.options.value !== null ? this.options.value : this.element.val() || 0); }, -- cgit v1.2.3 From a1eb5f4592be0ab6131f3ccf2e2428cf92900022 Mon Sep 17 00:00:00 2001 From: jzaefferer Date: Thu, 28 Oct 2010 17:45:34 +0200 Subject: Spinner: Make sure that options.value and input.val() are in sync --- ui/jquery.ui.spinner.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/jquery.ui.spinner.js b/ui/jquery.ui.spinner.js index 87e611ede..ad1698d16 100644 --- a/ui/jquery.ui.spinner.js +++ b/ui/jquery.ui.spinner.js @@ -243,7 +243,7 @@ $.widget('ui.spinner', { } // TODO refactor, maybe figure out some non-linear math - var newVal = this.options.value + step * (this.options.incremental && + var newVal = this.value() + step * (this.options.incremental && this.counter > 20 ? this.counter > 100 ? this.counter > 200 @@ -355,7 +355,7 @@ $.widget('ui.spinner', { value: function(newVal) { if (!arguments.length) { - return this.options.value; + return this._parse(this.element.val()); } this.option('value', newVal); }, -- cgit v1.2.3 From 345592ff4a802a0049aae016d88605b135f6d13a Mon Sep 17 00:00:00 2001 From: jzaefferer Date: Tue, 2 Nov 2010 16:31:22 +0100 Subject: Spinner: Removing rtl demo --- demos/spinner/index.html | 1 - demos/spinner/rtl.html | 36 ------------------------------------ 2 files changed, 37 deletions(-) delete mode 100644 demos/spinner/rtl.html diff --git a/demos/spinner/index.html b/demos/spinner/index.html index 68716ac99..f43f6e2a1 100644 --- a/demos/spinner/index.html +++ b/demos/spinner/index.html @@ -13,7 +13,6 @@
  • Currency
  • Map
  • Mousewheel Disabled
  • -
  • RTL
diff --git a/demos/spinner/rtl.html b/demos/spinner/rtl.html deleted file mode 100644 index 93a2022ff..000000000 --- a/demos/spinner/rtl.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - - jQuery UI Spinner - Default functionality - - - - - - - - - - - - -
- -

-

- -
- -
-

-Default spinner. -

-
- - - -- cgit v1.2.3 From fceb70d526587b78ceeaa7035ad5e9f152d79816 Mon Sep 17 00:00:00 2001 From: jzaefferer Date: Tue, 2 Nov 2010 16:41:11 +0100 Subject: Spinner: Adding overflow demo --- demos/spinner/index.html | 1 + demos/spinner/overflow.html | 47 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 demos/spinner/overflow.html diff --git a/demos/spinner/index.html b/demos/spinner/index.html index f43f6e2a1..766f6552c 100644 --- a/demos/spinner/index.html +++ b/demos/spinner/index.html @@ -13,6 +13,7 @@
  • Currency
  • Map
  • Mousewheel Disabled
  • +
  • Overflow
  • diff --git a/demos/spinner/overflow.html b/demos/spinner/overflow.html new file mode 100644 index 000000000..716489155 --- /dev/null +++ b/demos/spinner/overflow.html @@ -0,0 +1,47 @@ + + + + + jQuery UI Spinner - Default functionality + + + + + + + + + + + + +
    +

    + + +

    +
    + +
    +

    + Overflowing spinner restricted to a range of -10 to 10. + For anything above 10, it'll overflow to -10, and the other way round. +

    +
    + + + -- cgit v1.2.3 From bd3d324572811560da2cba0789b262dea5012051 Mon Sep 17 00:00:00 2001 From: jzaefferer Date: Sat, 20 Nov 2010 15:39:40 +0100 Subject: Spinner: Update license header. --- ui/jquery.ui.spinner.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/jquery.ui.spinner.js b/ui/jquery.ui.spinner.js index ad1698d16..80ade7ba4 100644 --- a/ui/jquery.ui.spinner.js +++ b/ui/jquery.ui.spinner.js @@ -1,9 +1,9 @@ /* * 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. + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license * * http://docs.jquery.com/UI/Spinner * -- cgit v1.2.3 From 970befceb4b782181fdca990971334a021000afb Mon Sep 17 00:00:00 2001 From: jzaefferer Date: Sun, 28 Nov 2010 19:59:29 +0100 Subject: Spinner: Add demo for time spinner --- demos/spinner/index.html | 2 +- demos/spinner/time.html | 61 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 demos/spinner/time.html diff --git a/demos/spinner/index.html b/demos/spinner/index.html index 766f6552c..a49fa1368 100644 --- a/demos/spinner/index.html +++ b/demos/spinner/index.html @@ -12,7 +12,7 @@
  • Decimal
  • Currency
  • Map
  • -
  • Mousewheel Disabled
  • +
  • Time
  • Overflow
  • diff --git a/demos/spinner/time.html b/demos/spinner/time.html new file mode 100644 index 000000000..2f55d0f09 --- /dev/null +++ b/demos/spinner/time.html @@ -0,0 +1,61 @@ + + + + + jQuery UI Spinner - decimal + + + + + + + + + + + + + + + +
    +

    + + +

    +
    + +
    +

    + Example of a decimal spinner. Step is set to 0.01. +
    The code handling the culture change reads the current spinner value, + then changes the culture, then sets the value again, resulting in an updated + formatting, based on the new culture. +

    +
    + + + -- cgit v1.2.3 From 9ef5cd02fa17e81d28e91b8a51f590148aa15949 Mon Sep 17 00:00:00 2001 From: jzaefferer Date: Tue, 30 Nov 2010 13:41:54 +0100 Subject: Spinner: Use inline-element for better inline-block support. Removed float:left from input to fix input focussing in IE6. --- themes/base/jquery.ui.spinner.css | 4 ++-- ui/jquery.ui.spinner.js | 8 +------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/themes/base/jquery.ui.spinner.css b/themes/base/jquery.ui.spinner.css index 0a0a5ef6d..711f4c885 100644 --- a/themes/base/jquery.ui.spinner.css +++ b/themes/base/jquery.ui.spinner.css @@ -1,7 +1,7 @@ /* 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; float: left; margin-left: .4em; margin-right: 22px; } +.ui-spinner-input { border: none; background: none; padding: 0; margin: .2em 0; vertical-align: middle; margin-left: .4em; margin-right: 22px; } .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; right: 0; } .ui-spinner a.ui-spinner-button { border-top: none; border-bottom: none; border-right: 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 */ @@ -9,4 +9,4 @@ .ui-spinner-down { bottom: 0; } /* TR overrides */ -div.ui-spinner { background: none; } +span.ui-spinner { background: none; } diff --git a/ui/jquery.ui.spinner.js b/ui/jquery.ui.spinner.js index 80ade7ba4..8be9bbd0c 100644 --- a/ui/jquery.ui.spinner.js +++ b/ui/jquery.ui.spinner.js @@ -70,12 +70,6 @@ $.widget('ui.spinner', { self.hovered = false; }); - // TODO: move to theme, ask FG how - // 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.spinner', function(event) { if (self.options.disabled) { @@ -206,7 +200,7 @@ $.widget('ui.spinner', { }, _uiSpinnerHtml: function() { - return '
    '; + return ''; }, _buttonHtml: function() { -- cgit v1.2.3 From 285c991b72f9eeaa4c79b723b8337b2ef1fa4cc9 Mon Sep 17 00:00:00 2001 From: jzaefferer Date: Tue, 30 Nov 2010 13:43:43 +0100 Subject: Spinner: Fix crlf to lf line endings. --- themes/base/jquery.ui.spinner.css | 24 +- ui/jquery.ui.spinner.js | 724 +++++++++++++++++++------------------- 2 files changed, 374 insertions(+), 374 deletions(-) diff --git a/themes/base/jquery.ui.spinner.css b/themes/base/jquery.ui.spinner.css index 711f4c885..77be36847 100644 --- a/themes/base/jquery.ui.spinner.css +++ b/themes/base/jquery.ui.spinner.css @@ -1,12 +1,12 @@ -/* 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; margin-left: .4em; margin-right: 22px; } -.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; right: 0; } -.ui-spinner a.ui-spinner-button { border-top: none; border-bottom: none; border-right: 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; } - -/* TR overrides */ -span.ui-spinner { background: none; } +/* 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; margin-left: .4em; margin-right: 22px; } +.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; right: 0; } +.ui-spinner a.ui-spinner-button { border-top: none; border-bottom: none; border-right: 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; } + +/* TR overrides */ +span.ui-spinner { background: none; } diff --git a/ui/jquery.ui.spinner.js b/ui/jquery.ui.spinner.js index 8be9bbd0c..d9659d167 100644 --- a/ui/jquery.ui.spinner.js +++ b/ui/jquery.ui.spinner.js @@ -1,362 +1,362 @@ -/* - * jQuery UI Spinner @VERSION - * - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Spinner - * - * Depends: - * jquery.ui.core.js - * jquery.ui.widget.js - */ -(function($) { - -// shortcut constants -var pageModifier = 10; - -$.widget('ui.spinner', { - options: { - incremental: true, - max: null, - min: null, - numberformat: null, - step: null, - value: null - }, - - _create: function() { - this._draw(); - this._markupOptions(); - this._mousewheel(); - this._aria(); - }, - - _markupOptions: function() { - var _this = this; - $.each({ - min: -Number.MAX_VALUE, - max: Number.MAX_VALUE, - step: 1 - }, function(attr, defaultValue) { - if (_this.options[attr] === null) { - var value = _this.element.attr(attr); - _this.options[attr] = typeof value == "string" && value.length > 0 ? _this._parse(value) : defaultValue; - } - }); - this.value(this.options.value !== null ? this.options.value : this.element.val() || 0); - }, - - _draw: function() { - var self = this, - options = self.options; - - var uiSpinner = this.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('ui-state-hover'); - } - self.hovered = true; - }, function() { - $(this).removeClass('ui-state-hover'); - self.hovered = false; - }); - - this.element - .bind('keydown.spinner', function(event) { - if (self.options.disabled) { - return; - } - if (self._start(event)) { - return self._keydown(event); - } - return true; - }) - .bind('keyup.spinner', function(event) { - if (self.options.disabled) { - return; - } - if (self.spinning) { - self._stop(event); - self._change(event); - } - }) - .bind('focus.spinner', function() { - uiSpinner.addClass('ui-state-active'); - self.focused = true; - }) - .bind('blur.spinner', function(event) { - self.value(self.element.val()); - if (!self.hovered) { - uiSpinner.removeClass('ui-state-active'); - } - self.focused = false; - }); - - // button bindings - this.buttons = uiSpinner.find('.ui-spinner-button') - .attr("tabIndex", -1) - .button() - .removeClass("ui-corner-all") - .bind('mousedown', function(event) { - if (self.options.disabled) { - return; - } - if (self._start(event) === false) { - return false; - } - self._repeat(null, $(this).hasClass('ui-spinner-up') ? 1 : -1, event); - }) - .bind('mouseup', function(event) { - if (self.options.disabled) { - return; - } - if (self.spinning) { - self._stop(event); - self._change(event); - } - }) - .bind("mouseenter", function() { - if (self.options.disabled) { - return; - } - // button will add ui-state-active if mouse was down while mouseleave and kept down - if ($(this).hasClass("ui-state-active")) { - if (self._start(event) === false) { - return false; - } - self._repeat(null, $(this).hasClass('ui-spinner-up') ? 1 : -1, event); - } - }) - .bind("mouseleave", function() { - if (self.spinning) { - self._stop(event); - self._change(event); - } - }); - - // disable spinner if element was already disabled - if (options.disabled) { - this.disable(); - } - }, - - _keydown: function(event) { - var o = this.options, - KEYS = $.ui.keyCode; - - switch (event.keyCode) { - case KEYS.UP: - this._repeat(null, 1, event); - return false; - case KEYS.DOWN: - this._repeat(null, -1, event); - return false; - case KEYS.PAGE_UP: - this._repeat(null, pageModifier, event); - return false; - case KEYS.PAGE_DOWN: - this._repeat(null, -pageModifier, event); - return false; - - case KEYS.ENTER: - this.value(this.element.val()); - } - - return true; - }, - - _mousewheel: function() { - // need the delta normalization that mousewheel plugin provides - if (!$.fn.mousewheel) { - return; - } - var self = this; - this.element.bind("mousewheel.spinner", function(event, delta) { - if (self.options.disabled) { - return; - } - if (!self.spinning && !self._start(event)) { - return false; - } - self._spin((delta > 0 ? 1 : -1) * self.options.step, event); - clearTimeout(self.timeout); - self.timeout = setTimeout(function() { - if (self.spinning) { - self._stop(event); - self._change(event); - } - }, 100); - event.preventDefault(); - }); - }, - - _uiSpinnerHtml: function() { - return ''; - }, - - _buttonHtml: function() { - return '' + - ''; - }, - - _start: function(event) { - if (!this.spinning && this._trigger('start', event) !== false) { - if (!this.counter) { - this.counter = 1; - } - this.spinning = true; - return true; - } - return false; - }, - - _repeat: function(i, steps, event) { - var self = this; - i = i || 500; - - clearTimeout(this.timer); - this.timer = setTimeout(function() { - self._repeat(40, steps, event); - }, i); - - self._spin(steps * self.options.step, event); - }, - - _spin: function(step, event) { - if (!this.counter) { - this.counter = 1; - } - - // TODO refactor, maybe figure out some non-linear math - var newVal = this.value() + step * (this.options.incremental && - this.counter > 20 - ? this.counter > 100 - ? this.counter > 200 - ? 100 - : 10 - : 2 - : 1); - - if (this._trigger('spin', event, { value: newVal }) !== false) { - this.value(newVal); - this.counter++; - } - }, - - _stop: function(event) { - this.counter = 0; - if (this.timer) { - window.clearTimeout(this.timer); - } - this.element[0].focus(); - this.spinning = false; - this._trigger('stop', event); - }, - - _change: function(event) { - this._trigger('change', event); - }, - - _setOption: function(key, value) { - if (key == 'value') { - value = this._parse(value); - if (value < this.options.min) { - value = this.options.min; - } - if (value > this.options.max) { - value = this.options.max; - } - } - if (key == 'disabled') { - if (value) { - this.element.attr("disabled", true); - this.buttons.button("disable"); - } else { - this.element.removeAttr("disabled"); - this.buttons.button("enable"); - } - } - $.Widget.prototype._setOption.call( this, key, value ); - }, - - _setOptions: function( options ) { - $.Widget.prototype._setOptions.call( this, options ); - if ( "value" in options ) { - this._format( this.options.value ); - } - this._aria(); - }, - - _aria: function() { - this.uiSpinner - .attr('aria-valuemin', this.options.min) - .attr('aria-valuemax', this.options.max) - .attr('aria-valuenow', this.options.value); - }, - - _parse: function(val) { - var input = val; - if (typeof val == 'string') { - // special case for currency formatting until Globalization handles currencies - if (this.options.numberformat == "C" && window.Globalization) { - // parseFloat should accept number format, including currency - var culture = Globalization.culture || Globalization.cultures['default']; - val = val.replace(culture.numberFormat.currency.symbol, ""); - } - val = window.Globalization && this.options.numberformat ? Globalization.parseFloat(val) : +val; - } - return isNaN(val) ? null : val; - }, - - _format: function(num) { - var num = this.options.value; - this.element.val( window.Globalization && this.options.numberformat ? Globalization.format(num, this.options.numberformat) : num ); - }, - - destroy: function() { - this.element - .removeClass('ui-spinner-input') - .removeAttr('disabled') - .removeAttr('autocomplete'); - $.Widget.prototype.destroy.call( this ); - this.uiSpinner.replaceWith(this.element); - }, - - stepUp: function(steps) { - this._spin((steps || 1) * this.options.step); - }, - - stepDown: function(steps) { - this._spin((steps || 1) * -this.options.step); - }, - - pageUp: function(pages) { - this.stepUp((pages || 1) * pageModifier); - }, - - pageDown: function(pages) { - this.stepDown((pages || 1) * pageModifier); - }, - - value: function(newVal) { - if (!arguments.length) { - return this._parse(this.element.val()); - } - this.option('value', newVal); - }, - - widget: function() { - return this.uiSpinner; - } -}); - -})(jQuery); +/* + * jQuery UI Spinner @VERSION + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Spinner + * + * Depends: + * jquery.ui.core.js + * jquery.ui.widget.js + */ +(function($) { + +// shortcut constants +var pageModifier = 10; + +$.widget('ui.spinner', { + options: { + incremental: true, + max: null, + min: null, + numberformat: null, + step: null, + value: null + }, + + _create: function() { + this._draw(); + this._markupOptions(); + this._mousewheel(); + this._aria(); + }, + + _markupOptions: function() { + var _this = this; + $.each({ + min: -Number.MAX_VALUE, + max: Number.MAX_VALUE, + step: 1 + }, function(attr, defaultValue) { + if (_this.options[attr] === null) { + var value = _this.element.attr(attr); + _this.options[attr] = typeof value == "string" && value.length > 0 ? _this._parse(value) : defaultValue; + } + }); + this.value(this.options.value !== null ? this.options.value : this.element.val() || 0); + }, + + _draw: function() { + var self = this, + options = self.options; + + var uiSpinner = this.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('ui-state-hover'); + } + self.hovered = true; + }, function() { + $(this).removeClass('ui-state-hover'); + self.hovered = false; + }); + + this.element + .bind('keydown.spinner', function(event) { + if (self.options.disabled) { + return; + } + if (self._start(event)) { + return self._keydown(event); + } + return true; + }) + .bind('keyup.spinner', function(event) { + if (self.options.disabled) { + return; + } + if (self.spinning) { + self._stop(event); + self._change(event); + } + }) + .bind('focus.spinner', function() { + uiSpinner.addClass('ui-state-active'); + self.focused = true; + }) + .bind('blur.spinner', function(event) { + self.value(self.element.val()); + if (!self.hovered) { + uiSpinner.removeClass('ui-state-active'); + } + self.focused = false; + }); + + // button bindings + this.buttons = uiSpinner.find('.ui-spinner-button') + .attr("tabIndex", -1) + .button() + .removeClass("ui-corner-all") + .bind('mousedown', function(event) { + if (self.options.disabled) { + return; + } + if (self._start(event) === false) { + return false; + } + self._repeat(null, $(this).hasClass('ui-spinner-up') ? 1 : -1, event); + }) + .bind('mouseup', function(event) { + if (self.options.disabled) { + return; + } + if (self.spinning) { + self._stop(event); + self._change(event); + } + }) + .bind("mouseenter", function() { + if (self.options.disabled) { + return; + } + // button will add ui-state-active if mouse was down while mouseleave and kept down + if ($(this).hasClass("ui-state-active")) { + if (self._start(event) === false) { + return false; + } + self._repeat(null, $(this).hasClass('ui-spinner-up') ? 1 : -1, event); + } + }) + .bind("mouseleave", function() { + if (self.spinning) { + self._stop(event); + self._change(event); + } + }); + + // disable spinner if element was already disabled + if (options.disabled) { + this.disable(); + } + }, + + _keydown: function(event) { + var o = this.options, + KEYS = $.ui.keyCode; + + switch (event.keyCode) { + case KEYS.UP: + this._repeat(null, 1, event); + return false; + case KEYS.DOWN: + this._repeat(null, -1, event); + return false; + case KEYS.PAGE_UP: + this._repeat(null, pageModifier, event); + return false; + case KEYS.PAGE_DOWN: + this._repeat(null, -pageModifier, event); + return false; + + case KEYS.ENTER: + this.value(this.element.val()); + } + + return true; + }, + + _mousewheel: function() { + // need the delta normalization that mousewheel plugin provides + if (!$.fn.mousewheel) { + return; + } + var self = this; + this.element.bind("mousewheel.spinner", function(event, delta) { + if (self.options.disabled) { + return; + } + if (!self.spinning && !self._start(event)) { + return false; + } + self._spin((delta > 0 ? 1 : -1) * self.options.step, event); + clearTimeout(self.timeout); + self.timeout = setTimeout(function() { + if (self.spinning) { + self._stop(event); + self._change(event); + } + }, 100); + event.preventDefault(); + }); + }, + + _uiSpinnerHtml: function() { + return ''; + }, + + _buttonHtml: function() { + return '' + + ''; + }, + + _start: function(event) { + if (!this.spinning && this._trigger('start', event) !== false) { + if (!this.counter) { + this.counter = 1; + } + this.spinning = true; + return true; + } + return false; + }, + + _repeat: function(i, steps, event) { + var self = this; + i = i || 500; + + clearTimeout(this.timer); + this.timer = setTimeout(function() { + self._repeat(40, steps, event); + }, i); + + self._spin(steps * self.options.step, event); + }, + + _spin: function(step, event) { + if (!this.counter) { + this.counter = 1; + } + + // TODO refactor, maybe figure out some non-linear math + var newVal = this.value() + step * (this.options.incremental && + this.counter > 20 + ? this.counter > 100 + ? this.counter > 200 + ? 100 + : 10 + : 2 + : 1); + + if (this._trigger('spin', event, { value: newVal }) !== false) { + this.value(newVal); + this.counter++; + } + }, + + _stop: function(event) { + this.counter = 0; + if (this.timer) { + window.clearTimeout(this.timer); + } + this.element[0].focus(); + this.spinning = false; + this._trigger('stop', event); + }, + + _change: function(event) { + this._trigger('change', event); + }, + + _setOption: function(key, value) { + if (key == 'value') { + value = this._parse(value); + if (value < this.options.min) { + value = this.options.min; + } + if (value > this.options.max) { + value = this.options.max; + } + } + if (key == 'disabled') { + if (value) { + this.element.attr("disabled", true); + this.buttons.button("disable"); + } else { + this.element.removeAttr("disabled"); + this.buttons.button("enable"); + } + } + $.Widget.prototype._setOption.call( this, key, value ); + }, + + _setOptions: function( options ) { + $.Widget.prototype._setOptions.call( this, options ); + if ( "value" in options ) { + this._format( this.options.value ); + } + this._aria(); + }, + + _aria: function() { + this.uiSpinner + .attr('aria-valuemin', this.options.min) + .attr('aria-valuemax', this.options.max) + .attr('aria-valuenow', this.options.value); + }, + + _parse: function(val) { + var input = val; + if (typeof val == 'string') { + // special case for currency formatting until Globalization handles currencies + if (this.options.numberformat == "C" && window.Globalization) { + // parseFloat should accept number format, including currency + var culture = Globalization.culture || Globalization.cultures['default']; + val = val.replace(culture.numberFormat.currency.symbol, ""); + } + val = window.Globalization && this.options.numberformat ? Globalization.parseFloat(val) : +val; + } + return isNaN(val) ? null : val; + }, + + _format: function(num) { + var num = this.options.value; + this.element.val( window.Globalization && this.options.numberformat ? Globalization.format(num, this.options.numberformat) : num ); + }, + + destroy: function() { + this.element + .removeClass('ui-spinner-input') + .removeAttr('disabled') + .removeAttr('autocomplete'); + $.Widget.prototype.destroy.call( this ); + this.uiSpinner.replaceWith(this.element); + }, + + stepUp: function(steps) { + this._spin((steps || 1) * this.options.step); + }, + + stepDown: function(steps) { + this._spin((steps || 1) * -this.options.step); + }, + + pageUp: function(pages) { + this.stepUp((pages || 1) * pageModifier); + }, + + pageDown: function(pages) { + this.stepDown((pages || 1) * pageModifier); + }, + + value: function(newVal) { + if (!arguments.length) { + return this._parse(this.element.val()); + } + this.option('value', newVal); + }, + + widget: function() { + return this.uiSpinner; + } +}); + +})(jQuery); -- cgit v1.2.3 From 56f842cee0af56b2674b1ee72772a6b7b8cdd5f3 Mon Sep 17 00:00:00 2001 From: jzaefferer Date: Tue, 30 Nov 2010 13:44:52 +0100 Subject: Spinner: Move aria-attributes to the focusable element of the widget --- ui/jquery.ui.spinner.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/jquery.ui.spinner.js b/ui/jquery.ui.spinner.js index d9659d167..7f32adbd8 100644 --- a/ui/jquery.ui.spinner.js +++ b/ui/jquery.ui.spinner.js @@ -297,7 +297,7 @@ $.widget('ui.spinner', { }, _aria: function() { - this.uiSpinner + this.element .attr('aria-valuemin', this.options.min) .attr('aria-valuemax', this.options.max) .attr('aria-valuenow', this.options.value); -- cgit v1.2.3 From aa59bdf70096fb8ae123b011ca3c6c083efbd410 Mon Sep 17 00:00:00 2001 From: jzaefferer Date: Wed, 1 Dec 2010 22:56:38 +0100 Subject: Spinner: Add spinner to visual tests index page --- tests/visual/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/visual/index.html b/tests/visual/index.html index 05f6fd49a..c7b709279 100644 --- a/tests/visual/index.html +++ b/tests/visual/index.html @@ -43,6 +43,7 @@
  • Dialog
  • Progressbar
  • Slider
  • +
  • Spinner
  • Tabs
  • -- cgit v1.2.3 From 13f931078343d7a1dd96090c3151a58fc647ea37 Mon Sep 17 00:00:00 2001 From: jzaefferer Date: Wed, 1 Dec 2010 22:56:58 +0100 Subject: Spinner: Added Themeswitcher to visual testpage --- tests/visual/spinner/spinner.html | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/visual/spinner/spinner.html b/tests/visual/spinner/spinner.html index 637e87d71..dc723a514 100644 --- a/tests/visual/spinner/spinner.html +++ b/tests/visual/spinner/spinner.html @@ -10,8 +10,15 @@ + - @@ -16,25 +15,31 @@ @@ -44,7 +49,14 @@

    - + +

    +

    + +

    diff --git a/ui/jquery.ui.spinner.js b/ui/jquery.ui.spinner.js index 7f32adbd8..39a498359 100644 --- a/ui/jquery.ui.spinner.js +++ b/ui/jquery.ui.spinner.js @@ -13,15 +13,13 @@ */ (function($) { -// shortcut constants -var pageModifier = 10; - $.widget('ui.spinner', { options: { incremental: true, max: null, min: null, numberformat: null, + page: 10, step: null, value: null }, @@ -161,10 +159,10 @@ $.widget('ui.spinner', { this._repeat(null, -1, event); return false; case KEYS.PAGE_UP: - this._repeat(null, pageModifier, event); + this._repeat(null, this.options.page, event); return false; case KEYS.PAGE_DOWN: - this._repeat(null, -pageModifier, event); + this._repeat(null, -this.options.page, event); return false; case KEYS.ENTER: @@ -340,11 +338,11 @@ $.widget('ui.spinner', { }, pageUp: function(pages) { - this.stepUp((pages || 1) * pageModifier); + this.stepUp((pages || 1) * this.options.page); }, pageDown: function(pages) { - this.stepDown((pages || 1) * pageModifier); + this.stepDown((pages || 1) * this.options.page); }, value: function(newVal) { -- cgit v1.2.3 From 26bc70180ac239f198334f9f040719a7ca2f5bb4 Mon Sep 17 00:00:00 2001 From: jzaefferer Date: Wed, 1 Dec 2010 23:31:01 +0100 Subject: Spinner: Update timespinner demo description. --- demos/spinner/time.html | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/demos/spinner/time.html b/demos/spinner/time.html index 4913a6307..72027eb1e 100644 --- a/demos/spinner/time.html +++ b/demos/spinner/time.html @@ -62,10 +62,9 @@

    - Example of a decimal spinner. Step is set to 0.01. -
    The code handling the culture change reads the current spinner value, - then changes the culture, then sets the value again, resulting in an updated - formatting, based on the new culture. + A custom widget extending spinner. Use the Globalization plugin to parse and output + a timestamp, with custom step and page options. Cursor up/down spins minutes, page up/down + spins hours.

    -- cgit v1.2.3