r2585 - Spinner: Added support for hexadecimal or other bases via a new radix option, added padLe...

r2585 - Spinner: Added support for hexadecimal or other bases via a new radix option, added padLe...


Author: btburnett3
Date: Sun May 17 15:05:21 2009
New Revision: 2585
Added:
branches/dev/spinner/demos/spinner/hexadecimal.html
Modified:
branches/dev/spinner/demos/spinner/index.html
branches/dev/spinner/ui/ui.spinner.js
Log:
Spinner: Added support for hexadecimal or other bases via a new radix
option, added padLength to left pad spinner with zeroes, added pageStep
option for page up/page down functionality, renamed options to reflect
HTML5 specifications
Added: branches/dev/spinner/demos/spinner/hexadecimal.html
==============================================================================
--- (empty file)
+++ branches/dev/spinner/demos/spinner/hexadecimal.html    Sun May 17 15:05:21
2009
@@ -0,0 +1,34 @@
+<!doctype html>
+<html lang="en">
+<head>
+    <title>jQuery UI Spinner - Hexadecimal</title>
+    <link type="text/css" href="../../themes/base/ui.all.css"
rel="stylesheet" />
+    <script type="text/javascript" src="../../jquery-1.3.2.js"></script>
+    <script type="text/javascript"
src="../../external/mousewheel/jquery.mousewheel.min.js"></script>
+    <script type="text/javascript" src="../../ui/ui.core.js"></script>
+    <script type="text/javascript" src="../../ui/ui.spinner.js"></script>
+    <link type="text/css" href="../demos.css" rel="stylesheet" />
+    <script type="text/javascript">
+    $(function() {
+        $("#hexadecimal").spinner({radix: 16, padLength: 6, precision: 2,
step: '0.01'});    
+    });
+    </script>
+</head>
+<body>
+
+<div class="demo">
+
+


+    <label for="hexadecimal">Hexadecimal spinner:</label>
+    <input id="hexadecimal" name="hexadecimal" value="0" />
+



+</div><!-- End demo -->
+
+<div class="demo-description">
+


+Example of a hexadecimal spinner.
+


+</div><!-- End demo-description -->
+
+</body>
+</html>
Modified: branches/dev/spinner/demos/spinner/index.html
==============================================================================
--- branches/dev/spinner/demos/spinner/index.html    (original)
+++ branches/dev/spinner/demos/spinner/index.html    Sun May 17 15:05:21 2009
@@ -10,6 +10,7 @@
        <ul>
            <li class="demo-config-on"><a href="default.html">Default
functionality</a></li>
            <li><a href="donation.html">Donation</a></li>
+            <li><a href="hexadecimal.html">Hexadecimal</a></li>
            <li><a href="latlong.html">Map</a></li>
            <li><a href="list.html">List</a></li>
            <li><a href="incremental.html">Incremental</a></li>
Modified: branches/dev/spinner/ui/ui.spinner.js
==============================================================================
--- branches/dev/spinner/ui/ui.spinner.js    (original)
+++ branches/dev/spinner/ui/ui.spinner.js    Sun May 17 15:05:21 2009
@@ -14,41 +14,67 @@
// shortcut constants
var hover = 'ui-state-hover',
-    active= 'ui-state-active',
+    active = 'ui-state-active',
    namespace = '.spinner';
