diff options
author | Tim Almdal <tnalmdal@shaw.ca> | 2009-09-11 09:41:01 -0700 |
---|---|---|
committer | Tim Almdal <tnalmdal@shaw.ca> | 2009-09-11 09:41:01 -0700 |
commit | afc1de54b1119680c144f474f1c587f3c69b6813 (patch) | |
tree | fd849488df52db27eb160544907d8553075470d8 /lib | |
parent | 3ac3d1e520f95cc859d990000561dbb727411e0c (diff) |
Upgraded to the latest svn version of selectable and applied local fixes to address ticket #696
Diffstat (limited to 'lib')
-rw-r--r-- | lib/jquery-ui.js | 410 |
1 files changed, 284 insertions, 126 deletions
diff --git a/lib/jquery-ui.js b/lib/jquery-ui.js index 4939d009..85e457a0 100644 --- a/lib/jquery-ui.js +++ b/lib/jquery-ui.js @@ -159,87 +159,98 @@ jQuery.effects||(function(d){d.effects={version:"1.7.2",save:function(g,h){for(v var self = this; this.items = $(this.options.filter, this.element); - this.element.addClass("ui-selectable"); + this.element.addClass("ui-selectable ui-widget"); //Set the currentFocus to the first item - this.currentFocus = this.items.eq(0); - + this.currentFocus = this.items.eq(0).attr('tabindex', 0); + //Refresh item positions - this.refresh(); + this.refresh(1); //Disable text selection this.element.disableSelection(); //Prepare caret selection - if(this.options.lasso) this._mouseInit(); + if(this.options.lasso) { + + // we need to move the lasso options onto the root options for the mouse clas + if(this.options.lasso !== true) { + $.extend(this.options, this.options.lasso); + } + + this._mouseInit(); + } this.element .bind('mousedown.selectable', function(event) { + if(self.options.disabled) + return; + var item = self._targetIsItem(event.target); if (!item) return; - + // If item is part of current selection and current // selection is multiple, return and allow mouseup // to fire (Windows gets this right too, OSX doesn't) - if(self._selection.length > 1 && $(item).hasClass(self.options.selectedClass)) { + if(self._selection.length > 1 && $(item).hasClass('ui-selected')) { return (self._listenForMouseUp = 1); } - + if(self._trigger('beforeselect', event) === false) return true; self._select(event, item); self.element[0].focus(); event.preventDefault(); - + }) .bind('mouseup.selectable', function(event) { if(self._listenForMouseUp) { - + self._listenForMouseUp = 0; var item = self._targetIsItem(event.target); if (!item) return; - + if(self._trigger('beforeselect', event) === false) return true; - + self._select(event, item); self.element[0].focus(); event.preventDefault(); } }) .bind('focus.selectable', function() { - self.currentFocus.addClass('ui-focused'); + if(!self.options.disabled) self.currentFocus.addClass('ui-state-focus'); }) .bind('blur.selectable', function() { - self.currentFocus.removeClass('ui-focused'); + if(!self.options.disabled) self.currentFocus.removeClass('ui-state-focus'); }) .bind('keydown.selectable', function(event) { - if(!self.options.keyboard) + if(!self.options.keyboard || self.options.disabled) return; - + if(self._trigger('beforeselect', event) === false) return true; - + if(event.keyCode == $.ui.keyCode.DOWN) { - self.options.smart ? self.selectClosest('down', event) : self.selectNext(event); + self.options.closest ? self.selectClosest('down', event) : self.next(event); event.preventDefault(); } - + if(event.keyCode == $.ui.keyCode.RIGHT) { - self.options.smart ? self.selectClosest('right', event) : self.selectNext(event); + self.options.closest ? self.selectClosest('right', event) : self.next(event); event.preventDefault(); } - + if(event.keyCode == $.ui.keyCode.UP) { - self.options.smart ? self.selectClosest('up', event) : self.selectPrevious(event); + self.options.closest ? self.selectClosest('up', event) : self.previous(event); event.preventDefault(); } - + if(event.keyCode == $.ui.keyCode.LEFT) { - self.options.smart ? self.selectClosest('left', event) : self.selectPrevious(event); + self.options.closest ? self.selectClosest('left', event) : self.previous(event); event.preventDefault(); } @@ -253,14 +264,14 @@ jQuery.effects||(function(d){d.effects={version:"1.7.2",save:function(g,h){for(v .addClass("ui-selectable-lasso"); }, - + selectClosest: function(direction, event) { - + 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() { + this.items.not(this.currentFocus).filter(':visible').each(function() { var $this = $(this), offset = $this.data('selectable-item'), @@ -270,71 +281,84 @@ jQuery.effects||(function(d){d.effects={version:"1.7.2",save:function(g,h){for(v }; switch(direction) { - + case 'up': if((selfOffset.top > offset.top && offset.top >= current[0]) && (offset.top != current[0] || distance.x < overlap)) { current = [offset.top, $this]; overlap = distance.x; } break; - + case 'down': if((selfOffset.top < offset.top && offset.top <= current[0]) && (offset.top != current[0] || distance.x < overlap)) { current = [offset.top, $this]; overlap = distance.x; } break; - + case 'left': if((selfOffset.left > offset.left && offset.left >= current[0]) && (offset.left != current[0] || distance.y < overlap)) { current = [offset.left, $this]; overlap = distance.y; } break; - + case 'right': if((selfOffset.left < offset.left && offset.left <= current[0]) && (offset.left != current[0] || distance.y < overlap)) { current = [offset.left, $this]; overlap = distance.y; } break; - + } }); - - 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 = this.items.index(this.currentFocus[0]); + var newIndex = this.items.index(current[1]); + + return this._selectAdjacent(event, newIndex - currentIndex); + }, destroy: function() { + this.items.removeClass("ui-selectable-item ui-selected ui-state-active"); this.element - .removeClass("ui-selectable ui-selectable-disabled") + .removeClass("ui-selectable ui-selectable-disabled ui-widget") .removeData("selectable") .unbind(".selectable"); this._mouseDestroy(); }, - + _mouseCapture: function(event) { //If the item we start dragging on is a selectable, we bail (if keyboard is used) this.clickedOnItem = this._targetIsItem(event.target); - return !this.options.keyboard || !this.clickedOnItem; + return true; // TODO: this starts the lasso on items as well - we might want to introduce an option to disable this }, - + _mouseStart: function(event) { - + var self = this, o = this.options; this.opos = [event.pageX, event.pageY]; - + if (o.disabled) return; - + //Cache positions - this.refresh(); - + this.refresh(1); + //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); this.helper.css({ @@ -347,36 +371,36 @@ jQuery.effects||(function(d){d.effects={version:"1.7.2",save:function(g,h){for(v }); //Tell the intersection that some start selected - this.items.filter('.'+this.options.selectedClass).each(function() { - if(event.metaKey) { - if(this != self.clickedOnItem) $.data(this, "selectable-item").startSelected = true; - } else self._removeFromSelection($(this), event); - }); - + for (var i = this._selection.length - 1; i >= 0; i--){ + if(event.metaKey || event.ctrlKey) { + if(this != self.clickedOnItem) $(this._selection[i]).data("selectable-item").startSelected = true; + } else self._removeFromSelection($(this._selection[i]), event); + }; + }, - + _mouseDrag: function(event) { - + var self = this, o = this.options; - + if (o.disabled) return; - + //Do the lasso magic var x1 = this.opos[0], y1 = this.opos[1], x2 = event.pageX, y2 = event.pageY; if (x1 > x2) { var tmp = x2; x2 = x1; x1 = tmp; } if (y1 > y2) { var tmp = y2; y2 = y1; y1 = tmp; } this.helper.css({left: x1, top: y1, width: x2-x1, height: y2-y1}); - + //Loop through all items and check overlaps this.items.each(function() { - + var item = $.data(this, "selectable-item"); - + //prevent helper from being selected if appendTo: selectable if (!item || item.element == self.element[0]) return; - + var hit = false; if (o.lasso && o.lasso.tolerance == 'touch') { hit = ( !(item.left > x2 || item.right < x1 || item.top > y2 || item.bottom < y1) ); @@ -387,43 +411,119 @@ jQuery.effects||(function(d){d.effects={version:"1.7.2",save:function(g,h){for(v hit ? item.startSelected ? self._removeFromSelection($(this), event) : self._addToSelection($(this), event) : !item.startSelected ? self._removeFromSelection($(this), event) : self._addToSelection($(this), event); - + }); - + return false; - + }, - + _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; - }, - + }, + _targetIsItem: function(item) { var found = $(item).parents().andSelf().filter(':data(selectable-item)'); return found.length && found; - }, + }, _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--){ + var data = this._previousSelection[i].data('selectable-item'); + if(!data || !data.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 = []; for (var i = this._selection.length - 1; i >= 0; i--){ - if(triggerEvent && this._selection[i].data('selectable-item').selected) triggerItems.push(this._selection[i]); - this._selection[i].removeClass(this.options.selectedClass); - this._selection[i].data('selectable-item').selected = false; + var data = this._selection[i].data('selectable-item'); + if(triggerEvent && data && data.selected) triggerItems.push(this._selection[i]); + this._selection[i].removeClass('ui-selected ui-state-active'); + if (data) + data.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) { @@ -433,13 +533,13 @@ jQuery.effects||(function(d){d.effects={version:"1.7.2",save:function(g,h){for(v this._selection.push(item); this.latestSelection = item; - item.addClass(this.options.selectedClass); + item.addClass('ui-selected ui-state-active'); item.data('selectable-item').selected = true; - + if(triggerEvent) { this._trigger('select', triggerEvent, $.extend({ lasso: true }, this._uiHash(item))); } - + return item; }, @@ -448,10 +548,14 @@ jQuery.effects||(function(d){d.effects={version:"1.7.2",save:function(g,h){for(v for (var i=0; i < this._selection.length; i++) { if (this._selection[i][0] == item[0]) { - this._selection[i].removeClass(this.options.selectedClass); - this._selection[i].data('selectable-item').selected = false; + this._selection[i].removeClass('ui-selected ui-state-active'); + var data = this._selection[i].data('selectable-item'); + if (data) { + data.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; } }; @@ -462,10 +566,10 @@ jQuery.effects||(function(d){d.effects={version:"1.7.2",save:function(g,h){for(v var newlySelected = []; - if (event.shiftKey && this.options.multiple) { + if (event && event.shiftKey) { //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; @@ -479,16 +583,17 @@ jQuery.effects||(function(d){d.effects={version:"1.7.2",save:function(g,h){for(v } else { - if (event.metaKey) { - this._toggleSelection(this.currentFocus, event); + if (event && (event.metaKey || event.ctrlKey)) { + 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; } } - + return $($.map(newlySelected, function(i) { return i[0]; })); }, @@ -497,7 +602,7 @@ jQuery.effects||(function(d){d.effects={version:"1.7.2",save:function(g,h){for(v var newlySelected = []; - if (event.shiftKey && this.options.multiple) { + if (event && event.shiftKey) { if (this.currentFocus.data('selectable-item').selected) { this._removeFromSelection(this.previousFocus, event); @@ -519,7 +624,7 @@ jQuery.effects||(function(d){d.effects={version:"1.7.2",save:function(g,h){for(v } else { //If the CTRL or Apple/Win key is pressed, only set focus - if (event.metaKey) + if (event && (event.metaKey || event.ctrlKey)) return; this._clearSelection(event); @@ -527,84 +632,148 @@ jQuery.effects||(function(d){d.effects={version:"1.7.2",save:function(g,h){for(v this.latestWithoutModifier = this.currentFocus; } - - return $($.map(newlySelected, function(i) { return i[0]; })); + + return $($.map(newlySelected, function(i) { if(i) return i[0]; })); }, _select: function(event, item) { //Set the current selection to the previous/next item - this.previousFocus = this.currentFocus; - this.currentFocus = $(item); + this.previousFocus = this.currentFocus.attr('tabindex', -1); + this.currentFocus = $(item).attr('tabindex', 0); - this.previousFocus.removeClass('ui-focused'); - this.currentFocus.addClass('ui-focused'); + this.previousFocus.removeClass('ui-state-focus'); + this.currentFocus.addClass('ui-state-focus'); //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); }, _selectAdjacent: function(event, index) { var item = this.items.eq(this.items.index(this.currentFocus[0]) + index); - + //Bail if there's no previous/next item if (!item.length) return; //Set the current selection to the previous/next item - this.previousFocus = this.currentFocus; - this.currentFocus = item; + this.previousFocus = this.currentFocus.attr('tabindex', -1); + this.currentFocus = item.attr('tabindex', 0); - this.previousFocus.removeClass('ui-focused'); - this.currentFocus.addClass('ui-focused'); + this.previousFocus.removeClass('ui-state-focus'); + this.currentFocus.addClass('ui-state-focus'); //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); }, - selectPrevious: function(event) { + previous: function(event) { this._selectAdjacent(event, -1); }, - selectNext: function(event) { + next: function(event) { this._selectAdjacent(event, 1); }, - - refresh: function() { - var o = this.options; + refresh: function(fromInside) { + + var o = this.options, self = this; this.items = $(o.filter, this.element); + this.items.addClass('ui-selectable-item'); this.items.each(function() { + var $this = $(this); var pos = $this.offset(); + + if(self.currentFocus && self.currentFocus[0] != this) + $this.attr('tabindex', -1); + $.data(this, "selectable-item", { left: pos.left, top: pos.top, right: pos.left + $this.width(), bottom: pos.top + $this.height(), startSelected: false, - selected: $this.hasClass(o.selectedClass) + selected: $this.hasClass('ui-selected') }); }); + + if(!fromInside) { + this._previousSelection = this._selection.slice(); + this._selection = []; + for (var i=0; i < this._previousSelection.length; i++) { + if(this._previousSelection[i][0].parentNode) this._selection.push(this._previousSelection[i]); + }; + + this._endSelection(); + } + + }, - + select: function(item) { - //TODO + + if(!isNaN(parseInt(item))) + item = this.items.get(item); + + item = $(item, this.element); + if(!item.length) return; + + // clear the current selection + this._clearSelection(); + + // select all found + var newlySelected = [], self = this; + item.each(function(i) { + if(i == 0) { //Setting the focus on the first item in the list + self.previousFocus = self.currentFocus.attr('tabindex', -1); + self.currentFocus = $(this).attr('tabindex', 0); + self.previousFocus.removeClass('ui-state-focus'); + self.currentFocus.addClass('ui-state-focus'); + } + newlySelected.push(self._addToSelection($(this))); + }); + + // Ending the selection does a diff and then triggers appropriate events + this._endSelection(event, $($.map(newlySelected, function(i) { return i[0]; }))); + }, - + deselect: function(item) { - if(!item) this._clearSelection(true); - //TODO: Deselect single elements + + // if no item was specified, deselect all + if(!item) + this._clearSelection(true); + + if(!isNaN(parseInt(item))) + item = this.items.get(item); + + item = $(item, this.element); + if(!item.length) return; + + //store the current selection + this._previousSelection = this._selection.slice(); + + // deselect all found + var self = this; + item.each(function() { + self._removeFromSelection($(this)); + }); + + // Ending the selection does a diff and then triggers appropriate events + this._endSelection(event); + }, _uiHash: function(items, specialKey) { @@ -621,17 +790,9 @@ jQuery.effects||(function(d){d.effects={version:"1.7.2",save:function(g,h){for(v $.extend($.ui.selectable, { defaults: { - - //TODO: Figure out how to move these defaults out - cancel: ":input,option", - delay: 0, - distance: 1, - appendTo: 'body', - - multiple: true, - smart: true, + closest: true, filter: '> *', - + keyboard: true, lasso: { cancel: ":input,option", @@ -639,10 +800,7 @@ jQuery.effects||(function(d){d.effects={version:"1.7.2",save:function(g,h){for(v distance: 1, tolerance: 'touch', appendTo: 'body' - }, - - //Should we really delete that? - selectedClass: 'ui-state-selected' + } } }); |