r3098 committed - dev selectables: huge number of bugfixes on event triggering, now reli...

r3098 committed - dev selectables: huge number of bugfixes on event triggering, now reli...


Revision: 3098
Author: paul.bakaus
Date: Thu Aug 20 20:45:48 2009
Log: dev selectables: huge number of bugfixes on event triggering, now
reliably triggers deselect events (renamed from unselect). Added change
event that triggers after something has changed, in the case of the lasso,
after button release. Fixed CTRL focus bug with new closest selection.
Added removed+added as delta to the stop event, effectively replacing the
ui-selecting feature completely
http://code.google.com/p/jquery-ui/source/detail?r=3098
Modified:
/branches/dev/selectable/demos/selectable/display-grid.html
/branches/dev/selectable/demos/selectable/serialize.html
/branches/dev/selectable/ui/ui.selectable.js
=======================================
--- /branches/dev/selectable/demos/selectable/display-grid.html    Thu Aug 13
05:07:27 2009
+++ /branches/dev/selectable/demos/selectable/display-grid.html    Thu Aug 20
20:45:48 2009
@@ -10,10 +10,28 @@
    <style type="text/css">
    #feedback { font-size: 1.4em; }
-    #selectable .ui-focused { background: #FECA40; }
-    #selectable .ui-state-selected { background: #F39814; color: white; }
-    #selectable { list-style-type: none; margin: 0; padding: 0; width:400px; }
-    #selectable li { margin: 3px; padding: 1px; float: left; width: 100px;
height: 80px; font-size: 4em; text-align: center; }
+
+    .ui-selectable-lasso {
+        border: 1px solid rgb(50, 50, 50);
+        background: rgba(100, 100, 100, 0.2);
+    }
+
+    #selectable .ui-focused {
+        background: #FECA40;
+    }
+
+    #selectable .ui-state-selected {
+        background: #F39814; color: white;
+    }
+
+    #selectable {
+        list-style-type: none; margin: 0; padding: 0; width:400px;
+    }
+
+    #selectable li {
+        margin: 3px; padding: 1px; float: left; width: 100px; height: 80px;
font-size: 4em; text-align: center;
+    }
+
    </style>
    <script type="text/javascript">
    $(function() {
@@ -24,7 +42,7 @@
<body>
<div class="demo">
-<ol id="selectable" tabindex="1">
+<ol id="selectable" tabindex="1" class="ui-helper-clearfix">
    <li class="ui-state-default">1</li>
    <li class="ui-state-default">2</li>
    <li class="ui-state-default">3</li>
=======================================
--- /branches/dev/selectable/demos/selectable/serialize.html    Thu Aug 13
05:07:27 2009
+++ /branches/dev/selectable/demos/selectable/serialize.html    Thu Aug 20
20:45:48 2009
@@ -10,6 +10,10 @@
    <style type="text/css">
    #feedback { font-size: 1.4em; }
+    .ui-selectable-lasso {
+        border: 1px solid rgb(50, 50, 50);
+        background: rgba(100, 100, 100, 0.2);
+    }
    #selectable .ui-focused { background: #FECA40; }
    #selectable .ui-state-selected { background: #F39814; color: white; }
    #selectable { list-style-type: none; margin: 0; padding: 0; width: 60%; }
@@ -18,12 +22,21 @@
    <script type="text/javascript">
    $(function() {
        $("#selectable").selectable({
-            stop: function(){
+
+            stop: function(e, ui) {
+                //console.log('added: ', ui.added ? ui.added.length : 0);
+                //console.log('removed: ', ui.removed ? ui.removed.length : 0);
+            },
+
+            change: function(e, ui){
+
+                //console.log('change triggered:', ui.selection);
+
                var result = $("#select-result").empty();
-                var items = $("#selectable li");
-                items.filter(".ui-state-selected").each(function(){
-                    result.append(" #" + (items.index(this) + 1));
+                ui.selection.each(function(){
+                    result.append(" #" + ($('#selectable li').index(this) + 1));
                });
+
            }
        });
    });
@@ -49,7 +62,7 @@
<div class="demo-description">
-

Write a function that fires on the <code>stop</code> event to collect
the index values of selected items. Present values as feedback, or pass as
a data string.


+

Write a function that fires on the <code>change</code> event to collect
the index values of selected items. Present values as feedback, or pass as
a data string.


</div><!-- End demo-description -->
</body>
=======================================
--- /branches/dev/selectable/ui/ui.selectable.js    Thu Aug 13 05:07:27 2009
+++ /branches/dev/selectable/ui/ui.selectable.js    Thu Aug 20 20:45:48 2009
@@ -117,9 +117,10 @@
            var current = [/(down|right)/.test(direction) ? 10000 : -10000, null],
                overlap = 10000,
-                selfOffset = this.currentFocus.data('selectable-item');
-
-            $(this.options.filter,
this.element).not(this.currentFocus).filter(':visible').each(function() {
+                selfOffset = this.currentFocus.data('selectable-item'),
+                items = $(this.options.filter, this.element);
+
+            items.not(this.currentFocus).filter(':visible').each(function() {
                var $this = $(this),
                    offset = $this.data('selectable-item'),
@@ -162,7 +163,16 @@
            });
-            return current[1] ? this._select(event, current[1]) : false;
+            // if nothing close is found, bail
+            if(!current[1])
+                return false;
+
+            //We need to find the index of the current, and the index of the new
one to call selectAdjacent
+            // - calling _select doesn't work, since it's only for mouse
interaction (no ctrl focus move!)
+            var currentIndex = items.index(this.currentFocus[0]);
+            var newIndex = items.index(current[1]);
+
+            return this._selectAdjacent(event, newIndex - currentIndex);
        },
@@ -193,6 +203,9 @@
            //Trigger start event
            this._trigger("start", event, this._uiHash());
+
+            //Save the current selection as previous
+            this._previousSelection = this._selection.slice();
            // append and position helper (lasso)
            $('body').append(this.helper);
@@ -206,12 +219,12 @@
            });
            //Tell the intersection that some start selected
-            this.items.filter('.'+this.options.selectedClass).each(function() {
+            for (var i = this._selection.length - 1; i >= 0; i--){
                if(event.metaKey) {
-                    if(this != self.clickedOnItem)
$.data(this, "selectable-item").startSelected = true;
-                } else self._removeFromSelection($(this), event);
-            });
-
+                    if(this != self.clickedOnItem)
$(this._selection[i]).data("selectable-item").startSelected = true;
+                } else self._removeFromSelection($(this._selection[i]), event);
+            };
+
        },
        _mouseDrag: function(event) {
@@ -254,7 +267,41 @@
        },
        _mouseStop: function(event) {
-            this._trigger("stop", event, this._uiHash());
+
+            var newlySelected = [],
+                newlyDeselected = [];
+
+            // Find out the delta of the newly selected items
+            for (var i=0; i < this._selection.length; i++) {
+                var wasAlreadyPartOfPreviousSelection = false;
+                for (var j=0; j < this._previousSelection.length; j++) {
+                    if(this._selection[i][0] == this._previousSelection[j][0])
+                        wasAlreadyPartOfPreviousSelection = true;
+                };
+                if(!wasAlreadyPartOfPreviousSelection)
newlySelected.push(this._selection[i]);
+            };
+
+            // Find out the delta of the newly unselected items
+            for (var i = this._previousSelection.length - 1; i >= 0; i--){
+                if(!this._previousSelection[i].data('selectable-item').selected)
newlyDeselected.push(this._previousSelection[i]);
+            };
+
+
+            // Transform both deltas into jQuery objects
+            newlySelected = $($.map(newlySelected, function(i) { return i[0]; }));
+            newlyDeselected = $($.map(newlyDeselected, function(i) { return i[0];
}));
+
+            var uiHash = $.extend(this._uiHash(), {
+                added: newlySelected || [],
+                removed: newlyDeselected || []
+            });
+            this._trigger("stop", event, uiHash);
+
+            // Trigger change event if anything has changed
+            if((newlySelected && newlySelected.length) || (newlyDeselected &&
newlyDeselected.length)) {
+                this._trigger('change', event, uiHash);
+            }
+
            this.helper.remove();
            return false;
        },
@@ -266,6 +313,42 @@
        _selection: [],
+        _endSelection: function(event, newlySelected) {
+
+            //Only trigger the 'deselect' event if items have been removed from the
selection
+            var newlyDeselected = this._triggerDeselection(event);
+
+            //Only trigger 'select' event if items have been added to the selection
+            if(newlySelected && newlySelected.length)
+                this._trigger('select', event, this._uiHash(newlySelected, 'added'));
+
+            // Trigger change event if anything has changed
+            if((newlySelected && newlySelected.length) || (newlyDeselected &&
newlyDeselected.length)) {
+                var uiHash = $.extend(this._uiHash(), {
+                    added: newlySelected || [],
+                    removed: newlyDeselected || []
+                });
+                this._trigger('change', event, uiHash);
+            }
+
+        },
+
+        _triggerDeselection: function(event) {
+
+            var triggerItems = [];
+
+            for (var i = this._previousSelection.length - 1; i >= 0; i--){
+                if(!this._previousSelection[i].data('selectable-item').selected)
triggerItems.push(this._previousSelection[i]);
+            };
+
+            this._previousSelection = [];
+            triggerItems = $($.map(triggerItems, function(i) { return i[0]; }));
+            if(triggerItems.length) this._trigger('deselect', event,
this._uiHash(triggerItems, 'removed'));
+
+            return triggerItems;
+
+        },
+
        _clearSelection: function(triggerEvent) {
            var triggerItems = [];
@@ -276,13 +359,16 @@
                this._selection[i].data('selectable-item').selected = false;
            };
+            this._previousSelection = this._selection.slice();
            this._selection = [];
-            if(triggerEvent) this._trigger('unselect', triggerEvent,
this._uiHash($($.map(triggerItems, function(i) { return i[0];
})), 'removed'));
+            if(triggerEvent && triggerItems.length) this._trigger('deselect',
triggerEvent, this._uiHash($($.map(triggerItems, function(i) { return i[0];
})), 'removed'));
        },
        _toggleSelection: function(item, event) {
-                item.data('selectable-item').selected ?
this._removeFromSelection(item, event) : this._addToSelection(item);
+            var selected = item.data('selectable-item').selected;
+            selected ? this._removeFromSelection(item, event) :
this._addToSelection(item);
+            return !selected;
        },
        _addToSelection: function(item, triggerEvent) {
@@ -310,7 +396,7 @@
                    this._selection[i].removeClass(this.options.selectedClass);
                    this._selection[i].data('selectable-item').selected = false;
                    this._selection.splice(i,1);
-                    if(triggerEvent) this._trigger('unselect', triggerEvent,
this._uiHash($(item), 'removed'));
+                    if(triggerEvent) this._trigger('deselect', triggerEvent,
this._uiHash($(item), 'removed'));
                    break;
                }
            };
