add jqueyr sortable lib

This commit is contained in:
perploug
2013-09-04 15:43:24 +02:00
parent d59963a53e
commit fd06c1b785
2 changed files with 624 additions and 0 deletions

View File

@@ -0,0 +1,607 @@
/* ===================================================
* jquery-sortable.js v0.9.11
* http://johnny.github.com/jquery-sortable/
* ===================================================
* Copyright (c) 2012 Jonas von Andrian
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ========================================================== */
!function ( $, window, undefined){
var eventNames,
pluginName = 'sortable',
containerDefaults = {
// If true, items can be dragged from this container
drag: true,
// If true, items can be droped onto this container
drop: true,
// Exclude items from being draggable, if the
// selector matches the item
exclude: "",
// If true, search for nested containers within an item
nested: true,
// If true, the items are assumed to be arranged vertically
vertical: true
}, // end container defaults
groupDefaults = {
// This is executed after the placeholder has been moved.
afterMove: function ($placeholder, container) {
},
// The exact css path between the container and its items, e.g. "> tbody"
containerPath: "",
// The css selector of the containers
containerSelector: "ol, ul",
// Distance the mouse has to travel to start dragging
distance: 0,
// The css selector of the drag handle
handle: "",
// The exact css path between the item and its subcontainers
itemPath: "",
// The css selector of the items
itemSelector: "li",
// Check if the dragged item may be inside the container.
// Use with care, since the search for a valid container entails a depth first search
// and may be quite expensive.
isValidTarget: function ($item, container) {
return true
},
// Executed before onDrop if placeholder is detached.
// This happens if pullPlaceholder is set to false and the drop occurs outside a container.
onCancel: function ($item, container, _super) {
},
// Executed at the beginning of a mouse move event.
// The Placeholder has not been moved yet.
onDrag: function ($item, position, _super) {
$item.css(position)
},
// Called after the drag has been started,
// that is the mouse button is beeing held down and
// the mouse is moving.
// The container is the closest initialized container.
// Therefore it might not be the container, that actually contains the item.
onDragStart: function ($item, container, _super) {
$item.css({
height: $item.height(),
width: $item.width()
})
$item.addClass("dragged")
$("body").addClass("dragging")
},
// Called when the mouse button is beeing released
onDrop: function ($item, container, _super) {
$item.removeClass("dragged").removeAttr("style")
$("body").removeClass("dragging")
},
// Called on mousedown.
onMousedown: function($item, event, _super) {
event.preventDefault()
},
// Template for the placeholder. Can be any valid jQuery input
// e.g. a string, a DOM element.
// The placeholder must have the class "placeholder"
placeholder: '<li class="placeholder"/>',
// If true, the position of the placeholder is calculated on every mousemove.
// If false, it is only calculated when the mouse is above a container.
pullPlaceholder: true,
// Specifies serialization of the container group.
// The pair $parent/$children is either container/items or item/subcontainers.
// Note that this default method only works, if every item only has one subcontainer
serialize: function ($parent, $children, parentIsContainer) {
var result = $.extend({}, $parent.data())
if(parentIsContainer)
return $children
else if ($children[0]){
result.children = $children
delete result.subContainer
}
delete result.sortable
return result
},
// Set tolerance while dragging. Positive values decrease sensitivity,
// negative values increase it.
tolerance: 0
}, // end group defaults
containerGroups = {},
groupCounter = 0,
emptyBox = {
left: 0,
top: 0,
bottom: 0,
right:0
}
eventNames = {
start: "touchstart.sortable mousedown.sortable",
drop: "touchend.sortable touchcancel.sortable mouseup.sortable",
drag: "touchmove.sortable mousemove.sortable",
scroll: "scroll.sortable"
}
/*
* a is Array [left, right, top, bottom]
* b is array [left, top]
*/
function d(a,b) {
var x = Math.max(0, a[0] - b[0], b[0] - a[1]),
y = Math.max(0, a[2] - b[1], b[1] - a[3])
return x+y;
}
function setDimensions(array, dimensions, tolerance, useOffset) {
var i = array.length,
offsetMethod = useOffset ? "offset" : "position"
tolerance = tolerance || 0
while(i--){
var el = array[i].el ? array[i].el : $(array[i]),
// use fitting method
pos = el[offsetMethod]()
pos.left += parseInt(el.css('margin-left'), 10)
pos.top += parseInt(el.css('margin-top'),10)
dimensions[i] = [
pos.left - tolerance,
pos.left + el.outerWidth() + tolerance,
pos.top - tolerance,
pos.top + el.outerHeight() + tolerance
]
}
}
function getRelativePosition(pointer, element) {
var offset = element.offset()
return {
left: pointer.left - offset.left,
top: pointer.top - offset.top
}
}
function sortByDistanceDesc(dimensions, pointer, lastPointer) {
pointer = [pointer.left, pointer.top]
lastPointer = lastPointer && [lastPointer.left, lastPointer.top]
var dim,
i = dimensions.length,
distances = []
while(i--){
dim = dimensions[i]
distances[i] = [i,d(dim,pointer), lastPointer && d(dim, lastPointer)]
}
distances = distances.sort(function (a,b) {
return b[1] - a[1] || b[2] - a[2] || b[0] - a[0]
})
// last entry is the closest
return distances
}
function ContainerGroup(options) {
this.options = $.extend({}, groupDefaults, options)
this.containers = []
this.scrollProxy = $.proxy(this.scroll, this)
this.dragProxy = $.proxy(this.drag, this)
this.dropProxy = $.proxy(this.drop, this)
if(!this.options.parentContainer){
this.placeholder = $(this.options.placeholder)
if(!options.isValidTarget)
this.options.isValidTarget = undefined
}
}
ContainerGroup.get = function (options) {
if( !containerGroups[options.group]) {
if(!options.group)
options.group = groupCounter ++
containerGroups[options.group] = new ContainerGroup(options)
}
return containerGroups[options.group]
}
ContainerGroup.prototype = {
dragInit: function (e, itemContainer) {
this.$document = $(itemContainer.el[0].ownerDocument)
if(itemContainer.enabled()){
this.toggleListeners('on')
// get item to drag
this.item = $(e.target).closest(this.options.itemSelector)
this.itemContainer = itemContainer
this.setPointer(e)
this.options.onMousedown(this.item, e, groupDefaults.onMousedown)
} else {
this.toggleListeners('on', ['drop'])
}
this.dragInitDone = true
},
drag: function (e) {
if(!this.dragging){
if(!this.distanceMet(e))
return
this.options.onDragStart(this.item, this.itemContainer, groupDefaults.onDragStart)
this.item.before(this.placeholder)
this.dragging = true
}
this.setPointer(e)
// place item under the cursor
this.options.onDrag(this.item,
getRelativePosition(this.pointer, this.item.offsetParent()),
groupDefaults.onDrag)
var x = e.pageX,
y = e.pageY,
box = this.sameResultBox,
t = this.options.tolerance
if(!box || box.top - t > y || box.bottom + t < y || box.left - t > x || box.right + t < x)
if(!this.searchValidTarget())
this.placeholder.detach()
},
drop: function (e) {
this.toggleListeners('off')
this.dragInitDone = false
if(this.dragging){
// processing Drop, check if placeholder is detached
if(this.placeholder.closest("html")[0])
this.placeholder.before(this.item).detach()
else
this.options.onCancel(this.item, this.itemContainer, groupDefaults.onCancel)
this.options.onDrop(this.item, this.getContainer(this.item), groupDefaults.onDrop)
// cleanup
this.clearDimensions()
this.clearOffsetParent()
this.lastAppendedItem = this.sameResultBox = undefined
this.dragging = false
}
},
searchValidTarget: function (pointer, lastPointer) {
if(!pointer){
pointer = this.relativePointer || this.pointer
lastPointer = this.lastRelativePointer || this.lastPointer
}
var distances = sortByDistanceDesc(this.getContainerDimensions(),
pointer,
lastPointer),
i = distances.length
while(i--){
var index = distances[i][0],
distance = distances[i][1]
if(!distance || this.options.pullPlaceholder){
var container = this.containers[index]
if(!container.disabled){
if(!this.$getOffsetParent()){
var offsetParent = container.getItemOffsetParent()
pointer = getRelativePosition(pointer, offsetParent)
lastPointer = getRelativePosition(lastPointer, offsetParent)
}
if(container.searchValidTarget(pointer, lastPointer))
return true
}
}
}
if(this.sameResultBox)
this.sameResultBox = undefined
},
movePlaceholder: function (container, item, method, sameResultBox) {
var lastAppendedItem = this.lastAppendedItem
if(!sameResultBox && lastAppendedItem && lastAppendedItem[0] === item[0])
return;
item[method](this.placeholder)
this.lastAppendedItem = item
this.sameResultBox = sameResultBox
this.options.afterMove(this.placeholder, container)
},
getContainerDimensions: function () {
if(!this.containerDimensions)
setDimensions(this.containers, this.containerDimensions = [], this.options.tolerance, !this.$getOffsetParent())
return this.containerDimensions
},
getContainer: function (element) {
return element.closest(this.options.containerSelector).data(pluginName)
},
$getOffsetParent: function () {
if(this.offsetParent === undefined){
var i = this.containers.length - 1,
offsetParent = this.containers[i].getItemOffsetParent()
if(!this.options.parentContainer){
while(i--){
if(offsetParent[0] != this.containers[i].getItemOffsetParent()[0]){
// If every container has the same offset parent,
// use position() which is relative to this parent,
// otherwise use offset()
// compare #setDimensions
offsetParent = false
break;
}
}
}
this.offsetParent = offsetParent
}
return this.offsetParent
},
setPointer: function (e) {
var pointer = {
left: e.pageX,
top: e.pageY
}
if(this.$getOffsetParent()){
var relativePointer = getRelativePosition(pointer, this.$getOffsetParent())
this.lastRelativePointer = this.relativePointer
this.relativePointer = relativePointer
}
this.lastPointer = this.pointer
this.pointer = pointer
},
distanceMet: function (e) {
return (Math.max(
Math.abs(this.pointer.left - e.pageX),
Math.abs(this.pointer.top - e.pageY)
) >= this.options.distance)
},
scroll: function (e) {
this.clearDimensions()
this.clearOffsetParent()
},
toggleListeners: function (method, events) {
var that = this
events = events || ['drag','drop','scroll']
$.each(events,function (i,event) {
that.$document[method](eventNames[event], that[event + 'Proxy'])
})
},
clearOffsetParent: function () {
this.offsetParent = undefined
},
// Recursively clear container and item dimensions
clearDimensions: function () {
this.containerDimensions = undefined
var i = this.containers.length
while(i--){
this.containers[i].clearDimensions()
}
}
}
function Container(element, options) {
this.el = element
this.options = $.extend( {}, containerDefaults, options)
this.group = ContainerGroup.get(this.options)
this.rootGroup = this.options.rootGroup = this.options.rootGroup || this.group
this.parentContainer = this.options.parentContainer
this.handle = this.rootGroup.options.handle || this.rootGroup.options.itemSelector
this.el.on(eventNames.start, this.handle, $.proxy(this.dragInit, this))
if(this.options.drop)
this.group.containers.push(this)
}
Container.prototype = {
dragInit: function (e) {
var rootGroup = this.rootGroup
if( !rootGroup.dragInitDone &&
e.which === 1 &&
this.options.drag &&
!$(e.target).is(this.options.exclude))
rootGroup.dragInit(e, this)
},
searchValidTarget: function (pointer, lastPointer) {
var distances = sortByDistanceDesc(this.getItemDimensions(),
pointer,
lastPointer),
i = distances.length,
rootGroup = this.rootGroup,
validTarget = !rootGroup.options.isValidTarget ||
rootGroup.options.isValidTarget(rootGroup.item, this)
if(!i && validTarget){
rootGroup.movePlaceholder(this, this.el, "append")
return true
} else
while(i--){
var index = distances[i][0],
distance = distances[i][1]
if(!distance && this.hasChildGroup(index)){
var found = this.getContainerGroup(index).searchValidTarget(pointer, lastPointer)
if(found)
return true
}
else if(validTarget){
this.movePlaceholder(index, pointer)
return true
}
}
},
movePlaceholder: function (index, pointer) {
var item = $(this.items[index]),
dim = this.itemDimensions[index],
method = "after",
width = item.outerWidth(),
height = item.outerHeight(),
offset = item.offset(),
sameResultBox = {
left: offset.left,
right: offset.left + width,
top: offset.top,
bottom: offset.top + height
}
if(this.options.vertical){
var yCenter = (dim[2] + dim[3]) / 2,
inUpperHalf = pointer.top <= yCenter
if(inUpperHalf){
method = "before"
sameResultBox.bottom -= height / 2
} else
sameResultBox.top += height / 2
} else {
var xCenter = (dim[0] + dim[1]) / 2,
inLeftHalf = pointer.left <= xCenter
if(inLeftHalf){
method = "before"
sameResultBox.right -= width / 2
} else
sameResultBox.left += width / 2
}
if(this.hasChildGroup(index))
sameResultBox = emptyBox
this.rootGroup.movePlaceholder(this, item, method, sameResultBox)
},
getItemDimensions: function () {
if(!this.itemDimensions){
this.items = this.$getChildren(this.el, "item").filter(":not(.placeholder, .dragged)").get()
setDimensions(this.items, this.itemDimensions = [], this.options.tolerance)
}
return this.itemDimensions
},
getItemOffsetParent: function () {
var offsetParent,
el = this.el
// Since el might be empty we have to check el itself and
// can not do something like el.children().first().offsetParent()
if(el.css("position") === "relative" || el.css("position") === "absolute" || el.css("position") === "fixed")
offsetParent = el
else
offsetParent = el.offsetParent()
return offsetParent
},
hasChildGroup: function (index) {
return this.options.nested && this.getContainerGroup(index)
},
getContainerGroup: function (index) {
var childGroup = $.data(this.items[index], "subContainer")
if( childGroup === undefined){
var childContainers = this.$getChildren(this.items[index], "container")
childGroup = false
if(childContainers[0]){
var options = $.extend({}, this.options, {
parentContainer: this,
group: groupCounter ++
})
childGroup = childContainers[pluginName](options).data(pluginName).group
}
$.data(this.items[index], "subContainer", childGroup)
}
return childGroup
},
enabled: function () {
return !this.disabled && (!this.parentContainer || this.parentContainer.enabled())
},
$getChildren: function (parent, type) {
var options = this.rootGroup.options,
path = options[type + "Path"],
selector = options[type + "Selector"]
parent = $(parent)
if(path)
parent = parent.find(path)
return parent.children(selector)
},
_serialize: function (parent, isContainer) {
var that = this,
childType = isContainer ? "item" : "container",
children = this.$getChildren(parent, childType).not(this.options.exclude).map(function () {
return that._serialize($(this), !isContainer)
}).get()
return this.rootGroup.options.serialize(parent, children, isContainer)
},
clearDimensions: function () {
this.itemDimensions = undefined
if(this.items && this.items[0]){
var i = this.items.length
while(i--){
var group = $.data(this.items[i], "subContainer")
if(group)
group.clearDimensions()
}
}
}
}
var API = {
enable: function (ignoreChildren) {
this.disabled = false
},
disable: function (ignoreChildren) {
this.disabled = true
},
serialize: function () {
return this._serialize(this.el, true)
}
}
$.extend(Container.prototype, API)
/**
* jQuery API
*
* Parameters are
* either options on init
* or a method name followed by arguments to pass to the method
*/
$.fn[pluginName] = function(methodOrOptions) {
var args = Array.prototype.slice.call(arguments, 1)
return this.map(function(){
var $t = $(this),
object = $t.data(pluginName)
if(object && API[methodOrOptions])
return API[methodOrOptions].apply(object, args) || this
else if(!object && (methodOrOptions === undefined ||
typeof methodOrOptions === "object"))
$t.data(pluginName, new Container($t, methodOrOptions))
return this
});
};
}(jQuery, window)
;

