1 |
/*! |
2 |
* jQuery UI Sortable 1.10.3 |
3 |
* http://jqueryui.com |
4 |
* |
5 |
* Copyright 2013 jQuery Foundation and other contributors |
6 |
* Released under the MIT license. |
7 |
* http://jquery.org/license |
8 |
* |
9 |
* http://api.jqueryui.com/sortable/ |
10 |
* |
11 |
* Depends: |
12 |
* jquery.ui.core.js |
13 |
* jquery.ui.mouse.js |
14 |
* jquery.ui.widget.js |
15 |
*/ |
16 |
(function( $, undefined ) { |
17 |
|
18 |
/*jshint loopfunc: true */ |
19 |
|
20 |
function isOverAxis( x, reference, size ) { |
21 |
return ( x > reference ) && ( x < ( reference + size ) ); |
22 |
} |
23 |
|
24 |
function isFloating(item) { |
25 |
return (/left|right/).test(item.css("float")) || (/inline|table-cell/).test(item.css("display")); |
26 |
} |
27 |
|
28 |
$.widget("ui.sortable", $.ui.mouse, { |
29 |
version: "1.10.3", |
30 |
widgetEventPrefix: "sort", |
31 |
ready: false, |
32 |
options: { |
33 |
appendTo: "parent", |
34 |
axis: false, |
35 |
connectWith: false, |
36 |
containment: false, |
37 |
cursor: "auto", |
38 |
cursorAt: false, |
39 |
dropOnEmpty: true, |
40 |
forcePlaceholderSize: false, |
41 |
forceHelperSize: false, |
42 |
grid: false, |
43 |
handle: false, |
44 |
helper: "original", |
45 |
items: "> *", |
46 |
opacity: false, |
47 |
placeholder: false, |
48 |
revert: false, |
49 |
scroll: true, |
50 |
scrollSensitivity: 20, |
51 |
scrollSpeed: 20, |
52 |
scope: "default", |
53 |
tolerance: "intersect", |
54 |
zIndex: 1000, |
55 |
|
56 |
// callbacks |
57 |
activate: null, |
58 |
beforeStop: null, |
59 |
change: null, |
60 |
deactivate: null, |
61 |
out: null, |
62 |
over: null, |
63 |
receive: null, |
64 |
remove: null, |
65 |
sort: null, |
66 |
start: null, |
67 |
stop: null, |
68 |
update: null |
69 |
}, |
70 |
_create: function() { |
71 |
|
72 |
var o = this.options; |
73 |
this.containerCache = {}; |
74 |
this.element.addClass("ui-sortable"); |
75 |
|
76 |
//Get the items |
77 |
this.refresh(); |
78 |
|
79 |
//Let's determine if the items are being displayed horizontally |
80 |
this.floating = this.items.length ? o.axis === "x" || isFloating(this.items[0].item) : false; |
81 |
|
82 |
//Let's determine the parent's offset |
83 |
this.offset = this.element.offset(); |
84 |
|
85 |
//Initialize mouse events for interaction |
86 |
this._mouseInit(); |
87 |
|
88 |
//We're ready to go |
89 |
this.ready = true; |
90 |
|
91 |
}, |
92 |
|
93 |
_destroy: function() { |
94 |
this.element |
95 |
.removeClass("ui-sortable ui-sortable-disabled"); |
96 |
this._mouseDestroy(); |
97 |
|
98 |
for ( var i = this.items.length - 1; i >= 0; i-- ) { |
99 |
this.items[i].item.removeData(this.widgetName + "-item"); |
100 |
} |
101 |
|
102 |
return this; |
103 |
}, |
104 |
|
105 |
_setOption: function(key, value){ |
106 |
if ( key === "disabled" ) { |
107 |
this.options[ key ] = value; |
108 |
|
109 |
this.widget().toggleClass( "ui-sortable-disabled", !!value ); |
110 |
} else { |
111 |
// Don't call widget base _setOption for disable as it adds ui-state-disabled class |
112 |
$.Widget.prototype._setOption.apply(this, arguments); |
113 |
} |
114 |
}, |
115 |
|
116 |
_mouseCapture: function(event, overrideHandle) { |
117 |
var currentItem = null, |
118 |
validHandle = false, |
119 |
that = this; |
120 |
|
121 |
if (this.reverting) { |
122 |
return false; |
123 |
} |
124 |
|
125 |
if(this.options.disabled || this.options.type === "static") { |
126 |
return false; |
127 |
} |
128 |
|
129 |
//We have to refresh the items data once first |
130 |
this._refreshItems(event); |
131 |
|
132 |
//Find out if the clicked node (or one of its parents) is a actual item in this.items |
133 |
$(event.target).parents().each(function() { |
134 |
if($.data(this, that.widgetName + "-item") === that) { |
135 |
currentItem = $(this); |
136 |
return false; |
137 |
} |
138 |
}); |
139 |
if($.data(event.target, that.widgetName + "-item") === that) { |
140 |
currentItem = $(event.target); |
141 |
} |
142 |
|
143 |
if(!currentItem) { |
144 |
return false; |
145 |
} |
146 |
if(this.options.handle && !overrideHandle) { |
147 |
$(this.options.handle, currentItem).find("*").addBack().each(function() { |
148 |
if(this === event.target) { |
149 |
validHandle = true; |
150 |
} |
151 |
}); |
152 |
if(!validHandle) { |
153 |
return false; |
154 |
} |
155 |
} |
156 |
|
157 |
this.currentItem = currentItem; |
158 |
this._removeCurrentsFromItems(); |
159 |
return true; |
160 |
|
161 |
}, |
162 |
|
163 |
_mouseStart: function(event, overrideHandle, noActivation) { |
164 |
|
165 |
var i, body, |
166 |
o = this.options; |
167 |
|
168 |
this.currentContainer = this; |
169 |
|
170 |
//We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture |
171 |
this.refreshPositions(); |
172 |
|
173 |
//Create and append the visible helper |
174 |
this.helper = this._createHelper(event); |
175 |
|
176 |
//Cache the helper size |
177 |
this._cacheHelperProportions(); |
178 |
|
179 |
/* |
180 |
* - Position generation - |
181 |
* This block generates everything position related - it's the core of draggables. |
182 |
*/ |
183 |
|
184 |
//Cache the margins of the original element |
185 |
this._cacheMargins(); |
186 |
|
187 |
//Get the next scrolling parent |
188 |
this.scrollParent = this.helper.scrollParent(); |
189 |
|
190 |
//The element's absolute position on the page minus margins |
191 |
this.offset = this.currentItem.offset(); |
192 |
this.offset = { |
193 |
top: this.offset.top - this.margins.top, |
194 |
left: this.offset.left - this.margins.left |
195 |
}; |
196 |
|
197 |
$.extend(this.offset, { |
198 |
click: { //Where the click happened, relative to the element |
199 |
left: event.pageX - this.offset.left, |
200 |
top: event.pageY - this.offset.top |
201 |
}, |
202 |
parent: this._getParentOffset(), |
203 |
relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper |
204 |
}); |
205 |
|
206 |
// Only after we got the offset, we can change the helper's position to absolute |
207 |
// TODO: Still need to figure out a way to make relative sorting possible |
208 |
this.helper.css("position", "absolute"); |
209 |
this.cssPosition = this.helper.css("position"); |
210 |
|
211 |
//Generate the original position |
212 |
this.originalPosition = this._generatePosition(event); |
213 |
this.originalPageX = event.pageX; |
214 |
this.originalPageY = event.pageY; |
215 |
|
216 |
//Adjust the mouse offset relative to the helper if "cursorAt" is supplied |
217 |
(o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt)); |
218 |
|
219 |
//Cache the former DOM position |
220 |
this.domPosition = { prev: this.currentItem.prev()[0], parent: this.currentItem.parent()[0] }; |
221 |
|
222 |
//If the helper is not the original, hide the original so it's not playing any role during the drag, won't cause anything bad this way |
223 |
if(this.helper[0] !== this.currentItem[0]) { |
224 |
this.currentItem.hide(); |
225 |
} |
226 |
|
227 |
//Create the placeholder |
228 |
this._createPlaceholder(); |
229 |
|
230 |
//Set a containment if given in the options |
231 |
if(o.containment) { |
232 |
this._setContainment(); |
233 |
} |
234 |
|
235 |
if( o.cursor && o.cursor !== "auto" ) { // cursor option |
236 |
body = this.document.find( "body" ); |
237 |
|
238 |
// support: IE |
239 |
this.storedCursor = body.css( "cursor" ); |
240 |
body.css( "cursor", o.cursor ); |
241 |
|
242 |
this.storedStylesheet = $( "<style>*{ cursor: "+o.cursor+" !important; }</style>" ).appendTo( body ); |
243 |
} |
244 |
|
245 |
if(o.opacity) { // opacity option |
246 |
if (this.helper.css("opacity")) { |
247 |
this._storedOpacity = this.helper.css("opacity"); |
248 |
} |
249 |
this.helper.css("opacity", o.opacity); |
250 |
} |
251 |
|
252 |
if(o.zIndex) { // zIndex option |
253 |
if (this.helper.css("zIndex")) { |
254 |
this._storedZIndex = this.helper.css("zIndex"); |
255 |
} |
256 |
this.helper.css("zIndex", o.zIndex); |
257 |
} |
258 |
|
259 |
//Prepare scrolling |
260 |
if(this.scrollParent[0] !== document && this.scrollParent[0].tagName !== "HTML") { |
261 |
this.overflowOffset = this.scrollParent.offset(); |
262 |
} |
263 |
|
264 |
//Call callbacks |
265 |
this._trigger("start", event, this._uiHash()); |
266 |
|
267 |
//Recache the helper size |
268 |
if(!this._preserveHelperProportions) { |
269 |
this._cacheHelperProportions(); |
270 |
} |
271 |
|
272 |
|
273 |
//Post "activate" events to possible containers |
274 |
if( !noActivation ) { |
275 |
for ( i = this.containers.length - 1; i >= 0; i-- ) { |
276 |
this.containers[ i ]._trigger( "activate", event, this._uiHash( this ) ); |
277 |
} |
278 |
} |
279 |
|
280 |
//Prepare possible droppables |
281 |
if($.ui.ddmanager) { |
282 |
$.ui.ddmanager.current = this; |
283 |
} |
284 |
|
285 |
if ($.ui.ddmanager && !o.dropBehaviour) { |
286 |
$.ui.ddmanager.prepareOffsets(this, event); |
287 |
} |
288 |
|
289 |
this.dragging = true; |
290 |
|
291 |
this.helper.addClass("ui-sortable-helper"); |
292 |
this._mouseDrag(event); //Execute the drag once - this causes the helper not to be visible before getting its correct position |
293 |
return true; |
294 |
|
295 |
}, |
296 |
|
297 |
_mouseDrag: function(event) { |
298 |
var i, item, itemElement, intersection, |
299 |
o = this.options, |
300 |
scrolled = false; |
301 |
|
302 |
//Compute the helpers position |
303 |
this.position = this._generatePosition(event); |
304 |
this.positionAbs = this._convertPositionTo("absolute"); |
305 |
|
306 |
if (!this.lastPositionAbs) { |
307 |
this.lastPositionAbs = this.positionAbs; |
308 |
} |
309 |
|
310 |
//Do scrolling |
311 |
if(this.options.scroll) { |
312 |
if(this.scrollParent[0] !== document && this.scrollParent[0].tagName !== "HTML") { |
313 |
|
314 |
if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) { |
315 |
this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed; |
316 |
} else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity) { |
317 |
this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed; |
318 |
} |
319 |
|
320 |
if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) { |
321 |
this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed; |
322 |
} else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity) { |
323 |
this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed; |
324 |
} |
325 |
|
326 |
} else { |
327 |
|
328 |
if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) { |
329 |
scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed); |
330 |
} else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) { |
331 |
scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed); |
332 |
} |
333 |
|
334 |
if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) { |
335 |
scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed); |
336 |
} else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) { |
337 |
scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed); |
338 |
} |
339 |
|
340 |
} |
341 |
|
342 |
if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) { |
343 |
$.ui.ddmanager.prepareOffsets(this, event); |
344 |
} |
345 |
} |
346 |
|
347 |
//Regenerate the absolute position used for position checks |
348 |
this.positionAbs = this._convertPositionTo("absolute"); |
349 |
|
350 |
//Set the helper position |
351 |
if(!this.options.axis || this.options.axis !== "y") { |
352 |
this.helper[0].style.left = this.position.left+"px"; |
353 |
} |
354 |
if(!this.options.axis || this.options.axis !== "x") { |
355 |
this.helper[0].style.top = this.position.top+"px"; |
356 |
} |
357 |
|
358 |
//Rearrange |
359 |
for (i = this.items.length - 1; i >= 0; i--) { |
360 |
|
361 |
//Cache variables and intersection, continue if no intersection |
362 |
item = this.items[i]; |
363 |
itemElement = item.item[0]; |
364 |
intersection = this._intersectsWithPointer(item); |
365 |
if (!intersection) { |
366 |
continue; |
367 |
} |
368 |
|
369 |
// Only put the placeholder inside the current Container, skip all |
370 |
// items form other containers. This works because when moving |
371 |
// an item from one container to another the |
372 |
// currentContainer is switched before the placeholder is moved. |
373 |
// |
374 |
// Without this moving items in "sub-sortables" can cause the placeholder to jitter |
375 |
// beetween the outer and inner container. |
376 |
if (item.instance !== this.currentContainer) { |
377 |
continue; |
378 |
} |
379 |
|
380 |
// cannot intersect with itself |
381 |
// no useless actions that have been done before |
382 |
// no action if the item moved is the parent of the item checked |
383 |
if (itemElement !== this.currentItem[0] && |
384 |
this.placeholder[intersection === 1 ? "next" : "prev"]()[0] !== itemElement && |
385 |
!$.contains(this.placeholder[0], itemElement) && |
386 |
(this.options.type === "semi-dynamic" ? !$.contains(this.element[0], itemElement) : true) |
387 |
) { |
388 |
|
389 |
this.direction = intersection === 1 ? "down" : "up"; |
390 |
|
391 |
if (this.options.tolerance === "pointer" || this._intersectsWithSides(item)) { |
392 |
this._rearrange(event, item); |
393 |
} else { |
394 |
break; |
395 |
} |
396 |
|
397 |
this._trigger("change", event, this._uiHash()); |
398 |
break; |
399 |
} |
400 |
} |
401 |
|
402 |
//Post events to containers |
403 |
this._contactContainers(event); |
404 |
|
405 |
//Interconnect with droppables |
406 |
if($.ui.ddmanager) { |
407 |
$.ui.ddmanager.drag(this, event); |
408 |
} |
409 |
|
410 |
//Call callbacks |
411 |
this._trigger("sort", event, this._uiHash()); |
412 |
|
413 |
this.lastPositionAbs = this.positionAbs; |
414 |
return false; |
415 |
|
416 |
}, |
417 |
|
418 |
_mouseStop: function(event, noPropagation) { |
419 |
|
420 |
if(!event) { |
421 |
return; |
422 |
} |
423 |
|
424 |
//If we are using droppables, inform the manager about the drop |
425 |
if ($.ui.ddmanager && !this.options.dropBehaviour) { |
426 |
$.ui.ddmanager.drop(this, event); |
427 |
} |
428 |
|
429 |
if(this.options.revert) { |
430 |
var that = this, |
431 |
cur = this.placeholder.offset(), |
432 |
axis = this.options.axis, |
433 |
animation = {}; |
434 |
|
435 |
if ( !axis || axis === "x" ) { |
436 |
animation.left = cur.left - this.offset.parent.left - this.margins.left + (this.offsetParent[0] === document.body ? 0 : this.offsetParent[0].scrollLeft); |
437 |
} |
438 |
if ( !axis || axis === "y" ) { |
439 |
animation.top = cur.top - this.offset.parent.top - this.margins.top + (this.offsetParent[0] === document.body ? 0 : this.offsetParent[0].scrollTop); |
440 |
} |
441 |
this.reverting = true; |
442 |
$(this.helper).animate( animation, parseInt(this.options.revert, 10) || 500, function() { |
443 |
that._clear(event); |
444 |
}); |
445 |
} else { |
446 |
this._clear(event, noPropagation); |
447 |
} |
448 |
|
449 |
return false; |
450 |
|
451 |
}, |
452 |
|
453 |
cancel: function() { |
454 |
|
455 |
if(this.dragging) { |
456 |
|
457 |
this._mouseUp({ target: null }); |
458 |
|
459 |
if(this.options.helper === "original") { |
460 |
this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"); |
461 |
} else { |
462 |
this.currentItem.show(); |
463 |
} |
464 |
|
465 |
//Post deactivating events to containers |
466 |
for (var i = this.containers.length - 1; i >= 0; i--){ |
467 |
this.containers[i]._trigger("deactivate", null, this._uiHash(this)); |
468 |
if(this.containers[i].containerCache.over) { |
469 |
this.containers[i]._trigger("out", null, this._uiHash(this)); |
470 |
this.containers[i].containerCache.over = 0; |
471 |
} |
472 |
} |
473 |
|
474 |
} |
475 |
|
476 |
if (this.placeholder) { |
477 |
//$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node! |
478 |
if(this.placeholder[0].parentNode) { |
479 |
this.placeholder[0].parentNode.removeChild(this.placeholder[0]); |
480 |
} |
481 |
if(this.options.helper !== "original" && this.helper && this.helper[0].parentNode) { |
482 |
this.helper.remove(); |
483 |
} |
484 |
|
485 |
$.extend(this, { |
486 |
helper: null, |
487 |
dragging: false, |
488 |
reverting: false, |
489 |
_noFinalSort: null |
490 |
}); |
491 |
|
492 |
if(this.domPosition.prev) { |
493 |
$(this.domPosition.prev).after(this.currentItem); |
494 |
} else { |
495 |
$(this.domPosition.parent).prepend(this.currentItem); |
496 |
} |
497 |
} |
498 |
|
499 |
return this; |
500 |
|
501 |
}, |
502 |
|
503 |
serialize: function(o) { |
504 |
|
505 |
var items = this._getItemsAsjQuery(o && o.connected), |
506 |
str = []; |
507 |
o = o || {}; |
508 |
|
509 |
$(items).each(function() { |
510 |
var res = ($(o.item || this).attr(o.attribute || "id") || "").match(o.expression || (/(.+)[\-=_](.+)/)); |
511 |
if (res) { |
512 |
str.push((o.key || res[1]+"[]")+"="+(o.key && o.expression ? res[1] : res[2])); |
513 |
} |
514 |
}); |
515 |
|
516 |
if(!str.length && o.key) { |
517 |
str.push(o.key + "="); |
518 |
} |
519 |
|
520 |
return str.join("&"); |
521 |
|
522 |
}, |
523 |
|
524 |
toArray: function(o) { |
525 |
|
526 |
var items = this._getItemsAsjQuery(o && o.connected), |
527 |
ret = []; |
528 |
|
529 |
o = o || {}; |
530 |
|
531 |
items.each(function() { ret.push($(o.item || this).attr(o.attribute || "id") || ""); }); |
532 |
return ret; |
533 |
|
534 |
}, |
535 |
|
536 |
/* Be careful with the following core functions */ |
537 |
_intersectsWith: function(item) { |
538 |
|
539 |
var x1 = this.positionAbs.left, |
540 |
x2 = x1 + this.helperProportions.width, |
541 |
y1 = this.positionAbs.top, |
542 |
y2 = y1 + this.helperProportions.height, |
543 |
l = item.left, |
544 |
r = l + item.width, |
545 |
t = item.top, |
546 |
b = t + item.height, |
547 |
dyClick = this.offset.click.top, |
548 |
dxClick = this.offset.click.left, |
549 |
isOverElementHeight = ( this.options.axis === "x" ) || ( ( y1 + dyClick ) > t && ( y1 + dyClick ) < b ), |
550 |
isOverElementWidth = ( this.options.axis === "y" ) || ( ( x1 + dxClick ) > l && ( x1 + dxClick ) < r ), |
551 |
isOverElement = isOverElementHeight && isOverElementWidth; |
552 |
|
553 |
if ( this.options.tolerance === "pointer" || |
554 |
this.options.forcePointerForContainers || |
555 |
(this.options.tolerance !== "pointer" && this.helperProportions[this.floating ? "width" : "height"] > item[this.floating ? "width" : "height"]) |
556 |
) { |
557 |
return isOverElement; |
558 |
} else { |
559 |
|
560 |
return (l < x1 + (this.helperProportions.width / 2) && // Right Half |
561 |
x2 - (this.helperProportions.width / 2) < r && // Left Half |
562 |
t < y1 + (this.helperProportions.height / 2) && // Bottom Half |
563 |
y2 - (this.helperProportions.height / 2) < b ); // Top Half |
564 |
|
565 |
} |
566 |
}, |
567 |
|
568 |
_intersectsWithPointer: function(item) { |
569 |
|
570 |
var isOverElementHeight = (this.options.axis === "x") || isOverAxis(this.positionAbs.top + this.offset.click.top, item.top, item.height), |
571 |
isOverElementWidth = (this.options.axis === "y") || isOverAxis(this.positionAbs.left + this.offset.click.left, item.left, item.width), |
572 |
isOverElement = isOverElementHeight && isOverElementWidth, |
573 |
verticalDirection = this._getDragVerticalDirection(), |
574 |
horizontalDirection = this._getDragHorizontalDirection(); |
575 |
|
576 |
if (!isOverElement) { |
577 |
return false; |
578 |
} |
579 |
|
580 |
return this.floating ? |
581 |
( ((horizontalDirection && horizontalDirection === "right") || verticalDirection === "down") ? 2 : 1 ) |
582 |
: ( verticalDirection && (verticalDirection === "down" ? 2 : 1) ); |
583 |
|
584 |
}, |
585 |
|
586 |
_intersectsWithSides: function(item) { |
587 |
|
588 |
var isOverBottomHalf = isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height/2), item.height), |
589 |
isOverRightHalf = isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width/2), item.width), |
590 |
verticalDirection = this._getDragVerticalDirection(), |
591 |
horizontalDirection = this._getDragHorizontalDirection(); |
592 |
|
593 |
if (this.floating && horizontalDirection) { |
594 |
return ((horizontalDirection === "right" && isOverRightHalf) || (horizontalDirection === "left" && !isOverRightHalf)); |
595 |
} else { |
596 |
return verticalDirection && ((verticalDirection === "down" && isOverBottomHalf) || (verticalDirection === "up" && !isOverBottomHalf)); |
597 |
} |
598 |
|
599 |
}, |
600 |
|
601 |
_getDragVerticalDirection: function() { |
602 |
var delta = this.positionAbs.top - this.lastPositionAbs.top; |
603 |
return delta !== 0 && (delta > 0 ? "down" : "up"); |
604 |
}, |
605 |
|
606 |
_getDragHorizontalDirection: function() { |
607 |
var delta = this.positionAbs.left - this.lastPositionAbs.left; |
608 |
return delta !== 0 && (delta > 0 ? "right" : "left"); |
609 |
}, |
610 |
|
611 |
refresh: function(event) { |
612 |
this._refreshItems(event); |
613 |
this.refreshPositions(); |
614 |
return this; |
615 |
}, |
616 |
|
617 |
_connectWith: function() { |
618 |
var options = this.options; |
619 |
return options.connectWith.constructor === String ? [options.connectWith] : options.connectWith; |
620 |
}, |
621 |
|
622 |
_getItemsAsjQuery: function(connected) { |
623 |
|
624 |
var i, j, cur, inst, |
625 |
items = [], |
626 |
queries = [], |
627 |
connectWith = this._connectWith(); |
628 |
|
629 |
if(connectWith && connected) { |
630 |
for (i = connectWith.length - 1; i >= 0; i--){ |
631 |
cur = $(connectWith[i]); |
632 |
for ( j = cur.length - 1; j >= 0; j--){ |
633 |
inst = $.data(cur[j], this.widgetFullName); |
634 |
if(inst && inst !== this && !inst.options.disabled) { |
635 |
queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element) : $(inst.options.items, inst.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), inst]); |
636 |
} |
637 |
} |
638 |
} |
639 |
} |
640 |
|
641 |
queries.push([$.isFunction(this.options.items) ? this.options.items.call(this.element, null, { options: this.options, item: this.currentItem }) : $(this.options.items, this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), this]); |
642 |
|
643 |
for (i = queries.length - 1; i >= 0; i--){ |
644 |
queries[i][0].each(function() { |
645 |
items.push(this); |
646 |
}); |
647 |
} |
648 |
|
649 |
return $(items); |
650 |
|
651 |
}, |
652 |
|
653 |
_removeCurrentsFromItems: function() { |
654 |
|
655 |
var list = this.currentItem.find(":data(" + this.widgetName + "-item)"); |
656 |
|
657 |
this.items = $.grep(this.items, function (item) { |
658 |
for (var j=0; j < list.length; j++) { |
659 |
if(list[j] === item.item[0]) { |
660 |
return false; |
661 |
} |
662 |
} |
663 |
return true; |
664 |
}); |
665 |
|
666 |
}, |
667 |
|
668 |
_refreshItems: function(event) { |
669 |
|
670 |
this.items = []; |
671 |
this.containers = [this]; |
672 |
|
673 |
var i, j, cur, inst, targetData, _queries, item, queriesLength, |
674 |
items = this.items, |
675 |
queries = [[$.isFunction(this.options.items) ? this.options.items.call(this.element[0], event, { item: this.currentItem }) : $(this.options.items, this.element), this]], |
676 |
connectWith = this._connectWith(); |
677 |
|
678 |
if(connectWith && this.ready) { //Shouldn't be run the first time through due to massive slow-down |
679 |
for (i = connectWith.length - 1; i >= 0; i--){ |
680 |
cur = $(connectWith[i]); |
681 |
for (j = cur.length - 1; j >= 0; j--){ |
682 |
inst = $.data(cur[j], this.widgetFullName); |
683 |
if(inst && inst !== this && !inst.options.disabled) { |
684 |
queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element[0], event, { item: this.currentItem }) : $(inst.options.items, inst.element), inst]); |
685 |
this.containers.push(inst); |
686 |
} |
687 |
} |
688 |
} |
689 |
} |
690 |
|
691 |
for (i = queries.length - 1; i >= 0; i--) { |
692 |
targetData = queries[i][1]; |
693 |
_queries = queries[i][0]; |
694 |
|
695 |
for (j=0, queriesLength = _queries.length; j < queriesLength; j++) { |
696 |
item = $(_queries[j]); |
697 |
|
698 |
item.data(this.widgetName + "-item", targetData); // Data for target checking (mouse manager) |
699 |
|
700 |
items.push({ |
701 |
item: item, |
702 |
instance: targetData, |
703 |
width: 0, height: 0, |
704 |
left: 0, top: 0 |
705 |
}); |
706 |
} |
707 |
} |
708 |
|
709 |
}, |
710 |
|
711 |
refreshPositions: function(fast) { |
712 |
|
713 |
//This has to be redone because due to the item being moved out/into the offsetParent, the offsetParent's position will change |
714 |
if(this.offsetParent && this.helper) { |
715 |
this.offset.parent = this._getParentOffset(); |
716 |
} |
717 |
|
718 |
var i, item, t, p; |
719 |
|
720 |
for (i = this.items.length - 1; i >= 0; i--){ |
721 |
item = this.items[i]; |
722 |
|
723 |
//We ignore calculating positions of all connected containers when we're not over them |
724 |
if(item.instance !== this.currentContainer && this.currentContainer && item.item[0] !== this.currentItem[0]) { |
725 |
continue; |
726 |
} |
727 |
|
728 |
t = this.options.toleranceElement ? $(this.options.toleranceElement, item.item) : item.item; |
729 |
|
730 |
if (!fast) { |
731 |
item.width = t.outerWidth(); |
732 |
item.height = t.outerHeight(); |
733 |
} |
734 |
|
735 |
p = t.offset(); |
736 |
item.left = p.left; |
737 |
item.top = p.top; |
738 |
} |
739 |
|
740 |
if(this.options.custom && this.options.custom.refreshContainers) { |
741 |
this.options.custom.refreshContainers.call(this); |
742 |
} else { |
743 |
for (i = this.containers.length - 1; i >= 0; i--){ |
744 |
p = this.containers[i].element.offset(); |
745 |
this.containers[i].containerCache.left = p.left; |
746 |
this.containers[i].containerCache.top = p.top; |
747 |
this.containers[i].containerCache.width = this.containers[i].element.outerWidth(); |
748 |
this.containers[i].containerCache.height = this.containers[i].element.outerHeight(); |
749 |
} |
750 |
} |
751 |
|
752 |
return this; |
753 |
}, |
754 |
|
755 |
_createPlaceholder: function(that) { |
756 |
that = that || this; |
757 |
var className, |
758 |
o = that.options; |
759 |
|
760 |
if(!o.placeholder || o.placeholder.constructor === String) { |
761 |
className = o.placeholder; |
762 |
o.placeholder = { |
763 |
element: function() { |
764 |
|
765 |
var nodeName = that.currentItem[0].nodeName.toLowerCase(), |
766 |
element = $( "<" + nodeName + ">", that.document[0] ) |
767 |
.addClass(className || that.currentItem[0].className+" ui-sortable-placeholder") |
768 |
.removeClass("ui-sortable-helper"); |
769 |
|
770 |
if ( nodeName === "tr" ) { |
771 |
that.currentItem.children().each(function() { |
772 |
$( "<td> </td>", that.document[0] ) |
773 |
.attr( "colspan", $( this ).attr( "colspan" ) || 1 ) |
774 |
.appendTo( element ); |
775 |
}); |
776 |
} else if ( nodeName === "img" ) { |
777 |
element.attr( "src", that.currentItem.attr( "src" ) ); |
778 |
} |
779 |
|
780 |
if ( !className ) { |
781 |
element.css( "visibility", "hidden" ); |
782 |
} |
783 |
|
784 |
return element; |
785 |
}, |
786 |
update: function(container, p) { |
787 |
|
788 |
// 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that |
789 |
// 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified |
790 |
if(className && !o.forcePlaceholderSize) { |
791 |
return; |
792 |
} |
793 |
|
794 |
//If the element doesn't have a actual height by itself (without styles coming from a stylesheet), it receives the inline height from the dragged item |
795 |
if(!p.height()) { p.height(that.currentItem.innerHeight() - parseInt(that.currentItem.css("paddingTop")||0, 10) - parseInt(that.currentItem.css("paddingBottom")||0, 10)); } |
796 |
if(!p.width()) { p.width(that.currentItem.innerWidth() - parseInt(that.currentItem.css("paddingLeft")||0, 10) - parseInt(that.currentItem.css("paddingRight")||0, 10)); } |
797 |
} |
798 |
}; |
799 |
} |
800 |
|
801 |
//Create the placeholder |
802 |
that.placeholder = $(o.placeholder.element.call(that.element, that.currentItem)); |
803 |
|
804 |
//Append it after the actual current item |
805 |
that.currentItem.after(that.placeholder); |
806 |
|
807 |
//Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317) |
808 |
o.placeholder.update(that, that.placeholder); |
809 |
|
810 |
}, |
811 |
|
812 |
_contactContainers: function(event) { |
813 |
var i, j, dist, itemWithLeastDistance, posProperty, sizeProperty, base, cur, nearBottom, floating, |
814 |
innermostContainer = null, |
815 |
innermostIndex = null; |
816 |
|
817 |
// get innermost container that intersects with item |
818 |
for (i = this.containers.length - 1; i >= 0; i--) { |
819 |
|
820 |
// never consider a container that's located within the item itself |
821 |
if($.contains(this.currentItem[0], this.containers[i].element[0])) { |
822 |
continue; |
823 |
} |
824 |
|
825 |
if(this._intersectsWith(this.containers[i].containerCache)) { |
826 |
|
827 |
// if we've already found a container and it's more "inner" than this, then continue |
828 |
if(innermostContainer && $.contains(this.containers[i].element[0], innermostContainer.element[0])) { |
829 |
continue; |
830 |
} |
831 |
|
832 |
innermostContainer = this.containers[i]; |
833 |
innermostIndex = i; |
834 |
|
835 |
} else { |
836 |
// container doesn't intersect. trigger "out" event if necessary |
837 |
if(this.containers[i].containerCache.over) { |
838 |
this.containers[i]._trigger("out", event, this._uiHash(this)); |
839 |
this.containers[i].containerCache.over = 0; |
840 |
} |
841 |
} |
842 |
|
843 |
} |
844 |
|
845 |
// if no intersecting containers found, return |
846 |
if(!innermostContainer) { |
847 |
return; |
848 |
} |
849 |
|
850 |
// move the item into the container if it's not there already |
851 |
if(this.containers.length === 1) { |
852 |
if (!this.containers[innermostIndex].containerCache.over) { |
853 |
this.containers[innermostIndex]._trigger("over", event, this._uiHash(this)); |
854 |
this.containers[innermostIndex].containerCache.over = 1; |
855 |
} |
856 |
} else { |
857 |
|
858 |
//When entering a new container, we will find the item with the least distance and append our item near it |
859 |
dist = 10000; |
860 |
itemWithLeastDistance = null; |
861 |
floating = innermostContainer.floating || isFloating(this.currentItem); |
862 |
posProperty = floating ? "left" : "top"; |
863 |
sizeProperty = floating ? "width" : "height"; |
864 |
base = this.positionAbs[posProperty] + this.offset.click[posProperty]; |
865 |
for (j = this.items.length - 1; j >= 0; j--) { |
866 |
if(!$.contains(this.containers[innermostIndex].element[0], this.items[j].item[0])) { |
867 |
continue; |
868 |
} |
869 |
if(this.items[j].item[0] === this.currentItem[0]) { |
870 |
continue; |
871 |
} |
872 |
if (floating && !isOverAxis(this.positionAbs.top + this.offset.click.top, this.items[j].top, this.items[j].height)) { |
873 |
continue; |
874 |
} |
875 |
cur = this.items[j].item.offset()[posProperty]; |
876 |
nearBottom = false; |
877 |
if(Math.abs(cur - base) > Math.abs(cur + this.items[j][sizeProperty] - base)){ |
878 |
nearBottom = true; |
879 |
cur += this.items[j][sizeProperty]; |
880 |
} |
881 |
|
882 |
if(Math.abs(cur - base) < dist) { |
883 |
dist = Math.abs(cur - base); itemWithLeastDistance = this.items[j]; |
884 |
this.direction = nearBottom ? "up": "down"; |
885 |
} |
886 |
} |
887 |
|
888 |
//Check if dropOnEmpty is enabled |
889 |
if(!itemWithLeastDistance && !this.options.dropOnEmpty) { |
890 |
return; |
891 |
} |
892 |
|
893 |
if(this.currentContainer === this.containers[innermostIndex]) { |
894 |
return; |
895 |
} |
896 |
|
897 |
itemWithLeastDistance ? this._rearrange(event, itemWithLeastDistance, null, true) : this._rearrange(event, null, this.containers[innermostIndex].element, true); |
898 |
this._trigger("change", event, this._uiHash()); |
899 |
this.containers[innermostIndex]._trigger("change", event, this._uiHash(this)); |
900 |
this.currentContainer = this.containers[innermostIndex]; |
901 |
|
902 |
//Update the placeholder |
903 |
this.options.placeholder.update(this.currentContainer, this.placeholder); |
904 |
|
905 |
this.containers[innermostIndex]._trigger("over", event, this._uiHash(this)); |
906 |
this.containers[innermostIndex].containerCache.over = 1; |
907 |
} |
908 |
|
909 |
|
910 |
}, |
911 |
|
912 |
_createHelper: function(event) { |
913 |
|
914 |
var o = this.options, |
915 |
helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event, this.currentItem])) : (o.helper === "clone" ? this.currentItem.clone() : this.currentItem); |
916 |
|
917 |
//Add the helper to the DOM if that didn't happen already |
918 |
if(!helper.parents("body").length) { |
919 |
$(o.appendTo !== "parent" ? o.appendTo : this.currentItem[0].parentNode)[0].appendChild(helper[0]); |
920 |
} |
921 |
|
922 |
if(helper[0] === this.currentItem[0]) { |
923 |
this._storedCSS = { width: this.currentItem[0].style.width, height: this.currentItem[0].style.height, position: this.currentItem.css("position"), top: this.currentItem.css("top"), left: this.currentItem.css("left") }; |
924 |
} |
925 |
|
926 |
if(!helper[0].style.width || o.forceHelperSize) { |
927 |
helper.width(this.currentItem.width()); |
928 |
} |
929 |
if(!helper[0].style.height || o.forceHelperSize) { |
930 |
helper.height(this.currentItem.height()); |
931 |
} |
932 |
|
933 |
return helper; |
934 |
|
935 |
}, |
936 |
|
937 |
_adjustOffsetFromHelper: function(obj) { |
938 |
if (typeof obj === "string") { |
939 |
obj = obj.split(" "); |
940 |
} |
941 |
if ($.isArray(obj)) { |
942 |
obj = {left: +obj[0], top: +obj[1] || 0}; |
943 |
} |
944 |
if ("left" in obj) { |
945 |
this.offset.click.left = obj.left + this.margins.left; |
946 |
} |
947 |
if ("right" in obj) { |
948 |
this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left; |
949 |
} |
950 |
if ("top" in obj) { |
951 |
this.offset.click.top = obj.top + this.margins.top; |
952 |
} |
953 |
if ("bottom" in obj) { |
954 |
this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top; |
955 |
} |
956 |
}, |
957 |
|
958 |
_getParentOffset: function() { |
959 |
|
960 |
|
961 |
//Get the offsetParent and cache its position |
962 |
this.offsetParent = this.helper.offsetParent(); |
963 |
var po = this.offsetParent.offset(); |
964 |
|
965 |
// This is a special case where we need to modify a offset calculated on start, since the following happened: |
966 |
// 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent |
967 |
// 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that |
968 |
// the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag |
969 |
if(this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) { |
970 |
po.left += this.scrollParent.scrollLeft(); |
971 |
po.top += this.scrollParent.scrollTop(); |
972 |
} |
973 |
|
974 |
// This needs to be actually done for all browsers, since pageX/pageY includes this information |
975 |
// with an ugly IE fix |
976 |
if( this.offsetParent[0] === document.body || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) { |
977 |
po = { top: 0, left: 0 }; |
978 |
} |
979 |
|
980 |
return { |
981 |
top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0), |
982 |
left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0) |
983 |
}; |
984 |
|
985 |
}, |
986 |
|
987 |
_getRelativeOffset: function() { |
988 |
|
989 |
if(this.cssPosition === "relative") { |
990 |
var p = this.currentItem.position(); |
991 |
return { |
992 |
top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(), |
993 |
left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft() |
994 |
}; |
995 |
} else { |
996 |
return { top: 0, left: 0 }; |
997 |
} |
998 |
|
999 |
}, |
1000 |
|
1001 |
_cacheMargins: function() { |
1002 |
this.margins = { |
1003 |
left: (parseInt(this.currentItem.css("marginLeft"),10) || 0), |
1004 |
top: (parseInt(this.currentItem.css("marginTop"),10) || 0) |
1005 |
}; |
1006 |
}, |
1007 |
|
1008 |
_cacheHelperProportions: function() { |
1009 |
this.helperProportions = { |
1010 |
width: this.helper.outerWidth(), |
1011 |
height: this.helper.outerHeight() |
1012 |
}; |
1013 |
}, |
1014 |
|
1015 |
_setContainment: function() { |
1016 |
|
1017 |
var ce, co, over, |
1018 |
o = this.options; |
1019 |
if(o.containment === "parent") { |
1020 |
o.containment = this.helper[0].parentNode; |
1021 |
} |
1022 |
if(o.containment === "document" || o.containment === "window") { |
1023 |
this.containment = [ |
1024 |
0 - this.offset.relative.left - this.offset.parent.left, |
1025 |
0 - this.offset.relative.top - this.offset.parent.top, |
1026 |
$(o.containment === "document" ? document : window).width() - this.helperProportions.width - this.margins.left, |
1027 |
($(o.containment === "document" ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top |
1028 |
]; |
1029 |
} |
1030 |
|
1031 |
if(!(/^(document|window|parent)$/).test(o.containment)) { |
1032 |
ce = $(o.containment)[0]; |
1033 |
co = $(o.containment).offset(); |
1034 |
over = ($(ce).css("overflow") !== "hidden"); |
1035 |
|
1036 |
this.containment = [ |
1037 |
co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left, |
1038 |
co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top, |
1039 |
co.left+(over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left, |
1040 |
co.top+(over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top |
1041 |
]; |
1042 |
} |
1043 |
|
1044 |
}, |
1045 |
|
1046 |
_convertPositionTo: function(d, pos) { |
1047 |
|
1048 |
if(!pos) { |
1049 |
pos = this.position; |
1050 |
} |
1051 |
var mod = d === "absolute" ? 1 : -1, |
1052 |
scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, |
1053 |
scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName); |
1054 |
|
1055 |
return { |
1056 |
top: ( |
1057 |
pos.top + // The absolute mouse position |
1058 |
this.offset.relative.top * mod + // Only for relative positioned nodes: Relative offset from element to offset parent |
1059 |
this.offset.parent.top * mod - // The offsetParent's offset without borders (offset + border) |
1060 |
( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod) |
1061 |
), |
1062 |
left: ( |
1063 |
pos.left + // The absolute mouse position |
1064 |
this.offset.relative.left * mod + // Only for relative positioned nodes: Relative offset from element to offset parent |
1065 |
this.offset.parent.left * mod - // The offsetParent's offset without borders (offset + border) |
1066 |
( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod) |
1067 |
) |
1068 |
}; |
1069 |
|
1070 |
}, |
1071 |
|
1072 |
_generatePosition: function(event) { |
1073 |
|
1074 |
var top, left, |
1075 |
o = this.options, |
1076 |
pageX = event.pageX, |
1077 |
pageY = event.pageY, |
1078 |
scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName); |
1079 |
|
1080 |
// This is another very weird special case that only happens for relative elements: |
1081 |
// 1. If the css position is relative |
1082 |
// 2. and the scroll parent is the document or similar to the offset parent |
1083 |
// we have to refresh the relative offset during the scroll so there are no jumps |
1084 |
if(this.cssPosition === "relative" && !(this.scrollParent[0] !== document && this.scrollParent[0] !== this.offsetParent[0])) { |
1085 |
this.offset.relative = this._getRelativeOffset(); |
1086 |
} |
1087 |
|
1088 |
/* |
1089 |
* - Position constraining - |
1090 |
* Constrain the position to a mix of grid, containment. |
1091 |
*/ |
1092 |
|
1093 |
if(this.originalPosition) { //If we are not dragging yet, we won't check for options |
1094 |
|
1095 |
if(this.containment) { |
1096 |
if(event.pageX - this.offset.click.left < this.containment[0]) { |
1097 |
pageX = this.containment[0] + this.offset.click.left; |
1098 |
} |
1099 |
if(event.pageY - this.offset.click.top < this.containment[1]) { |
1100 |
pageY = this.containment[1] + this.offset.click.top; |
1101 |
} |
1102 |
if(event.pageX - this.offset.click.left > this.containment[2]) { |
1103 |
pageX = this.containment[2] + this.offset.click.left; |
1104 |
} |
1105 |
if(event.pageY - this.offset.click.top > this.containment[3]) { |
1106 |
pageY = this.containment[3] + this.offset.click.top; |
1107 |
} |
1108 |
} |
1109 |
|
1110 |
if(o.grid) { |
1111 |
top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1]; |
1112 |
pageY = this.containment ? ( (top - this.offset.click.top >= this.containment[1] && top - this.offset.click.top <= this.containment[3]) ? top : ((top - this.offset.click.top >= this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top; |
1113 |
|
1114 |
left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0]; |
1115 |
pageX = this.containment ? ( (left - this.offset.click.left >= this.containment[0] && left - this.offset.click.left <= this.containment[2]) ? left : ((left - this.offset.click.left >= this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left; |
1116 |
} |
1117 |
|
1118 |
} |
1119 |
|
1120 |
return { |
1121 |
top: ( |
1122 |
pageY - // The absolute mouse position |
1123 |
this.offset.click.top - // Click offset (relative to the element) |
1124 |
this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent |
1125 |
this.offset.parent.top + // The offsetParent's offset without borders (offset + border) |
1126 |
( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) )) |
1127 |
), |
1128 |
left: ( |
1129 |
pageX - // The absolute mouse position |
1130 |
this.offset.click.left - // Click offset (relative to the element) |
1131 |
this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent |
1132 |
this.offset.parent.left + // The offsetParent's offset without borders (offset + border) |
1133 |
( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() )) |
1134 |
) |
1135 |
}; |
1136 |
|
1137 |
}, |
1138 |
|
1139 |
_rearrange: function(event, i, a, hardRefresh) { |
1140 |
|
1141 |
a ? a[0].appendChild(this.placeholder[0]) : i.item[0].parentNode.insertBefore(this.placeholder[0], (this.direction === "down" ? i.item[0] : i.item[0].nextSibling)); |
1142 |
|
1143 |
//Various things done here to improve the performance: |
1144 |
// 1. we create a setTimeout, that calls refreshPositions |
1145 |
// 2. on the instance, we have a counter variable, that get's higher after every append |
1146 |
// 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same |
1147 |
// 4. this lets only the last addition to the timeout stack through |
1148 |
this.counter = this.counter ? ++this.counter : 1; |
1149 |
var counter = this.counter; |
1150 |
|
1151 |
this._delay(function() { |
1152 |
if(counter === this.counter) { |
1153 |
this.refreshPositions(!hardRefresh); //Precompute after each DOM insertion, NOT on mousemove |
1154 |
} |
1155 |
}); |
1156 |
|
1157 |
}, |
1158 |
|
1159 |
_clear: function(event, noPropagation) { |
1160 |
|
1161 |
this.reverting = false; |
1162 |
// We delay all events that have to be triggered to after the point where the placeholder has been removed and |
1163 |
// everything else normalized again |
1164 |
var i, |
1165 |
delayedTriggers = []; |
1166 |
|
1167 |
// We first have to update the dom position of the actual currentItem |
1168 |
// Note: don't do it if the current item is already removed (by a user), or it gets reappended (see #4088) |
1169 |
if(!this._noFinalSort && this.currentItem.parent().length) { |
1170 |
this.placeholder.before(this.currentItem); |
1171 |
} |
1172 |
this._noFinalSort = null; |
1173 |
|
1174 |
if(this.helper[0] === this.currentItem[0]) { |
1175 |
for(i in this._storedCSS) { |
1176 |
if(this._storedCSS[i] === "auto" || this._storedCSS[i] === "static") { |
1177 |
this._storedCSS[i] = ""; |
1178 |
} |
1179 |
} |
1180 |
this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"); |
1181 |
} else { |
1182 |
this.currentItem.show(); |
1183 |
} |
1184 |
|
1185 |
if(this.fromOutside && !noPropagation) { |
1186 |
delayedTriggers.push(function(event) { this._trigger("receive", event, this._uiHash(this.fromOutside)); }); |
1187 |
} |
1188 |
if((this.fromOutside || this.domPosition.prev !== this.currentItem.prev().not(".ui-sortable-helper")[0] || this.domPosition.parent !== this.currentItem.parent()[0]) && !noPropagation) { |
1189 |
delayedTriggers.push(function(event) { this._trigger("update", event, this._uiHash()); }); //Trigger update callback if the DOM position has changed |
1190 |
} |
1191 |
|
1192 |
// Check if the items Container has Changed and trigger appropriate |
1193 |
// events. |
1194 |
if (this !== this.currentContainer) { |
1195 |
if(!noPropagation) { |
1196 |
delayedTriggers.push(function(event) { this._trigger("remove", event, this._uiHash()); }); |
1197 |
delayedTriggers.push((function(c) { return function(event) { c._trigger("receive", event, this._uiHash(this)); }; }).call(this, this.currentContainer)); |
1198 |
delayedTriggers.push((function(c) { return function(event) { c._trigger("update", event, this._uiHash(this)); }; }).call(this, this.currentContainer)); |
1199 |
} |
1200 |
} |
1201 |
|
1202 |
|
1203 |
//Post events to containers |
1204 |
for (i = this.containers.length - 1; i >= 0; i--){ |
1205 |
if(!noPropagation) { |
1206 |
delayedTriggers.push((function(c) { return function(event) { c._trigger("deactivate", event, this._uiHash(this)); }; }).call(this, this.containers[i])); |
1207 |
} |
1208 |
if(this.containers[i].containerCache.over) { |
1209 |
delayedTriggers.push((function(c) { return function(event) { c._trigger("out", event, this._uiHash(this)); }; }).call(this, this.containers[i])); |
1210 |
this.containers[i].containerCache.over = 0; |
1211 |
} |
1212 |
} |
1213 |
|
1214 |
//Do what was originally in plugins |
1215 |
if ( this.storedCursor ) { |
1216 |
this.document.find( "body" ).css( "cursor", this.storedCursor ); |
1217 |
this.storedStylesheet.remove(); |
1218 |
} |
1219 |
if(this._storedOpacity) { |
1220 |
this.helper.css("opacity", this._storedOpacity); |
1221 |
} |
1222 |
if(this._storedZIndex) { |
1223 |
this.helper.css("zIndex", this._storedZIndex === "auto" ? "" : this._storedZIndex); |
1224 |
} |
1225 |
|
1226 |
this.dragging = false; |
1227 |
if(this.cancelHelperRemoval) { |
1228 |
if(!noPropagation) { |
1229 |
this._trigger("beforeStop", event, this._uiHash()); |
1230 |
for (i=0; i < delayedTriggers.length; i++) { |
1231 |
delayedTriggers[i].call(this, event); |
1232 |
} //Trigger all delayed events |
1233 |
this._trigger("stop", event, this._uiHash()); |
1234 |
} |
1235 |
|
1236 |
this.fromOutside = false; |
1237 |
return false; |
1238 |
} |
1239 |
|
1240 |
if(!noPropagation) { |
1241 |
this._trigger("beforeStop", event, this._uiHash()); |
1242 |
} |
1243 |
|
1244 |
//$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node! |
1245 |
this.placeholder[0].parentNode.removeChild(this.placeholder[0]); |
1246 |
|
1247 |
if(this.helper[0] !== this.currentItem[0]) { |
1248 |
this.helper.remove(); |
1249 |
} |
1250 |
this.helper = null; |
1251 |
|
1252 |
if(!noPropagation) { |
1253 |
for (i=0; i < delayedTriggers.length; i++) { |
1254 |
delayedTriggers[i].call(this, event); |
1255 |
} //Trigger all delayed events |
1256 |
this._trigger("stop", event, this._uiHash()); |
1257 |
} |
1258 |
|
1259 |
this.fromOutside = false; |
1260 |
return true; |
1261 |
|
1262 |
}, |
1263 |
|
1264 |
_trigger: function() { |
1265 |
if ($.Widget.prototype._trigger.apply(this, arguments) === false) { |
1266 |
this.cancel(); |
1267 |
} |
1268 |
}, |
1269 |
|
1270 |
_uiHash: function(_inst) { |
1271 |
var inst = _inst || this; |
1272 |
return { |
1273 |
helper: inst.helper, |
1274 |
placeholder: inst.placeholder || $([]), |
1275 |
position: inst.position, |
1276 |
originalPosition: inst.originalPosition, |
1277 |
offset: inst.positionAbs, |
1278 |
item: inst.currentItem, |
1279 |
sender: _inst ? _inst.element : null |
1280 |
}; |
1281 |
} |
1282 |
|
1283 |
}); |
1284 |
|
1285 |
})(jQuery); |