r2581 - Spinner: added autoHide option to hide spinner on blur/mouseout, disable spinner if input...

r2581 - Spinner: added autoHide option to hide spinner on blur/mouseout, disable spinner if input...


Author: btburnett3
Date: Sat May 16 14:28:55 2009
New Revision: 2581
Added:
branches/dev/spinner/demos/spinner/autohide.html
Modified:
branches/dev/spinner/demos/spinner/index.html
branches/dev/spinner/ui/ui.spinner.js
Log:
Spinner: added autoHide option to hide spinner on blur/mouseout, disable
spinner if input is disabled on create, added hover effects to buttons,
minor changes to improve minimization
Added: branches/dev/spinner/demos/spinner/autohide.html
==============================================================================
--- (empty file)
+++ branches/dev/spinner/demos/spinner/autohide.html    Sat May 16 14:28:55
2009
@@ -0,0 +1,54 @@
+<!doctype html>
+<html lang="en">
+<head>
+    <title>jQuery UI Spinner - Hiding</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() {
+        $("#s1, #s2").spinner({autoHide: true});
+        
+        $("#autohide").toggle(function() {
+            $("#s1, #s2").spinner("option", "autoHide", false);
+        }, function() {
+            $("#s1, #s2").spinner("option", "autoHide", true);
+        });
+        $("#hide").click(function() {
+            $("#s1, #s2").spinner("hideButtons");
+        });
+        $("#show").click(function() {
+            $("#s1, #s2").spinner("showButtons");
+        });
+    });
+    </script>
+</head>
+<body>
+
+<div class="demo">
+
+

<label for="s1">Select a value:</label>
+<input id="s1" name="value" />


+
+

<label for="s2">Select a value:</label>
+<input id="s2" name="value" />


+
+


+<button id="autohide" class="ui-button ui-state-default
ui-corner-all">Toggle auto hide</button>
+<button id="hide" class="ui-button ui-state-default ui-corner-all">Hide
buttons</button>
+<button id="show" class="ui-button ui-state-default ui-corner-all">Show
buttons</button>
+







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


+Hiding 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    Sat May 16 14:28:55 2009
@@ -14,6 +14,7 @@
            <li><a href="list.html">List</a></li>
            <li><a href="incremental.html">Incremental</a></li>
            <li><a href="mousewheel-disabled.html">Mousewheel Disabled</a></li>
+            <li><a href="autohide.html">Hiding</a></li>
        </ul>
    </div>