@@ -324,7 +410,7 @@
            if (event.shiftKey && this.options.multiple) {
                //Clear the previous selection to make room for a shift selection
-                this._clearSelection(event);
+                this._clearSelection();
                var index = this.items.index(this.latestWithoutModifier[0]) >
this.items.index(this.currentFocus[0]) ? -1 : 1;
                var i = this.latestWithoutModifier.data('selectable-item').selected ?
this.items.eq(this.items.index(this.latestWithoutModifier[0])+index) :
this.latestWithoutModifier;
@@ -339,9 +425,10 @@
            } else {
                if (event.metaKey) {
-                    this._toggleSelection(this.currentFocus, event);
+                    var withMetaIsNewlySelected =
this._toggleSelection(this.currentFocus, event);
+                    if(withMetaIsNewlySelected) newlySelected.push(this.currentFocus);
                } else {
-                    this._clearSelection(event);
+                    this._clearSelection();
                    newlySelected.push(this._addToSelection(this.currentFocus));
                    this.latestWithoutModifier = this.currentFocus;
                }
@@ -403,8 +490,8 @@
            //Set and update the selection
            var newlySelected = this._updateSelectionMouse(event);
-            //Trigger select event
-            if(newlySelected && newlySelected.length) this._trigger('select',
event, this._uiHash(newlySelected, 'added'));
+            // Ending the selection does a diff and then triggers appropriate events
+            this._endSelection(event, newlySelected);
        },
@@ -423,10 +510,11 @@
            this.currentFocus.addClass('ui-focused');
            //Set and update the selection
+            this._previousSelection = this._selection.slice();
            var newlySelected = this._updateSelection(event, index);
-            //Trigger select event
-            if(newlySelected && newlySelected.length) this._trigger('select',
event, this._uiHash(newlySelected, 'added'));
+            // Ending the selection does a diff and then triggers appropriate events
+            this._endSelection(event, newlySelected);
        },