From 198d809f9bb8aa5a20181d8b4d0a2f58bbcc4179 Mon Sep 17 00:00:00 2001 From: Ca-Phun Ung Date: Tue, 19 Aug 2008 07:18:04 +0000 Subject: [PATCH] Added enhancements to ui.spinner as seen at http://yelotofu.com/labs/jquery/UI/spinner/spinner.html Enhancements include: - removal of all hardcoded styling for better theming capability - visual test page made more appealing supporting browser zooming - added utility functions to disable, enable and destroy ui.spinner - up/down buttons listens to more mouse and keyboard events - support for decimal and currency format - ui.spinner now restricts input on keydown - cleanup occurs when the input textbox loses focus. this seems to be a better approach for future scalability --- tests/images/spinner-bg.gif | Bin 0 -> 218 bytes tests/visual/spinner.html | 130 ++++++++++++++-- ui/ui.spinner.js | 294 ++++++++++++++++++++++++++++-------- 3 files changed, 345 insertions(+), 79 deletions(-) create mode 100644 tests/images/spinner-bg.gif diff --git a/tests/images/spinner-bg.gif b/tests/images/spinner-bg.gif new file mode 100644 index 0000000000000000000000000000000000000000..914b6d1baf76fb9fc3479090d61037ff9ff589b5 GIT binary patch literal 218 zcmZ?wbhEHbBpOo-x+WN#h)w;>zlU(hYPBz+AlXOF`CP`A8T_$~K;S5cFC{(11fh6b_bmKH?@YXE~{ Ba_0a5 literal 0 HcmV?d00001 diff --git a/tests/visual/spinner.html b/tests/visual/spinner.html index 863346c42..b3002ec0f 100644 --- a/tests/visual/spinner.html +++ b/tests/visual/spinner.html @@ -1,37 +1,97 @@ - -Untitled Document + +jQuery UI Spinner Test page - @@ -39,7 +99,45 @@ font-family:"lucida grande", tahoma, verdana, arial, sans-serif; - +

jQuery UI Spinner Test Page

+ +

This is a visual test page for developers and demonstrates some of the features included in ui.spinner. Mousewheel support is provided by the mousewheel plugin.

+ +

+

+ +

+ + + + +

+ +
+ +

+

+ +

+ + + + +

+ +
+ +

+

+ +

+ + + + +

