|
1 /** |
|
2 * Interface Elements for jQuery |
|
3 * Sortables |
|
4 * |
|
5 * http://interface.eyecon.ro |
|
6 * |
|
7 * Copyright (c) 2006 Stefan Petre |
|
8 * Dual licensed under the MIT (MIT-LICENSE.txt) |
|
9 * and GPL (GPL-LICENSE.txt) licenses. |
|
10 * |
|
11 * |
|
12 */ |
|
13 |
|
14 /** |
|
15 * Allows you to resort elements within a container by dragging and dropping. Requires |
|
16 * the Draggables and Droppables plugins. The container and each item inside the container |
|
17 * must have an ID. Sortables are especially useful for lists. |
|
18 * |
|
19 * @see Plugins/Interface/Draggable |
|
20 * @see Plugins/Interface/Droppable |
|
21 * @author Stefan Petre |
|
22 * @name Sortable |
|
23 * @cat Plugins/Interface |
|
24 * @param Hash options A hash of options |
|
25 * @option String accept The class name for items inside the container (mandatory) |
|
26 * @option String activeclass The class for the container when one of its items has started to move |
|
27 * @option String hoverclass The class for the container when an acceptable item is inside it |
|
28 * @option String helperclass The helper is used to point to the place where the item will be |
|
29 * moved. This is the class for the helper. |
|
30 * @option Float opacity Opacity (between 0 and 1) of the item while being dragged |
|
31 * @option Boolean ghosting When true, the sortable is ghosted when dragged |
|
32 * @option String tolerance Either 'pointer', 'intersect', or 'fit'. See Droppable for more details |
|
33 * @option Boolean fit When true, sortable must be inside the container in order to drop |
|
34 * @option Integer fx Duration for the effect applied to the sortable |
|
35 * @option Function onchange Callback that gets called when the sortable list changed. It takes |
|
36 * an array of serialized elements |
|
37 * @option Boolean floats True if the sorted elements are floated |
|
38 * @option String containment Use 'parent' to constrain the drag to the container |
|
39 * @option String axis Use 'horizontally' or 'vertically' to constrain dragging to an axis |
|
40 * @option String handle The jQuery selector that indicates the draggable handle |
|
41 * @option DOMElement handle The node that indicates the draggable handle |
|
42 * @option Function onHover Callback that is called when an acceptable item is dragged over the |
|
43 * container. Gets the hovering DOMElement as a parameter |
|
44 * @option Function onOut Callback that is called when an acceptable item leaves the container. |
|
45 * Gets the leaving DOMElement as a parameter |
|
46 * @option Object cursorAt The mouse cursor will be moved to the offset on the dragged item |
|
47 * indicated by the object, which takes "top", "bottom", "left", and |
|
48 * "right" keys |
|
49 * @option Function onStart Callback function triggered when the dragging starts |
|
50 * @option Function onStop Callback function triggered when the dragging stops |
|
51 * @example $('ul').Sortable( |
|
52 * { |
|
53 * accept : 'sortableitem', |
|
54 * activeclass : 'sortableactive', |
|
55 * hoverclass : 'sortablehover', |
|
56 * helperclass : 'sorthelper', |
|
57 * opacity: 0.5, |
|
58 * fit : false |
|
59 * } |
|
60 * ) |
|
61 */ |
|
62 |
|
63 jQuery.iSort = { |
|
64 changed : [], |
|
65 collected : {}, |
|
66 helper : false, |
|
67 inFrontOf: null, |
|
68 |
|
69 start : function () |
|
70 { |
|
71 if (jQuery.iDrag.dragged == null) { |
|
72 return; |
|
73 } |
|
74 var shs, margins,c, cs; |
|
75 |
|
76 jQuery.iSort.helper.get(0).className = jQuery.iDrag.dragged.dragCfg.hpc; |
|
77 shs = jQuery.iSort.helper.get(0).style; |
|
78 shs.display = 'block'; |
|
79 jQuery.iSort.helper.oC = jQuery.extend( |
|
80 jQuery.iUtil.getPosition(jQuery.iSort.helper.get(0)), |
|
81 jQuery.iUtil.getSize(jQuery.iSort.helper.get(0)) |
|
82 ); |
|
83 |
|
84 shs.width = jQuery.iDrag.dragged.dragCfg.oC.wb + 'px'; |
|
85 shs.height = jQuery.iDrag.dragged.dragCfg.oC.hb + 'px'; |
|
86 //shs.cssFloat = jQuery.iDrag.dragged.dragCfg.oF; |
|
87 margins = jQuery.iUtil.getMargins(jQuery.iDrag.dragged); |
|
88 shs.marginTop = margins.t; |
|
89 shs.marginRight = margins.r; |
|
90 shs.marginBottom = margins.b; |
|
91 shs.marginLeft = margins.l; |
|
92 if (jQuery.iDrag.dragged.dragCfg.ghosting == true) { |
|
93 c = jQuery.iDrag.dragged.cloneNode(true); |
|
94 cs = c.style; |
|
95 cs.marginTop = '0px'; |
|
96 cs.marginRight = '0px'; |
|
97 cs.marginBottom = '0px'; |
|
98 cs.marginLeft = '0px'; |
|
99 cs.display = 'block'; |
|
100 jQuery.iSort.helper.empty().append(c); |
|
101 } |
|
102 jQuery(jQuery.iDrag.dragged).after(jQuery.iSort.helper.get(0)); |
|
103 jQuery.iDrag.dragged.style.display = 'none'; |
|
104 }, |
|
105 |
|
106 check : function (e) |
|
107 { |
|
108 if (!e.dragCfg.so && jQuery.iDrop.overzone.sortable) { |
|
109 if (e.dragCfg.onStop) |
|
110 e.dragCfg.onStop.apply(dragged); |
|
111 jQuery(e).css('position', e.dragCfg.initialPosition || e.dragCfg.oP); |
|
112 jQuery(e).DraggableDestroy(); |
|
113 jQuery(jQuery.iDrop.overzone).SortableAddItem(e); |
|
114 } |
|
115 jQuery.iSort.helper.removeClass(e.dragCfg.hpc).html(' '); |
|
116 jQuery.iSort.inFrontOf = null; |
|
117 var shs = jQuery.iSort.helper.get(0).style; |
|
118 shs.display = 'none'; |
|
119 jQuery.iSort.helper.after(e); |
|
120 if (e.dragCfg.fx > 0) { |
|
121 jQuery(e).fadeIn(e.dragCfg.fx); |
|
122 } |
|
123 jQuery('body').append(jQuery.iSort.helper.get(0)); |
|
124 var ts = []; |
|
125 var fnc = false; |
|
126 for(var i=0; i<jQuery.iSort.changed.length; i++){ |
|
127 var iEL = jQuery.iDrop.zones[jQuery.iSort.changed[i]].get(0); |
|
128 var id = jQuery.attr(iEL, 'id'); |
|
129 var ser = jQuery.iSort.serialize(id); |
|
130 if (iEL.dropCfg.os != ser.hash) { |
|
131 iEL.dropCfg.os = ser.hash; |
|
132 if (fnc == false && iEL.dropCfg.onChange) { |
|
133 fnc = iEL.dropCfg.onChange; |
|
134 } |
|
135 ser.id = id; |
|
136 ts[ts.length] = ser; |
|
137 } |
|
138 } |
|
139 jQuery.iSort.changed = []; |
|
140 if (fnc != false && ts.length > 0) { |
|
141 fnc(ts); |
|
142 } |
|
143 }, |
|
144 |
|
145 checkhover : function(e,o) |
|
146 { |
|
147 if (!jQuery.iDrag.dragged) |
|
148 return; |
|
149 var cur = false; |
|
150 var i = 0; |
|
151 if ( e.dropCfg.el.size() > 0) { |
|
152 for (i = e.dropCfg.el.size(); i >0; i--) { |
|
153 if (e.dropCfg.el.get(i-1) != jQuery.iDrag.dragged) { |
|
154 if (!e.sortCfg.floats) { |
|
155 if ( |
|
156 (e.dropCfg.el.get(i-1).pos.y + e.dropCfg.el.get(i-1).pos.hb/2) > jQuery.iDrag.dragged.dragCfg.ny |
|
157 ) { |
|
158 cur = e.dropCfg.el.get(i-1); |
|
159 } else { |
|
160 break; |
|
161 } |
|
162 } else { |
|
163 if ( |
|
164 (e.dropCfg.el.get(i-1).pos.x + e.dropCfg.el.get(i-1).pos.wb/2) > jQuery.iDrag.dragged.dragCfg.nx && |
|
165 (e.dropCfg.el.get(i-1).pos.y + e.dropCfg.el.get(i-1).pos.hb/2) > jQuery.iDrag.dragged.dragCfg.ny |
|
166 ) { |
|
167 cur = e.dropCfg.el.get(i-1); |
|
168 } |
|
169 } |
|
170 } |
|
171 } |
|
172 } |
|
173 //helpos = jQuery.iUtil.getPos(jQuery.iSort.helper.get(0)); |
|
174 if (cur && jQuery.iSort.inFrontOf != cur) { |
|
175 jQuery.iSort.inFrontOf = cur; |
|
176 jQuery(cur).before(jQuery.iSort.helper.get(0)); |
|
177 } else if(!cur && (jQuery.iSort.inFrontOf != null || jQuery.iSort.helper.get(0).parentNode != e) ) { |
|
178 jQuery.iSort.inFrontOf = null; |
|
179 jQuery(e).append(jQuery.iSort.helper.get(0)); |
|
180 } |
|
181 jQuery.iSort.helper.get(0).style.display = 'block'; |
|
182 }, |
|
183 |
|
184 measure : function (e) |
|
185 { |
|
186 if (jQuery.iDrag.dragged == null) { |
|
187 return; |
|
188 } |
|
189 e.dropCfg.el.each ( |
|
190 function () |
|
191 { |
|
192 this.pos = jQuery.extend( |
|
193 jQuery.iUtil.getSizeLite(this), |
|
194 jQuery.iUtil.getPositionLite(this) |
|
195 ); |
|
196 } |
|
197 ); |
|
198 }, |
|
199 |
|
200 serialize : function(s) |
|
201 { |
|
202 var i; |
|
203 var h = ''; |
|
204 var o = {}; |
|
205 if (s) { |
|
206 if (jQuery.iSort.collected[s] ) { |
|
207 o[s] = []; |
|
208 jQuery('#' + s + ' .' + jQuery.iSort.collected[s]).each( |
|
209 function () |
|
210 { |
|
211 if (h.length > 0) { |
|
212 h += '&'; |
|
213 } |
|
214 h += s + '[]=' + jQuery.attr(this,'id'); |
|
215 o[s][o[s].length] = jQuery.attr(this,'id'); |
|
216 } |
|
217 ); |
|
218 } else { |
|
219 for ( a in s) { |
|
220 if (jQuery.iSort.collected[s[a]] ) { |
|
221 o[s[a]] = []; |
|
222 jQuery('#' + s[a] + ' .' + jQuery.iSort.collected[s[a]]).each( |
|
223 function () |
|
224 { |
|
225 if (h.length > 0) { |
|
226 h += '&'; |
|
227 } |
|
228 h += s[a] + '[]=' + jQuery.attr(this,'id'); |
|
229 o[s[a]][o[s[a]].length] = jQuery.attr(this,'id'); |
|
230 } |
|
231 ); |
|
232 } |
|
233 } |
|
234 } |
|
235 } else { |
|
236 for ( i in jQuery.iSort.collected){ |
|
237 o[i] = []; |
|
238 jQuery('#' + i + ' .' + jQuery.iSort.collected[i]).each( |
|
239 function () |
|
240 { |
|
241 if (h.length > 0) { |
|
242 h += '&'; |
|
243 } |
|
244 h += i + '[]=' + jQuery.attr(this,'id'); |
|
245 o[i][o[i].length] = jQuery.attr(this,'id'); |
|
246 } |
|
247 ); |
|
248 } |
|
249 } |
|
250 return {hash:h, o:o}; |
|
251 }, |
|
252 |
|
253 addItem : function (e) |
|
254 { |
|
255 if ( !e.childNodes ) { |
|
256 return; |
|
257 } |
|
258 return this.each( |
|
259 function () |
|
260 { |
|
261 if(!this.sortCfg || !jQuery(e).is('.' + this.sortCfg.accept)) |
|
262 jQuery(e).addClass(this.sortCfg.accept); |
|
263 jQuery(e).Draggable(this.sortCfg.dragCfg); |
|
264 } |
|
265 ); |
|
266 }, |
|
267 |
|
268 destroy: function() |
|
269 { |
|
270 return this.each( |
|
271 function() |
|
272 { |
|
273 jQuery('.' + this.sortCfg.accept).DraggableDestroy(); |
|
274 jQuery(this).DroppableDestroy(); |
|
275 this.sortCfg = null; |
|
276 this.isSortable = null; |
|
277 } |
|
278 ); |
|
279 }, |
|
280 |
|
281 build : function (o) |
|
282 { |
|
283 if (o.accept && jQuery.iUtil && jQuery.iDrag && jQuery.iDrop) { |
|
284 if (!jQuery.iSort.helper) { |
|
285 jQuery('body',document).append('<div id="sortHelper"> </div>'); |
|
286 jQuery.iSort.helper = jQuery('#sortHelper'); |
|
287 jQuery.iSort.helper.get(0).style.display = 'none'; |
|
288 } |
|
289 this.Droppable( |
|
290 { |
|
291 accept : o.accept, |
|
292 activeclass : o.activeclass ? o.activeclass : false, |
|
293 hoverclass : o.hoverclass ? o.hoverclass : false, |
|
294 helperclass : o.helperclass ? o.helperclass : false, |
|
295 /*onDrop: function (drag, fx) |
|
296 { |
|
297 jQuery.iSort.helper.after(drag); |
|
298 if (fx > 0) { |
|
299 jQuery(drag).fadeIn(fx); |
|
300 } |
|
301 },*/ |
|
302 onHover: o.onHover||o.onhover, |
|
303 onOut: o.onOut||o.onout, |
|
304 sortable : true, |
|
305 onChange : o.onChange||o.onchange, |
|
306 fx : o.fx ? o.fx : false, |
|
307 ghosting : o.ghosting ? true : false, |
|
308 tolerance: o.tolerance ? o.tolerance : 'intersect' |
|
309 } |
|
310 ); |
|
311 |
|
312 return this.each( |
|
313 function() |
|
314 { |
|
315 var dragCfg = { |
|
316 revert : o.revert? true : false, |
|
317 zindex : 3000, |
|
318 opacity : o.opacity ? parseFloat(o.opacity) : false, |
|
319 hpc : o.helperclass ? o.helperclass : false, |
|
320 fx : o.fx ? o.fx : false, |
|
321 so : true, |
|
322 ghosting : o.ghosting ? true : false, |
|
323 handle: o.handle ? o.handle : null, |
|
324 containment: o.containment ? o.containment : null, |
|
325 onStart : o.onStart && o.onStart.constructor == Function ? o.onStart : false, |
|
326 onDrag : o.onDrag && o.onDrag.constructor == Function ? o.onDrag : false, |
|
327 onStop : o.onStop && o.onStop.constructor == Function ? o.onStop : false, |
|
328 axis : /vertically|horizontally/.test(o.axis) ? o.axis : false, |
|
329 snapDistance : o.snapDistance ? parseInt(o.snapDistance)||0 : false, |
|
330 cursorAt: o.cursorAt ? o.cursorAt : false |
|
331 }; |
|
332 jQuery('.' + o.accept, this).Draggable(dragCfg); |
|
333 this.isSortable = true; |
|
334 this.sortCfg = { |
|
335 accept : o.accept, |
|
336 revert : o.revert? true : false, |
|
337 zindex : 3000, |
|
338 opacity : o.opacity ? parseFloat(o.opacity) : false, |
|
339 hpc : o.helperclass ? o.helperclass : false, |
|
340 fx : o.fx ? o.fx : false, |
|
341 so : true, |
|
342 ghosting : o.ghosting ? true : false, |
|
343 handle: o.handle ? o.handle : null, |
|
344 containment: o.containment ? o.containment : null, |
|
345 floats: o.floats ? true : false, |
|
346 dragCfg : dragCfg |
|
347 } |
|
348 } |
|
349 ); |
|
350 } |
|
351 } |
|
352 }; |
|
353 |
|
354 jQuery.fn.extend( |
|
355 { |
|
356 Sortable : jQuery.iSort.build, |
|
357 /** |
|
358 * A new item can be added to a sortable by adding it to the DOM and then adding it via |
|
359 * SortableAddItem. |
|
360 * |
|
361 * @name SortableAddItem |
|
362 * @param DOMElement elem A DOM Element to add to the sortable list |
|
363 * @example $('#sortable1').append('<li id="newitem">new item</li>') |
|
364 * .SortableAddItem($("#new_item")[0]) |
|
365 * @type jQuery |
|
366 * @cat Plugins/Interface |
|
367 */ |
|
368 SortableAddItem : jQuery.iSort.addItem, |
|
369 /** |
|
370 * Destroy a sortable |
|
371 * |
|
372 * @name SortableDestroy |
|
373 * @example $('#sortable1').SortableDestroy(); |
|
374 * @type jQuery |
|
375 * @cat Plugins/Interface |
|
376 */ |
|
377 SortableDestroy: jQuery.iSort.destroy |
|
378 } |
|
379 ); |
|
380 |
|
381 /** |
|
382 * This function returns the hash and an object (can be used as arguments for $.post) for every |
|
383 * sortable in the page or specific sortables. The hash is based on the 'id' attributes of |
|
384 * container and items. |
|
385 * |
|
386 * @params String sortable The id of the sortable to serialize |
|
387 * @name $.SortSerialize |
|
388 * @type String |
|
389 * @cat Plugins/Interface |
|
390 */ |
|
391 |
|
392 jQuery.SortSerialize = jQuery.iSort.serialize; |