| 1068 |
rajveer |
1 |
/*
|
|
|
2 |
Copyright (c) 2009 Jordan Bach, http://www.utdallas.edu/~jrb048000/
|
|
|
3 |
|
|
|
4 |
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
5 |
of this software and associated documentation files (the "Software"), to deal
|
|
|
6 |
in the Software without restriction, including without limitation the rights
|
|
|
7 |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
8 |
copies of the Software, and to permit persons to whom the Software is
|
|
|
9 |
furnished to do so, subject to the following conditions:
|
|
|
10 |
|
|
|
11 |
The above copyright notice and this permission notice shall be included in
|
|
|
12 |
all copies or substantial portions of the Software.
|
|
|
13 |
|
|
|
14 |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
15 |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
16 |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
17 |
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
18 |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
19 |
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
20 |
THE SOFTWARE.
|
|
|
21 |
*/
|
|
|
22 |
|
|
|
23 |
/*
|
|
|
24 |
List Reorder <http://www.utdallas.edu/~jrb048000/ListReorder/>
|
|
|
25 |
|
|
|
26 |
Enables drag-and-drop reordering of list items for any simple ordered <ol> or unordered <li> list.
|
|
|
27 |
|
|
|
28 |
Author: Jordan Bach
|
|
|
29 |
Version: 0.1
|
|
|
30 |
Created: January 1, 2009
|
|
|
31 |
License: MIT License
|
|
|
32 |
|
|
|
33 |
Constructor
|
|
|
34 |
$(expr).ListOrder(options)
|
|
|
35 |
|
|
|
36 |
Methods:
|
|
|
37 |
makeDefaultOrder - sets the current list order to [0, 1, 2, ...]
|
|
|
38 |
restoreOrder - returns the list to its original order
|
|
|
39 |
|
|
|
40 |
Events:
|
|
|
41 |
listorderchanged - Fired after a list item is dropped. The 2nd argument
|
|
|
42 |
is a jQuery object representing the list that fired the event.
|
|
|
43 |
The 3rd argument is an array where the values represent
|
|
|
44 |
the original index of each list item.
|
|
|
45 |
|
|
|
46 |
Options:
|
|
|
47 |
itemHoverClass : 'itemHover',
|
|
|
48 |
dragTargetClass : 'dragTarget',
|
|
|
49 |
dropTargetClass : 'dropTarget',
|
|
|
50 |
dragHandleClass : 'dragHandle'
|
|
|
51 |
|
|
|
52 |
*/
|
|
|
53 |
|
|
|
54 |
(function($){
|
|
|
55 |
|
|
|
56 |
$.fn.ListReorder = function (options) {
|
|
|
57 |
|
|
|
58 |
$.fn.ListReorder.defaults = {
|
|
|
59 |
itemHoverClass : 'itemHover',
|
|
|
60 |
dragTargetClass : 'dragTarget',
|
|
|
61 |
dropTargetClass : 'dropTarget',
|
|
|
62 |
dragHandleClass : 'dragHandle',
|
|
|
63 |
useDefaultDragHandle : false
|
|
|
64 |
};
|
|
|
65 |
|
|
|
66 |
var opts = $.extend({}, $.fn.ListReorder.defaults, options);
|
|
|
67 |
|
|
|
68 |
return this.each(function() {
|
|
|
69 |
var theList = $(this), // The list (<ul>|<ol>)
|
|
|
70 |
theItems = $('li', theList), // All <li> elements in the list
|
|
|
71 |
dragActive = false, // Are we currently dragging an item?
|
|
|
72 |
dropTarget = null, // The list placeholder
|
|
|
73 |
dragTarget = null, // The element currently being dragged
|
|
|
74 |
dropIndex = -1, // The list index of the dropTarget
|
|
|
75 |
offset = {}, // Positions the mouse in the dragTarget
|
|
|
76 |
listOrder = [], // Keeps track of order relative to original order
|
|
|
77 |
ref = this;
|
|
|
78 |
|
|
|
79 |
theList.mouseout(ul_mouseout);
|
|
|
80 |
|
|
|
81 |
// Create the drag target
|
|
|
82 |
dragTarget = $('<div></div>');
|
|
|
83 |
dragTarget.insertAfter(theList);
|
|
|
84 |
dragTarget.hide();
|
|
|
85 |
dragTarget.css('position', 'absolute');
|
|
|
86 |
dragTarget.addClass(opts.dragTargetClass);
|
|
|
87 |
|
|
|
88 |
for (var i = 0; i < theItems.length; i++)
|
|
|
89 |
listOrder.push(i);
|
|
|
90 |
|
|
|
91 |
resetList();
|
|
|
92 |
|
|
|
93 |
function resetList() {
|
|
|
94 |
theItems = $('li', theList),
|
|
|
95 |
|
|
|
96 |
// For each <li> in the list
|
|
|
97 |
theItems.each(function() {
|
|
|
98 |
var li = $(this);
|
|
|
99 |
|
|
|
100 |
var dragHandle = $('<span></span>');
|
|
|
101 |
dragHandle.addClass(opts.dragHandleClass)
|
|
|
102 |
.mouseover(li_mouseover)
|
|
|
103 |
.mousedown(dragHandle_mousedown);
|
|
|
104 |
|
|
|
105 |
if (opts.useDefaultDragHandle)
|
|
|
106 |
dragHandle.css({
|
|
|
107 |
'display' : 'block',
|
|
|
108 |
'float' : 'right',
|
|
|
109 |
'width' : '10px',
|
|
|
110 |
'height' : '10px',
|
|
|
111 |
'border' : '2px solid #333',
|
|
|
112 |
'background' : '#ccc',
|
|
|
113 |
'cursor' : 'move'
|
|
|
114 |
});
|
|
|
115 |
|
|
|
116 |
$('.' + opts.dragHandleClass, li).remove();
|
|
|
117 |
li.prepend(dragHandle);
|
|
|
118 |
});
|
|
|
119 |
|
|
|
120 |
clearListItemStyles();
|
|
|
121 |
}
|
|
|
122 |
|
|
|
123 |
// Return all list items to their default state
|
|
|
124 |
function clearListItemStyles() {
|
|
|
125 |
theItems.each(function() {
|
|
|
126 |
var li = $(this);
|
|
|
127 |
li.removeClass(opts.itemHoverClass);
|
|
|
128 |
li.removeClass(opts.dropTargetClass);
|
|
|
129 |
});
|
|
|
130 |
}
|
|
|
131 |
|
|
|
132 |
// Handle any cleanup when the mouse leaves the list
|
|
|
133 |
function ul_mouseout() {
|
|
|
134 |
if (!dragActive)
|
|
|
135 |
clearListItemStyles();
|
|
|
136 |
}
|
|
|
137 |
|
|
|
138 |
// Add a hover class to a list item on mouseover
|
|
|
139 |
function li_mouseover() {
|
|
|
140 |
if (!dragActive) {
|
|
|
141 |
clearListItemStyles();
|
|
|
142 |
$(this).parent().addClass(opts.itemHoverClass);
|
|
|
143 |
}
|
|
|
144 |
}
|
|
|
145 |
|
|
|
146 |
// Prepare the list for dragging an item
|
|
|
147 |
function dragHandle_mousedown(e) {
|
|
|
148 |
var li = $(this).parent();
|
|
|
149 |
|
|
|
150 |
dragActive = true;
|
|
|
151 |
dropIndex = theItems.index(li);
|
|
|
152 |
|
|
|
153 |
// Show the drag target
|
|
|
154 |
dragTarget.html(li.html());
|
|
|
155 |
dragTarget.css('display', 'block');
|
|
|
156 |
offset.top = e.pageY - li.offset().top;
|
|
|
157 |
offset.left = e.pageX - li.offset().left;
|
|
|
158 |
updateDragTargetPos(e);
|
|
|
159 |
|
|
|
160 |
// Insert the placeholder
|
|
|
161 |
dropTarget = li;
|
|
|
162 |
dropTarget.html('');
|
|
|
163 |
dropTarget.css('height', dragTarget.css('height'));
|
|
|
164 |
dragTarget.css('width', dropTarget.width() + 'px');
|
|
|
165 |
dropTarget.addClass(opts.dropTargetClass);
|
|
|
166 |
|
|
|
167 |
// Disable Text and DOM selection
|
|
|
168 |
$(document).disableTextSelect();
|
|
|
169 |
|
|
|
170 |
$(document).mouseup(dragHandle_mouseup);
|
|
|
171 |
$(document).mousemove(document_mousemove);
|
|
|
172 |
}
|
|
|
173 |
|
|
|
174 |
// If this were on the element, we could lose the drag on the element
|
|
|
175 |
// if we move the mouse too fast
|
|
|
176 |
function document_mousemove(e) {
|
|
|
177 |
if (dragActive) {
|
|
|
178 |
// drag target follows mouse cursor
|
|
|
179 |
updateDragTargetPos(e);
|
|
|
180 |
|
|
|
181 |
// Don't do mess with drop index if we are above or below the list
|
|
|
182 |
if (y_mid(dragTarget) > y_bot(theList)
|
|
|
183 |
|| y_mid(dragTarget) < y_top(theList)) {
|
|
|
184 |
return;
|
|
|
185 |
}
|
|
|
186 |
|
|
|
187 |
// detect position of drag target relative to list items
|
|
|
188 |
// and swap drop target and neighboring item if necessary
|
|
|
189 |
if (y_mid(dragTarget) + 5 < y_top(dropTarget)) {
|
|
|
190 |
swapListItems(dropIndex, --dropIndex);
|
|
|
191 |
} else if (y_mid(dragTarget) - 5 > y_bot(dropTarget)) {
|
|
|
192 |
swapListItems(dropIndex, ++dropIndex);
|
|
|
193 |
}
|
|
|
194 |
}
|
|
|
195 |
}
|
|
|
196 |
|
|
|
197 |
function dragHandle_mouseup() {
|
|
|
198 |
// Restore the drop target
|
|
|
199 |
dropTarget.html(dragTarget.html());
|
|
|
200 |
dropTarget.removeClass(opts.dragTargetClass);
|
|
|
201 |
dropTarget = null;
|
|
|
202 |
|
|
|
203 |
// Hide the drag target
|
|
|
204 |
dragTarget.css('display', 'none');
|
|
|
205 |
|
|
|
206 |
dragActive = false;
|
|
|
207 |
dragTarget.unbind('mouseup', dragHandle_mouseup);
|
|
|
208 |
$(document).unbind('mousemove', document_mousemove);
|
|
|
209 |
resetList();
|
|
|
210 |
|
|
|
211 |
theList.trigger('listorderchanged', [theList, listOrder]);
|
|
|
212 |
|
|
|
213 |
// Re-enable text selection
|
|
|
214 |
$(document).enableTextSelect();
|
|
|
215 |
$(document).unbind('mouseup', dragHandle_mouseup);
|
|
|
216 |
}
|
|
|
217 |
|
|
|
218 |
function updateDragTargetPos(e) {
|
|
|
219 |
dragTarget.css({
|
|
|
220 |
'top' : e.pageY - offset.top + 'px',
|
|
|
221 |
'left' : e.pageX - offset.left + 'px'
|
|
|
222 |
});
|
|
|
223 |
}
|
|
|
224 |
|
|
|
225 |
// Change the order of two list items
|
|
|
226 |
function swapListItems(oldDropIndex, newDropIndex) {
|
|
|
227 |
// keep indices in bounds
|
|
|
228 |
if (dropIndex < 0) {
|
|
|
229 |
dropIndex = 0;
|
|
|
230 |
return;
|
|
|
231 |
} else if (dropIndex >= theItems.length) {
|
|
|
232 |
dropIndex = theItems.length - 1;
|
|
|
233 |
return;
|
|
|
234 |
}
|
|
|
235 |
|
|
|
236 |
var t = listOrder[oldDropIndex];
|
|
|
237 |
listOrder[oldDropIndex] = listOrder[newDropIndex];
|
|
|
238 |
listOrder[newDropIndex] = t;
|
|
|
239 |
|
|
|
240 |
// swap list items
|
|
|
241 |
var oldDropTarget = theItems.get(oldDropIndex),
|
|
|
242 |
newDropTarget = theItems.get(newDropIndex),
|
|
|
243 |
temp1 = $(oldDropTarget).clone(true);
|
|
|
244 |
temp2 = $(newDropTarget).clone(true);
|
|
|
245 |
|
|
|
246 |
$(oldDropTarget).replaceWith(temp2)
|
|
|
247 |
.mouseover(li_mouseover)
|
|
|
248 |
.mousedown(dragHandle_mousedown);
|
|
|
249 |
$(newDropTarget).replaceWith(temp1)
|
|
|
250 |
.mouseover(li_mouseover)
|
|
|
251 |
.mousedown(dragHandle_mousedown);
|
|
|
252 |
|
|
|
253 |
// reset so it is valid on next use
|
|
|
254 |
theItems = $('li', theList);
|
|
|
255 |
dropTarget = $(theItems.get(newDropIndex));
|
|
|
256 |
}
|
|
|
257 |
|
|
|
258 |
function y_top(jq) {
|
|
|
259 |
return jq.offset().top;
|
|
|
260 |
}
|
|
|
261 |
|
|
|
262 |
function y_mid(jq) {
|
|
|
263 |
return (y_top(jq) + y_bot(jq)) / 2
|
|
|
264 |
}
|
|
|
265 |
|
|
|
266 |
function y_bot(jq) {
|
|
|
267 |
return jq.offset().top + jq.outerHeight();
|
|
|
268 |
}
|
|
|
269 |
|
|
|
270 |
this.makeDefaultOrder = function() {
|
|
|
271 |
for (var i = 0; i < listOrder.length; i++)
|
|
|
272 |
listOrder[i] = i;
|
|
|
273 |
}
|
|
|
274 |
|
|
|
275 |
this.restoreOrder = function() {
|
|
|
276 |
for (var i = 0; i < theItems.length; i++) {
|
|
|
277 |
if (i != listOrder[i]) {
|
|
|
278 |
var k = 0;
|
|
|
279 |
for (; k < listOrder.length; k++)
|
|
|
280 |
if (listOrder[k] == i)
|
|
|
281 |
break;
|
|
|
282 |
swapListItems(i, k);
|
|
|
283 |
}
|
|
|
284 |
}
|
|
|
285 |
theList.trigger('listorderchanged', [theList, listOrder]);
|
|
|
286 |
}
|
|
|
287 |
});
|
|
|
288 |
}
|
|
|
289 |
})(jQuery);
|