+ +
\ No newline at end of file diff --git a/ui/ui.spinner.js b/ui/ui.spinner.js index 4bcb9e843..5d322bbc1 100644 --- a/ui/ui.spinner.js +++ b/ui/ui.spinner.js @@ -12,127 +12,295 @@ */ (function($) { -$.widget("ui.spinner", { +$.widget('ui.spinner', { _init: function() { - + if($.data(this.element[0], 'spinner')) return; + + // check for decimals in steppinng and set _decimals as internal (needs cleaning up) + var decimals = 0; + if (this.options.stepping.toString().indexOf('.') != -1) { + var s = this.options.stepping.toString(); + decimals = s.slice(s.indexOf('.')+1, s.length).length; + } + $.extend(this.options, { + _decimals: decimals + }); + //Initialize needed constants var self = this; - this.element.addClass("ui-spinner"); - this.element[0].value = this.options.start; + this.element + .addClass('ui-spinner-box') + .attr('autocomplete', 'off'); // switch off autocomplete in opera + + this._setValue( isNaN(this._getValue()) ? this.options.start : this._getValue() ); - var pickerHeight = this.element.innerHeight() / 2 - parseInt(this.element.css("borderTopWidth"),10) - 2; this.element - .wrap("
") + .wrap('
') .parent() - .css({ - position: this.element.css("position") == "static" ? "relative" : this.element.css("position"), - left: this.element.css("left"), - top: this.element.css("top"), - width: this.element.outerWidth(), - height: this.element.outerHeight() - }) - .css("float", this.element.css("float")) - .prepend('
') - .find("div.ui-spinner-up") - .bind("mousedown", function() { if(!self.counter) self.counter = 1; self._mousedown(100, "_up"); }) - .bind("mouseup", function(e) { if(self.counter == 1) self._up(); self.counter = 0; if(self.timer) window.clearInterval(self.timer); self.element[0].focus(); self._propagate("change", e); }) - .bind("dblclick", function(e) { self._up(); }) // mousedown/mouseup capture first click, now handle second click - .css({ height: pickerHeight, top: parseInt(this.element.css("borderTopWidth"),10)+1, right: parseInt(this.element.css("borderRightWidth"),10)+1 }) + .addClass('ui-spinner') + .append('') + .find('.ui-spinner-up') + .bind('mousedown', function(e) { + $(this).addClass('ui-spinner-pressed'); + if(!self.counter) self.counter = 1; + self._mousedown(100, '_up', e); + }) + .bind('mouseup', function(e) { + $(this).removeClass('ui-spinner-pressed'); + if(self.counter == 1) self._up(e); + self._mouseup(e); + }) + .bind('mouseout', function(e) { + $(this).removeClass('ui-spinner-pressed'); + self._mouseup(e); + }) + // mousedown/mouseup capture first click, now handle second click + .bind('dblclick', function(e) { + $(this).removeClass('ui-spinner-pressed'); + self._up(e); + }) + .bind('keydown.spinner', function(e) { + var KEYS = $.ui.spinner.keys; + if (e.keyCode == KEYS.SPACE || e.keyCode == KEYS.RETURN) { + $(this).addClass('ui-spinner-pressed'); + if(!self.counter) self.counter = 1; + self._up.call(self, e); + } else if (e.keyCode == KEYS.DOWN || e.keyCode == KEYS.RIGHT) { + self.element.siblings('.ui-spinner-down').focus(); + } else if (e.keyCode == KEYS.LEFT) { + self.element.focus(); + } + }) + .bind('keyup.spinner', function(e) { + $(this).removeClass('ui-spinner-pressed'); + self.counter = 0; + self._propagate('change', e); + }) .end() - .append('
') - .find("div.ui-spinner-down") - .bind("mousedown", function() { if(!self.counter) self.counter = 1; self._mousedown(100, "_down"); }) - .bind("mouseup", function(e) { if(self.counter == 1) self._down(); self.counter = 0; if(self.timer) window.clearInterval(self.timer); self.element[0].focus(); self._propagate("change", e); }) - .bind("dblclick", function(e) { self._down(); }) // mousedown/mouseup capture first click, now handle second click - .css({ height: pickerHeight, bottom: parseInt(this.element.css("borderBottomWidth"),10)+1, right: parseInt(this.element.css("borderRightWidth"),10)+1 }) - .end() - ; + .append('') + .find('.ui-spinner-down') + .bind('mousedown', function(e) { + $(this).addClass('ui-spinner-pressed'); + if(!self.counter) self.counter = 1; + self._mousedown(100, '_down', e); + }) + .bind('mouseup', function(e) { + $(this).removeClass('ui-spinner-pressed'); + if(self.counter == 1) self._down(); + self._mouseup(e); + }) + .bind('mouseout', function(e) { + $(this).removeClass('ui-spinner-pressed'); + self._mouseup(e); + }) + // mousedown/mouseup capture first click, now handle second click + .bind('dblclick', function(e) { + $(this).removeClass('ui-spinner-pressed'); + self._down(e); + }) + .bind('keydown.spinner', function(e) { + var KEYS = $.ui.spinner.keys; + if (e.keyCode == KEYS.SPACE || e.keyCode == KEYS.RETURN) { + $(this).addClass('ui-spinner-pressed'); + if(!self.counter) self.counter = 1; + self._down.call(self, e); + } else if (e.keyCode == KEYS.UP || e.keyCode == KEYS.LEFT) { + self.element.siblings('.ui-spinner-up').focus(); + } + }) + .bind('keyup.spinner', function(e) { + $(this).removeClass('ui-spinner-pressed'); + self.counter = 0; + self._propagate('change', e); + }) + .end(); this.element - .bind("keydown.spinner", function(e) { + .bind('keydown.spinner', function(e) { if(!self.counter) self.counter = 1; - self._keydown.call(self, e); + return self._keydown.call(self, e); }) - .bind("keyup.spinner", function(e) { + .bind('keyup.spinner', function(e) { self.counter = 0; - self._cleanUp(); - self._propagate("change", e); + self._propagate('change', e); }) - ; + .bind('blur.spinner', function(e) { + self._cleanUp() + }); + if ($.fn.mousewheel) { - this.element.mousewheel(function(e, delta) { self._mousewheel(e, delta); }); + this.element.mousewheel(function(e, delta) { + self._mousewheel(e, delta); + }); } - }, - plugins: {}, + _constrain: function() { - if(this.options.min != undefined && this.element[0].value < this.options.min) this.element[0].value = this.options.min; - if(this.options.max != undefined && this.element[0].value > this.options.max) this.element[0].value = this.options.max; + if(this.options.min != undefined && this._getValue() < this.options.min) this._setValue(this.options.min); + if(this.options.max != undefined && this._getValue() > this.options.max) this._setValue(this.options.max); }, _cleanUp: function() { - this.element[0].value = this.element[0].value.replace(/[^0-9\-]/g, ''); + this._setValue(this._getValue()); this._constrain(); }, - _spin: function(d ,e) { - if(isNaN(parseInt(this.element[0].value,10))) this.element[0].value = this.options.start; - this.element[0].value = parseFloat(this.element[0].value) + ( d == "up" ? 1 : -1 ) * (this.options.incremental && this.counter > 100 ? (this.counter > 200 ? 100 : 10) : 1) * this.options.stepping; + _spin: function(d, e) { + if (this.disabled) return; + + if(isNaN(this._getValue())) this._setValue(this.options.start); + this._setValue(this._getValue() + (d == 'up' ? 1:-1) * (this.options.incremental && this.counter > 100 ? (this.counter > 200 ? 100 : 10) : 1) * this.options.stepping); this._constrain(); if(this.counter) this.counter++; - this._propagate("spin", e); + this._propagate('spin', e); + }, _down: function(e) { - this._spin("down", e); + this._spin('down', e); + this._propagate('down', e); }, _up: function(e) { - this._spin("up", e); + this._spin('up', e); + this._propagate('up', e); }, - _mousedown: function(i, d) { + _mousedown: function(i, d, e) { var self = this; i = i || 100; if(this.timer) window.clearInterval(this.timer); this.timer = window.setInterval(function() { - self[d](); - if(self.counter > 20) self._mousedown(20, d); + self[d](e); + if(self.counter > 20) self._mousedown(20, d, e); }, i); }, + _mouseup: function(e) { + this.counter = 0; + if(this.timer) window.clearInterval(this.timer); + this.element[0].focus(); + this._propagate('change', e); + }, _keydown: function(e) { - if(e.keyCode == 38 || e.keyCode == 39) this._up(e); - if(e.keyCode == 40 || e.keyCode == 37) this._down(e); - if(e.keyCode == 36) this.element[0].value = this.options.min || this.options.start; //Home key goes to min, if defined, else to start - if(e.keyCode == 35 && this.options.max != undefined) this.element[0].value = this.options.max; //End key goes to maximum + var KEYS = $.ui.spinner.keys; + + if(e.keyCode == KEYS.UP) this._up(e); + if(e.keyCode == KEYS.DOWN) this._down(e); + if(e.keyCode == KEYS.HOME) this._setValue(this.options.min || this.options.start); //Home key goes to min, if defined, else to start + if(e.keyCode == KEYS.END && this.options.max != undefined) this._setValue(this.options.max); //End key goes to maximum + return (e.keyCode == KEYS.TAB || e.keyCode == KEYS.BACKSPACE || + e.keyCode == KEYS.LEFT || e.keyCode == KEYS.RIGHT || e.keyCode == KEYS.PERIOD || + (/[0-9\-\.]/).test(String.fromCharCode(e.keyCode))) ? true : false; }, _mousewheel: function(e, delta) { delta = ($.browser.opera ? -delta / Math.abs(delta) : delta); delta > 0 ? this._up(e) : this._down(e); e.preventDefault(); }, + _getValue: function() { + return parseFloat(this.element[0].value.replace(/[^0-9\-\.]/g, '')); + }, + _setValue: function(newVal) { + if(isNaN(newVal)) newVal = this.options.start; + this.element[0].value = ( + this.options.currency ? + $.ui.spinner.format.currency(newVal, this.options.currency) : + $.ui.spinner.format.number(newVal, this.options._decimals) + ); + }, + + + plugins: {}, ui: function(e) { return { instance: this, options: this.options, - element: this.element + element: this.element, + value: this._getValue() }; }, _propagate: function(n,e) { $.ui.plugin.call(this, n, [e, this.ui()]); - return this.element.triggerHandler(n == "spin" ? n : "spin"+n, [e, this.ui()], this.options[n]); + return this.element.triggerHandler(n == 'spin' ? n : 'spin'+n, [e, this.ui()], this.options[n]); }, destroy: function() { if(!$.data(this.element[0], 'spinner')) return; + this.element - .removeClass("ui-spinner ui-spinner-disabled") - .removeData("spinner") - .unbind(".spinner"); + .removeClass('ui-spinner-box') + .removeAttr('disabled') + .removeData('spinner') + .unbind('.spinner') + .siblings() + .remove() + .end() + .parent() + .removeClass('ui-spinner ui-spinner-disabled') + .before(this.element.clone()) + .remove() + .end(); + if ($.fn.mousewheel) { this.element.unmousewheel(); } + }, + enable: function() { + this.element + .removeAttr('disabled') + .siblings() + .removeAttr('disabled') + .parent() + .removeClass('ui-spinner-disabled'); + this.disabled = false; + }, + disable: function() { + this.element + .attr('disabled', true) + .siblings() + .attr('disabled', true) + .parent() + .addClass('ui-spinner-disabled'); + this.disabled = true; } }); -$.ui.spinner.defaults = { - stepping: 1, - start: 0, - incremental: true -}; +$.extend($.ui.spinner, { + defaults: { + stepping: 1, + start: 0, + incremental: true, + currency: false + }, + format: { + number: function(num, dec) { + return this.round(num, dec); + }, + currency: function(num, sym) { + return (num !== Math.abs(num) ? '-' : '') + sym + this.round(Math.abs(num), 2); + }, + round: function(num, dec) { + if (dec > 0) { + var s = num + ((num.toString().indexOf('.') == -1) ? '.' : '') + '0000000001'; + s = s.substr(0, s.indexOf('.')+1+dec); + } else { + var s = Math.round(num); + } + return s; + } + }, + keys: { + BACKSPACE: 8, + TAB: 9, + RETURN: 13, + ESC: 27, + SPACE: 32, + PAGEUP: 33, + PAGEDOWN: 34, + END: 35, + HOME: 36, + LEFT: 37, + UP: 38, + RIGHT: 39, + DOWN: 40, + DEL: 46, + COMMA: 188, + PERIOD: 190 + } +}); })(jQuery); -- 2.39.5