View File

@@ -0,0 +1,17 @@
!function(e,x,h){function r(a,b){var c=Math.max(0,a[0]-b[0],b[0]-a[1]),d=Math.max(0,a[2]-b[1],b[1]-a[3]);return c+d}function s(a,b,c,d){for(var f=a.length,d=d?"offset":"position",c=c||0;f--;){var k=a[f].el?a[f].el:e(a[f]),i=k[d]();i.left+=parseInt(k.css("margin-left"),10);i.top+=parseInt(k.css("margin-top"),10);b[f]=[i.left-c,i.left+k.outerWidth()+c,i.top-c,i.top+k.outerHeight()+c]}}function l(a,b){var c=b.offset();return{left:a.left-c.left,top:a.top-c.top}}function t(a,b,c){for(var b=[b.left,b.top],
c=c&&[c.left,c.top],d,f=a.length,e=[];f--;)d=a[f],e[f]=[f,r(d,b),c&&r(d,c)];return e=e.sort(function(a,b){return b[1]-a[1]||b[2]-a[2]||b[0]-a[0]})}function m(a){this.options=e.extend({},j,a);this.containers=[];this.scrollProxy=e.proxy(this.scroll,this);this.dragProxy=e.proxy(this.drag,this);this.dropProxy=e.proxy(this.drop,this);this.options.parentContainer||(this.placeholder=e(this.options.placeholder),a.isValidTarget||(this.options.isValidTarget=h))}function n(a,b){this.el=a;this.options=e.extend({},
v,b);this.group=m.get(this.options);this.rootGroup=this.options.rootGroup=this.options.rootGroup||this.group;this.parentContainer=this.options.parentContainer;this.handle=this.rootGroup.options.handle||this.rootGroup.options.itemSelector;this.el.on(o.start,this.handle,e.proxy(this.dragInit,this));this.options.drop&&this.group.containers.push(this)}var o,v={drag:!0,drop:!0,exclude:"",nested:!0,vertical:!0},j={afterMove:function(){},containerPath:"",containerSelector:"ol, ul",distance:0,handle:"",itemPath:"",
itemSelector:"li",isValidTarget:function(){return!0},onCancel:function(){},onDrag:function(a,b){a.css(b)},onDragStart:function(a){a.css({height:a.height(),width:a.width()});a.addClass("dragged");e("body").addClass("dragging")},onDrop:function(a){a.removeClass("dragged").removeAttr("style");e("body").removeClass("dragging")},onMousedown:function(a,b){b.preventDefault()},placeholder:'<li class="placeholder"/>',pullPlaceholder:!0,serialize:function(a,b,c){a=e.extend({},a.data());if(c)return b;b[0]&&
(a.children=b,delete a.subContainer);delete a.sortable;return a},tolerance:0},p={},u=0,w={left:0,top:0,bottom:0,right:0};o={start:"touchstart.sortable mousedown.sortable",drop:"touchend.sortable touchcancel.sortable mouseup.sortable",drag:"touchmove.sortable mousemove.sortable",scroll:"scroll.sortable"};m.get=function(a){p[a.group]||(a.group||(a.group=u++),p[a.group]=new m(a));return p[a.group]};m.prototype={dragInit:function(a,b){this.$document=e(b.el[0].ownerDocument);b.enabled()?(this.toggleListeners("on"),
this.item=e(a.target).closest(this.options.itemSelector),this.itemContainer=b,this.setPointer(a),this.options.onMousedown(this.item,a,j.onMousedown)):this.toggleListeners("on",["drop"]);this.dragInitDone=!0},drag:function(a){if(!this.dragging){if(!this.distanceMet(a))return;this.options.onDragStart(this.item,this.itemContainer,j.onDragStart);this.item.before(this.placeholder);this.dragging=!0}this.setPointer(a);this.options.onDrag(this.item,l(this.pointer,this.item.offsetParent()),j.onDrag);var b=
a.pageX,a=a.pageY,c=this.sameResultBox,d=this.options.tolerance;if(!c||c.top-d>a||c.bottom+d<a||c.left-d>b||c.right+d<b)this.searchValidTarget()||this.placeholder.detach()},drop:function(){this.toggleListeners("off");this.dragInitDone=!1;if(this.dragging){if(this.placeholder.closest("html")[0])this.placeholder.before(this.item).detach();else this.options.onCancel(this.item,this.itemContainer,j.onCancel);this.options.onDrop(this.item,this.getContainer(this.item),j.onDrop);this.clearDimensions();this.clearOffsetParent();
this.lastAppendedItem=this.sameResultBox=h;this.dragging=!1}},searchValidTarget:function(a,b){a||(a=this.relativePointer||this.pointer,b=this.lastRelativePointer||this.lastPointer);for(var c=t(this.getContainerDimensions(),a,b),d=c.length;d--;){var f=c[d][0];if(!c[d][1]||this.options.pullPlaceholder)if(f=this.containers[f],!f.disabled){if(!this.$getOffsetParent())var e=f.getItemOffsetParent(),a=l(a,e),b=l(b,e);if(f.searchValidTarget(a,b))return!0}}this.sameResultBox&&(this.sameResultBox=h)},movePlaceholder:function(a,
b,c,d){var f=this.lastAppendedItem;if(d||!(f&&f[0]===b[0]))b[c](this.placeholder),this.lastAppendedItem=b,this.sameResultBox=d,this.options.afterMove(this.placeholder,a)},getContainerDimensions:function(){this.containerDimensions||s(this.containers,this.containerDimensions=[],this.options.tolerance,!this.$getOffsetParent());return this.containerDimensions},getContainer:function(a){return a.closest(this.options.containerSelector).data("sortable")},$getOffsetParent:function(){if(this.offsetParent===
h){var a=this.containers.length-1,b=this.containers[a].getItemOffsetParent();if(!this.options.parentContainer)for(;a--;)if(b[0]!=this.containers[a].getItemOffsetParent()[0]){b=!1;break}this.offsetParent=b}return this.offsetParent},setPointer:function(a){a={left:a.pageX,top:a.pageY};if(this.$getOffsetParent()){var b=l(a,this.$getOffsetParent());this.lastRelativePointer=this.relativePointer;this.relativePointer=b}this.lastPointer=this.pointer;this.pointer=a},distanceMet:function(a){return Math.max(Math.abs(this.pointer.left-
a.pageX),Math.abs(this.pointer.top-a.pageY))>=this.options.distance},scroll:function(){this.clearDimensions();this.clearOffsetParent()},toggleListeners:function(a,b){var c=this,b=b||["drag","drop","scroll"];e.each(b,function(b,f){c.$document[a](o[f],c[f+"Proxy"])})},clearOffsetParent:function(){this.offsetParent=h},clearDimensions:function(){this.containerDimensions=h;for(var a=this.containers.length;a--;)this.containers[a].clearDimensions()}};n.prototype={dragInit:function(a){var b=this.rootGroup;
!b.dragInitDone&&1===a.which&&this.options.drag&&!e(a.target).is(this.options.exclude)&&b.dragInit(a,this)},searchValidTarget:function(a,b){var c=t(this.getItemDimensions(),a,b),d=c.length,f=this.rootGroup,e=!f.options.isValidTarget||f.options.isValidTarget(f.item,this);if(!d&&e)return f.movePlaceholder(this,this.el,"append"),!0;for(;d--;)if(f=c[d][0],!c[d][1]&&this.hasChildGroup(f)){if(this.getContainerGroup(f).searchValidTarget(a,b))return!0}else if(e)return this.movePlaceholder(f,a),!0},movePlaceholder:function(a,
b){var c=e(this.items[a]),d=this.itemDimensions[a],f="after",h=c.outerWidth(),i=c.outerHeight(),g=c.offset(),g={left:g.left,right:g.left+h,top:g.top,bottom:g.top+i};this.options.vertical?b.top<=(d[2]+d[3])/2?(f="before",g.bottom-=i/2):g.top+=i/2:b.left<=(d[0]+d[1])/2?(f="before",g.right-=h/2):g.left+=h/2;this.hasChildGroup(a)&&(g=w);this.rootGroup.movePlaceholder(this,c,f,g)},getItemDimensions:function(){this.itemDimensions||(this.items=this.$getChildren(this.el,"item").filter(":not(.placeholder, .dragged)").get(),
s(this.items,this.itemDimensions=[],this.options.tolerance));return this.itemDimensions},getItemOffsetParent:function(){var a=this.el;return"relative"===a.css("position")||"absolute"===a.css("position")||"fixed"===a.css("position")?a:a.offsetParent()},hasChildGroup:function(a){return this.options.nested&&this.getContainerGroup(a)},getContainerGroup:function(a){var b=e.data(this.items[a],"subContainer");if(b===h){var c=this.$getChildren(this.items[a],"container"),b=!1;c[0]&&(b=e.extend({},this.options,
{parentContainer:this,group:u++}),b=c.sortable(b).data("sortable").group);e.data(this.items[a],"subContainer",b)}return b},enabled:function(){return!this.disabled&&(!this.parentContainer||this.parentContainer.enabled())},$getChildren:function(a,b){var c=this.rootGroup.options,d=c[b+"Path"],c=c[b+"Selector"],a=e(a);d&&(a=a.find(d));return a.children(c)},_serialize:function(a,b){var c=this,d=this.$getChildren(a,b?"item":"container").not(this.options.exclude).map(function(){return c._serialize(e(this),
!b)}).get();return this.rootGroup.options.serialize(a,d,b)},clearDimensions:function(){this.itemDimensions=h;if(this.items&&this.items[0])for(var a=this.items.length;a--;){var b=e.data(this.items[a],"subContainer");b&&b.clearDimensions()}}};var q={enable:function(){this.disabled=!1},disable:function(){this.disabled=!0},serialize:function(){return this._serialize(this.el,!0)}};e.extend(n.prototype,q);e.fn.sortable=function(a){var b=Array.prototype.slice.call(arguments,1);return this.map(function(){var c=
e(this),d=c.data("sortable");if(d&&q[a])return q[a].apply(d,b)||this;!d&&(a===h||"object"===typeof a)&&c.data("sortable",new n(c,a));return this})}}(jQuery,window);