</body>
Modified: branches/dev/spinner/ui/ui.spinner.js
==============================================================================
--- branches/dev/spinner/ui/ui.spinner.js    (original)
+++ branches/dev/spinner/ui/ui.spinner.js    Sat May 16 14:28:55 2009
@@ -12,9 +12,22 @@
*/
(function($) {
+// shortcut constants
+var hover = 'ui-state-hover',
+    active= 'ui-state-active',
+    namespace = '.spinner';
+
$.widget('ui.spinner', {
    _init: function() {
-        this._trigger('init', null, this.ui(null));
+        var self = this;
+
+        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
+        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')) {
@@ -32,15 +45,14 @@
        }
        //Initialize needed constants
-        var self = this;
-        this.element
+        self.element
            .addClass('ui-spinner-box')
            .attr('autocomplete', 'off') // switch off autocomplete in opera
            .width(this.options.width);
-        this._setValue( isNaN(this._getValue()) ? this.options.start :
this._getValue() );
+        self._setValue( isNaN(self._getValue()) ? self.options.start :
self._getValue() );
        
-        this.element
+        var widget = self.element
        .wrap('<div>')
        .parent()
            .addClass('ui-spinner ui-widget ui-widget-content ui-corner-all')
@@ -51,38 +63,38 @@
            .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>')
            .find('.ui-spinner-up')
                .bind('mouseover', function(event) {
-                    $(this).addClass('ui-state-hover');
+                    $(this).addClass(hover);
                })
                .bind('mousedown', function(event) {
-                    $(this).addClass('ui-state-active');
+                    $(this).addClass(active);
                    if (!self.counter) {
                        self.counter = 1;
                    }
                    self._mousedown(100, '_up', event);
                })
                .bind('mouseup', function(event) {
-                    $(this).removeClass('ui-state-active');
+                    $(this).removeClass(active);
                    if (self.counter == 1) {
                        self._up(event);
                    }
                    self._mouseup(event);
                })
                .bind('mouseout', function(event) {
-                    $(this).removeClass('ui-state-active ui-state-hover');
+                    $(this).removeClass(active + ' ' + hover);
                    if (self.timer) {
                        self._mouseup(event);
                    }
                })
                // mousedown/mouseup capture first click, now handle second click
                .bind('dblclick', function(event) {
-                    $(this).removeClass('ui-state-active');
+                    $(this).removeClass(active);
                    self._up(event);
                    self._mouseup(event);
                })
-                .bind('keydown.spinner', function(event) {
+                .bind('keydown'+namespace, function(event) {
                    var KEYS = $.ui.keyCode;
                    if (event.keyCode == KEYS.SPACE || event.keyCode == KEYS.ENTER) {
-                        $(this).addClass('ui-state-active');
+                        $(this).addClass(active);
                        if (!self.counter) {
                            self.counter = 1;
                        }
@@ -93,8 +105,8 @@
                        self.element.focus();
                    }
                })
-                .bind('keyup.spinner', function(event) {
-                    $(this).removeClass('ui-state-active');
+                .bind('keyup'+namespace, function(event) {
+                    $(this).removeClass(active);
                    self.counter = 0;
                    self._trigger('change', event);
                })
@@ -102,38 +114,38 @@
            .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>')
            .find('.ui-spinner-down')
                .bind('mouseover', function(event) {
-                    $(this).addClass('ui-state-hover');
+                    $(this).addClass(hover);
                })
                .bind('mousedown', function(event) {
-                    $(this).addClass('ui-state-active');
+                    $(this).addClass(active);
                    if (!self.counter) {
                        self.counter = 1;
                    }
                    self._mousedown(100, '_down', event);
                })                
                .bind('mouseup', function(event) {
-                    $(this).removeClass('ui-state-active');
+                    $(this).removeClass(active);
                    if (self.counter == 1) {
                        self._down();
                    }
                    self._mouseup(event);
                })
                .bind('mouseout', function(event) {
-                    $(this).removeClass('ui-state-active ui-state-hover');
+                    $(this).removeClass(active + ' ' + hover);
                    if (self.timer) {
                        self._mouseup(event);
                    }
                })
                // mousedown/mouseup capture first click, now handle second click
                .bind('dblclick', function(event) {
-                    $(this).removeClass('ui-state-active');
+                    $(this).removeClass(active);
                    self._down(event);
                    self._mouseup(event);
                })
-                .bind('keydown.spinner', function(event) {
+                .bind('keydown'+namespace, function(event) {
                    var KEYS = $.ui.keyCode;
                    if (event.keyCode == KEYS.SPACE || event.keyCode == KEYS.ENTER) {
-                        $(this).addClass('ui-state-active');
+                        $(this).addClass(active);
                        if (!self.counter) {
                            self.counter = 1;
                        }
@@ -142,16 +154,34 @@
                        self.element.siblings('.ui-spinner-up').focus();
                    }
                })
-                .bind('keyup.spinner', function(event) {
-                    $(this).removeClass('ui-state-active');
+                .bind('keyup'+namespace, function(event) {
+                    $(this).removeClass(active);
                    self.counter = 0;
                    self._trigger('change', event);
                })
-            .end();
+            .end()
+            .hover(function() {
+                self.hovered = true;
+                if (self.options.autoHide && !self.focused)
+                    self._delay(self.showButtons, 100, 'hide');
+            }, function() {
+                self.hovered = false;
+                if (self.options.autoHide && !self.focused)
+                    self._delay(self.hideButtons, 100, 'hide');
+            });
+        
+        self.buttons = widget.find('button')
+            .hover(function() {
+                $(this).addClass(hover);
+            }, function() {
+                $(this).removeClass(hover);
+            });
+        if (self.options.autoHide)
+            self.hideButtons(true);
        // Give the spinner casing a unique id only if one exists in original
input
        // - this should aid targetted customisations if a page contains
multiple instances
-        this.element.attr('id', function(){
+        self.element.attr('id', function(){
            if (this.id) {
                $(this).parent().attr('id', this.id+'-ui-spinner');
            }
@@ -159,12 +189,12 @@
        // DataList: Set contraints for object length and step size.
        // Manipulate height of spinner.
-        this._items = this.element.children().length;
-        if (this._items > 1) {
-            var margins = this.element.outerHeight(true) -
this.element.outerHeight();
-            var height = this.element.outerHeight()/this._items + margins*2;
-            //var height = this.options.height;
-            this.element
+        self._items = self.element.children().length;
+        if (self._items > 1) {
+            var margins = self.element.outerHeight(true) -
self.element.outerHeight();
+            var height = self.element.outerHeight()/self._items + margins*2;
+            //var height = self.options.height;
+            self.element
            .addClass('ui-spinner-list')
            .height(height)
            .children()
@@ -175,29 +205,41 @@
            .parent()
                .height(height)
            .end();
-            this.options.stepping = 1;
-            this.options.min = 0;
-            this.options.max = this._items-1;
+            self.options.stepping = 1;
+            self.options.min = 0;
+            self.options.max = self._items-1;
        }
-        this.element
-        .bind('keydown.spinner', function(event) {
+        self.element
+        .bind('keydown'+namespace, function(event) {
            if (!self.counter) {
                self.counter = 1;
            }
            return self._keydown.call(self, event);
        })
-        .bind('keyup.spinner', function(event) {
+        .bind('keyup'+namespace, function(event) {
            self.counter = 0;
            self._trigger('change', event);
        })
-        .bind('blur.spinner', function(event) {
+        .bind('focus'+namespace, function() {
+            self.focused = true;
+            if (!self.hovered && self.options.autoHide)
+                self._delay(self.showButtons, 100, 'hide');
+        })
+        .bind('blur'+namespace, function(event) {
+            self.focused = false;
+            if (!self.hovered && self.options.autoHide)
+                self._delay(self.hideButtons, 100, 'hide');
            self._cleanUp();
        });
-        if ($.fn.mousewheel && this.options.mouseWheel) {
-            this.element.mousewheel(self._mousewheel);
+        if ($.fn.mousewheel && self.options.mouseWheel) {
+            self.element.mousewheel(self._mousewheel);
        }
+        
+        // disable spinner if element was already disabled
+        if (self.element.disabled)
+            self.disable();
    },
    _constrain: function() {
@@ -349,9 +391,38 @@
            this.element.append('<'+ wrapper +' class="ui-spinner-dyn">'+ html
+ '</'+ wrapper +'>');
        }
    },
+    // delays a function call, allowing only one at a time of the same type
+    _delay: function(callback, delay, type) {
+        type = type || 'a';
+
+        var self = this,
+            curDelay = self._curDelay[type] || {},
+            args = Array.prototype.slice.call(arguments, 3);
+        
+        // reassign in case it's a new delay
+        self._curDelay[type] = curDelay;
+        
+        if (curDelay.i) {
+            // don't do anything if resetting the same delay
+            if (curDelay.f === callback) return;
+            clearTimeout(curDelay.i);
+        }
+        
+        curDelay.f = callback;
+        curDelay.i = setTimeout(function() {
+            curDelay.i = 0;
+            curDelay.f.apply(self, args);
+        }, delay);
+    },
    _setData: function(key, value) {
        if ((key == 'mouseWheel') && (value != this.options.mouseWheel) &&
$.fn.mousewheel)
            this.element[value ? 'mousewheel' : 'unmousewheel'](this._mousewheel);
+        else if (key == 'autoHide') {
+            if (!value)
+                this.showButtons(true);
+            else if (!this.hovered && !this.focused)
+                this.hideButtons(true);                
+        }
        
        $.widget.prototype._setData.call(this, key, value);
    },
@@ -381,7 +452,7 @@
            .removeAttr('disabled')
            .removeAttr('autocomplete')
            .removeData('spinner')
-            .unbind('.spinner')
+            .unbind(namespace)
            .siblings()
                .remove()
            .end()
@@ -403,6 +474,7 @@
            .parent()
                .removeClass('ui-state-disabled');
        this.disabled = false;
+        return this;
    },
    disable: function() {
        this.element
@@ -412,6 +484,7 @@
            .parent()
                .addClass('ui-state-disabled');
        this.disabled = true;
+        return this;
    },
    value: function(newVal) {
        if (!arguments.length)
@@ -419,6 +492,24 @@
            
        this._setValue(newVal);
        return this;
+    },
+    showButtons: function(immediate) {
+        var buttons = this.buttons.stop();
+        if (immediate)
+            buttons.css('opacity', 1);
+        else
+            buttons.fadeTo('fast', 1);
+        return this;
+    },
+    
+    hideButtons: function(immediate) {
+        // also removeClass(hover) in case it was left despite losing mouse hover
+        var buttons = this.buttons.stop().removeClass(hover);
+        if (immediate)
+            buttons.css('opacity', 0);
+        else
+            buttons.fadeTo('fast', 0);
+        return this;
    }
});
@@ -437,7 +528,8 @@
        items: null,
        max: null,
        min: null,
-        width: 'auto'
+        width: 'auto',
+        autoHide: false
    },
    format: {
        currency: function(num, sym, group, pt) {