$.widget('ui.spinner', {
    _init: function() {
-        var self = this;
-
+        var self = this,
+            validChars;
+                    
+        function parse(val) {
+            if ((val != null) && (typeof val == 'string'))
+                val = self._parseValue(val, self.options.radix,
self.options.groupSeparator, self.options.radixPoint);
+            return isNaN(val) ? null : val;
+        }
+    
        self._trigger('init', null, self.ui(null));
        
        // initialize variables
-        // this can't be initialized as part of the prototype because all
widgets would share the same object
+        // _curDelay can't be initialized as part of the prototype because all
widgets would share the same object
        self._curDelay = {};
        self.focused = false;
        self.hovered = false;
        // perform data bind on generic objects
-        if (this.options.items != null && typeof this.options.items[0]
== 'object' && !this.element.is('input')) {
+        if (self.options.items != null && typeof self.options.items[0]
== 'object' && !self.element.is('input')) {
            var data = this.options.items;
            for (var i=0; i<data.length; i++) {
-                this._addItem(data[i]);
+                self._addItem(data[i]);
            }
        }
-
-        // check for decimals in steppinng and set _decimals as internal
-        this._decimals = parseInt(this.options.decimals, 10);
-        if (this.options.stepping.toString().indexOf('.') != -1 &&
this._decimals == 0) {
-            var s = this.options.stepping.toString();
-            this._decimals = s.slice(s.indexOf('.')+1, s.length).length;
+        
+        // Create list of valid characters used to build regexs
+        validChars = '\\-\\' + self.options.radixPoint;
+        if (self.options.groupSeparator)
+            validChars += '\\' + self.options.groupSeparator;
+        if (self.options.radix < 10)
+            validChars += '0-' + self.options.radix;
+        else {
+            validChars += '0-9'
+            if (self.options.radix > 10)
+                validChars += 'a-' + String.fromCharCode('a'.charCodeAt(0) +
self.options.radix - 11);
+        }
+        self.validChars = validChars;
+
+        // Parse min, max, step, and pageStep for strings based on radix
+        self.options.min = parse(self.options.min);
+        self.options.max = parse(self.options.max);
+        self.options.step = parse(self.options.step);
+        self.options.pageStep = parse(self.options.pageStep);
+        
+        // check for precision in steppinng and set _precision as internal
+        self._precision = parseInt(self.options.precision, 10);
+        if (self.options.step.toString().indexOf('.') != -1 && self._precision
== 0) {
+            var s = self.options.step.toString();
+            self._precision = s.slice(s.indexOf('.')+1, s.length).length;
        }
-
+                
        //Initialize needed constants
        self.element
            .addClass('ui-spinner-box')
            .attr('autocomplete', 'off') // switch off autocomplete in opera
-            .width(this.options.width);
+            .width(self.options.width);
        self._setValue( isNaN(self._getValue()) ? self.options.start :
self._getValue() );
        
@@ -58,9 +84,9 @@
            .addClass('ui-spinner ui-widget ui-widget-content ui-corner-all')
            // check for IE    
            .css({
-                display: !$.support.opacity &&
$(this).css('display', 'inline-block') ? 'inline' : false
+                display: !$.support.opacity &&
$(self).css('display', 'inline-block') ? 'inline' : false
            })
-            .append('<button class="ui-spinner-up ui-state-default ui-corner-tr"
type="button"><span class="ui-icon
ui-icon-triangle-1-n">&#9650;</span></button>')
+            .append('<button class="ui-spinner-up ui-state-default ui-corner-tr"
tabindex="-1"><span class="ui-icon
ui-icon-triangle-1-n">&#9650;</span></button>')
            .find('.ui-spinner-up')
                .bind('mousedown', function(event) {
                    $(this).addClass(active);
@@ -72,37 +98,20 @@
                .bind('mouseup', function(event) {
                    $(this).removeClass(active);
                    if (self.counter == 1) {
-                        self._up(event);
+                        self._up(self.options.step, event);
                    }
                    self._mouseup(event);
                })
                // mousedown/mouseup capture first click, now handle second click
                .bind('dblclick', function(event) {
-                    $(this).removeClass(active);
-                    self._up(event);
-                    self._mouseup(event);
-                })
-                .bind('keydown'+namespace, function(event) {
-                    var KEYS = $.ui.keyCode;
-                    if (event.keyCode == KEYS.SPACE || event.keyCode == KEYS.ENTER) {
-                        $(this).addClass(active);
-                        if (!self.counter) {
-                            self.counter = 1;
-                        }
-                        self._up.call(self, event);
-                    } else if (event.keyCode == KEYS.DOWN || event.keyCode == KEYS.RIGHT)
{
-                        self.element.siblings('.ui-spinner-down').focus();
-                    } else if (event.keyCode == KEYS.LEFT) {
-                        self.element.focus();
+                    if ($.browser.msie) {
+                        $(this).removeClass(active);
+                        self._up(self.options.step, event);
+                        self._mouseup(event);
                    }
                })
-                .bind('keyup'+namespace, function(event) {
-                    $(this).removeClass(active);
-                    self.counter = 0;
-                    self._trigger('change', event);
-                })
            .end()
-            .append('<button class="ui-spinner-down ui-state-default ui-corner-br"
type="button"><span class="ui-icon
ui-icon-triangle-1-s">&#9660;</span></button>')
+            .append('<button class="ui-spinner-down ui-state-default ui-corner-br"
tabindex="-1"><span class="ui-icon
ui-icon-triangle-1-s">&#9660;</span></button>')
            .find('.ui-spinner-down')
                .bind('mousedown', function(event) {
                    $(this).addClass(active);
@@ -114,33 +123,18 @@
                .bind('mouseup', function(event) {
                    $(this).removeClass(active);
                    if (self.counter == 1) {
-                        self._down();
+                        self._down(self.options.step, event);
                    }
                    self._mouseup(event);
                })
-                // mousedown/mouseup capture first click, now handle second click
+                // mousedown/mouseup capture first click, now handle second click in IE
                .bind('dblclick', function(event) {
-                    $(this).removeClass(active);
-                    self._down(event);
-                    self._mouseup(event);
-                })
-                .bind('keydown'+namespace, function(event) {
-                    var KEYS = $.ui.keyCode;
-                    if (event.keyCode == KEYS.SPACE || event.keyCode == KEYS.ENTER) {
-                        $(this).addClass(active);
-                        if (!self.counter) {
-                            self.counter = 1;
-                        }
-                        self._down.call(self, event);
-                    } else if (event.keyCode == KEYS.UP || event.keyCode == KEYS.LEFT) {
-                        self.element.siblings('.ui-spinner-up').focus();
+                    if ($.browser.msie) {
+                        $(this).removeClass(active);
+                        self._down(self.options.step, event);
+                        self._mouseup(event);
                    }
                })
-                .bind('keyup'+namespace, function(event) {
-                    $(this).removeClass(active);
-                    self.counter = 0;
-                    self._trigger('change', event);
-                })
            .end()
            .hover(function() {
                self.hovered = true;
@@ -190,7 +184,7 @@
            .parent()
                .height(height)
            .end();
-            self.options.stepping = 1;
+            self.options.step = 1;
            self.options.min = 0;
            self.options.max = self._items-1;
        }
@@ -239,28 +233,31 @@
        this._setValue(this._getValue());
        this._constrain();
    },
-    _spin: function(d, event) {
+    _spin: function(d, step, event) {
        if (this.disabled) {
            return;
        }
+        
+        var value = this._getValue();
-        if (isNaN(this._getValue())) {
-            this._setValue(this.options.start);
+        if (isNaN(value)) {
+            value = 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._setValue(value + (d == 'up' ? 1:-1) *(this.options.incremental &&
this.counter > 100 ? (this.counter > 200 ? 100 : 10) : 1) * step);
        this._animate(d);
        this._constrain();
        if (this.counter) {
            this.counter++;
        }
+        
        this._trigger('spin', event);
    },
-    _down: function(event) {
-        this._spin('down', event);
+    _down: function(step, event) {
+        this._spin('down', step, event);
        this._trigger('down', event);
    },
-    _up: function(event) {
-        this._spin('up', event);
+    _up: function(step, event) {
+        this._spin('up', step, event);
        this._trigger('up', event);
    },
    _mousedown: function(i, d, event) {
@@ -271,7 +268,7 @@
            this.timer = 0;
        }
        this.timer = window.setInterval(function() {
-            self[d](event);
+            self[d](self.options.step, event);
            if (self.options.incremental && self.counter > 20) {
                self._mousedown(20, d, event);
            }
@@ -290,10 +287,16 @@
        var KEYS = $.ui.keyCode;
        if (event.keyCode == KEYS.UP) {
-            this._up(event);
+            this._up(this.options.step, event);
        }
        if (event.keyCode == KEYS.DOWN) {
-            this._down(event);
+            this._down(this.options.step, event);
+        }
+        if (event.keyCode == KEYS.PAGE_UP) {
+            this._up(this.options.pageStep, event);
+        }
+        if (event.keyCode == KEYS.PAGE_DOWN) {
+            this._down(this.options.pageStep, event);
        }
        if (event.keyCode == KEYS.HOME) {
            //Home key goes to min, if defined, else to start
@@ -307,7 +310,7 @@
            event.keyCode == KEYS.LEFT || event.keyCode == KEYS.RIGHT ||
event.keyCode == KEYS.PERIOD ||
            event.keyCode == KEYS.NUMPAD_DECIMAL || event.keyCode ==
KEYS.NUMPAD_SUBTRACT ||
            (event.keyCode >= 96 && event.keyCode <= 105) || // add support for
numeric keypad 0-9
-            (/[0-9\-\.]/).test(String.fromCharCode(event.keyCode))) ? true : false;
+            (new RegExp('[' + this.validChars
+ ']', 'i').test(String.fromCharCode(event.keyCode)))) ? true : false;
    },
    _mousewheel: function(event, delta) {
        // this = element, not widget, in event call
@@ -315,7 +318,7 @@
        var self = $.data(this, 'spinner');
        
        delta = ($.browser.opera ? -delta / Math.abs(delta) : delta);
-        (delta > 0 ? self._up(event) : self._down(event));
+        self[delta > 0 ? '_up' : '_down'](self.options.step, event);
        if (self.timeout) {
            window.clearTimeout(self.timeout);
            self.timeout = 0;
@@ -323,12 +326,23 @@
        self.timeout = window.setTimeout(function(){self._trigger('change',
event);}, 400);
        event.preventDefault();
    },
+    _parseValue: function(val, radix, groupSeparator, radixPoint) {
+        // Because groupSeparator is included in the regex, we must replace it
independently
+        if (groupSeparator)
+            val = val.replace(groupSeparator, '');
+            
+        val = val.replace(new RegExp('[^' + this.validChars
+ ']', 'gi'), '').split(radixPoint);
+        
+        result = parseInt(val[0], radix);
+        if (val.length > 1)
+            result += parseInt(val[1], radix) / Math.pow(radix, val[1].length) *
+                // must test first character of val[0] for minus sign because -0 is
parsed as 0 in result
+                (val[0][0] == '-' ? -1 : 1);
+        
+        return result;
+    },
    _getValue: function() {
-        var val = this.element.val().replace(this.options.point, '.');
-        if (this.options.group === '.') {
-            val = val.replace('.','');
-        }
-        return parseFloat(val.replace(/[^0-9\-\.]/g, ''));
+        return this._parseValue(this.element.val(), this.options.radix,
this.options.groupSeparator, this.options.radixPoint);
    },
    _setValue: function(newVal) {
        if (isNaN(newVal)) {
@@ -336,8 +350,8 @@
        }
        this.element.val(
            this.options.currency ?
-                $.ui.spinner.format.currency(newVal, this.options.currency,
this.options.group, this.options.point) :
-                $.ui.spinner.format.number(newVal, this._decimals, this.options.group,
this.options.point)
+                $.ui.spinner.format.currency(newVal, this.options.currency,
this.options.groupSeparator, this.options.radixPoint) :
+                $.ui.spinner.format.number(newVal, this._precision,
this.options.radix, this.options.groupSeparator, this.options.radixPoint,
this.options.padLength)
        );
    },
    _animate: function(d) {
@@ -501,18 +515,25 @@
$.extend($.ui.spinner, {
    version: "@VERSION",
    defaults: {
-        decimals: 0,
-        stepping: 1,
+        precision: 0,
+        radix: 10,
+        padLength: 0,
+        max: null,
+        min: null,
+        
+        step: 1,
+        pageStep: 5,
        start: 0,
+
        incremental: true,
        mouseWheel: true,
        currency: false,
+        
        format: '%',
-        group: '',
-        point: '.',
+        groupSeparator: '',
+        radixPoint: '.',
+        
        items: null,
-        max: null,
-        min: null,
        width: 'auto',
        autoHide: false
    },
@@ -521,10 +542,23 @@
            num = isNaN(num) ? 0 : num;
            return (num !== Math.abs(num) ? '-' : '') + sym +
this.number(Math.abs(num), 2, group || ',', pt);
        },
-        number: function(num, dec, group, pt) {
-            var regex = /(\d+)(\d{3})/;
-            for (num = isNaN(num) ? 0 : parseFloat(num,10).toFixed(dec), num =
num.replace('.', pt); regex.test(num) && group;
num=num.replace(regex, '$1'+group+'$2'));
-            return num;
+        number: function(num, dec, radix, group, pt, padLength) {
+            var whole = Math.floor(Math.abs(num)),
+                result = whole.toString(radix),
+                part = Math.floor(((Math.abs(num) - whole) * Math.pow(radix,
dec))).toString(radix),
+                regex = /(\d+)(\d{3})/;
+            
+            while (regex.test(result) && group)
result=result.replace(regex, '$1'+group+'$2');
+            
+            if (dec > 0) {
+                while (part.length < dec) part = '0' + part;
+                result += pt + part;
+            }
+            
+            while (padLength && (result.length < padLength))
+                result = '0' + result;
+                        
+            return (num < 0 ? '-' : '') + result;
        }
    }
});