Update and decouple drag.sort, add usePlaceholderClone option, recompile assets

This commit is contained in:
Samuel Georges 2015-07-15 19:09:23 +10:00
parent f0f1c549c9
commit 095721bd40
5 changed files with 4367 additions and 16449 deletions

File diff suppressed because it is too large Load Diff

View File

@ -961,7 +961,6 @@ if(this.$overlay)this.$overlay.addClass('in')
$(document.body).addClass('popover-open')
var showEvent=jQuery.Event('show.oc.popover',{relatedTarget:this.$container.get(0)})
this.$el.trigger(showEvent)
this.$container.on('mousedown',function(e){e.stopPropagation();})
this.$container.on('close.oc.popover',function(e){self.hide()})
this.$container.on('click','[data-dismiss=popover]',function(e){self.hide()
return false})
@ -1011,7 +1010,10 @@ $container=$(this.options.container),containerOffset=$container.offset()
result.x-=containerOffset.left
result.y-=containerOffset.top
return result}
Popover.prototype.onDocumentClick=function(){if(this.options.closeOnPageClick)
Popover.prototype.onDocumentClick=function(e){if(!this.options.closeOnPageClick)
return
if($.contains(this.$container.get(0),e.target))
return
this.hide();}
Popover.DEFAULTS={placement:'bottom',fallbackPlacement:'bottom',content:'<p>Popover content<p>',width:false,modal:false,highlightModalTarget:false,closeOnPageClick:true,closeOnEsc:true,container:false,containerClass:null,offset:15,useAnimation:false}
var old=$.fn.ocPopover
@ -1269,8 +1271,8 @@ self.trigger('select',{originalEvent:evt,data:data});});this.$results.on('mousee
var $options=this.$results.find('[aria-selected]');var currentIndex=$options.index($highlighted);var currentOffset=this.$results.offset().top;var nextTop=$highlighted.offset().top;var nextOffset=this.$results.scrollTop()+(nextTop-currentOffset);var offsetDelta=nextTop-currentOffset;nextOffset-=$highlighted.outerHeight(false)*2;if(currentIndex<=2){this.$results.scrollTop(0);}else if(offsetDelta>this.$results.outerHeight()||offsetDelta<0){this.$results.scrollTop(nextOffset);}};Results.prototype.template=function(result,container){var template=this.options.get('templateResult');var escapeMarkup=this.options.get('escapeMarkup');var content=template(result);if(content==null){container.style.display='none';}else if(typeof content==='string'){container.innerHTML=escapeMarkup(content);}else{$(container).append(content);}};return Results;});S2.define('select2/keys',[],function(){var KEYS={BACKSPACE:8,TAB:9,ENTER:13,SHIFT:16,CTRL:17,ALT:18,ESC:27,SPACE:32,PAGE_UP:33,PAGE_DOWN:34,END:35,HOME:36,LEFT:37,UP:38,RIGHT:39,DOWN:40,DELETE:46};return KEYS;});S2.define('select2/selection/base',['jquery','../utils','../keys'],function($,Utils,KEYS){function BaseSelection($element,options){this.$element=$element;this.options=options;BaseSelection.__super__.constructor.call(this);}
Utils.Extend(BaseSelection,Utils.Observable);BaseSelection.prototype.render=function(){var $selection=$('<span class="select2-selection" role="combobox" '+'aria-autocomplete="list" aria-haspopup="true" aria-expanded="false">'+'</span>');this._tabindex=0;if(this.$element.data('old-tabindex')!=null){this._tabindex=this.$element.data('old-tabindex');}else if(this.$element.attr('tabindex')!=null){this._tabindex=this.$element.attr('tabindex');}
$selection.attr('title',this.$element.attr('title'));$selection.attr('tabindex',this._tabindex);this.$selection=$selection;return $selection;};BaseSelection.prototype.bind=function(container,$container){var self=this;var id=container.id+'-container';var resultsId=container.id+'-results';this.container=container;this.$selection.on('focus',function(evt){self.trigger('focus',evt);});this.$selection.on('blur',function(evt){self._handleBlur(evt);});this.$selection.on('keydown',function(evt){self.trigger('keypress',evt);if(evt.which===KEYS.SPACE){evt.preventDefault();}});container.on('results:focus',function(params){self.$selection.attr('aria-activedescendant',params.data._resultId);});container.on('selection:update',function(params){self.update(params.data);});container.on('open',function(){self.$selection.attr('aria-expanded','true');self.$selection.attr('aria-owns',resultsId);self._attachCloseHandler(container);});container.on('close',function(){self.$selection.attr('aria-expanded','false');self.$selection.removeAttr('aria-activedescendant');self.$selection.removeAttr('aria-owns');self.$selection.focus();self._detachCloseHandler(container);});container.on('enable',function(){self.$selection.attr('tabindex',self._tabindex);});container.on('disable',function(){self.$selection.attr('tabindex','-1');});};BaseSelection.prototype._handleBlur=function(evt){var self=this;window.setTimeout(function(){if((document.activeElement==self.$selection[0])||($.contains(self.$selection[0],document.activeElement))){return;}
self.trigger('blur',evt);},1);};BaseSelection.prototype._attachCloseHandler=function(container){var self=this;$(document.body).on('click.select2.'+container.id,function(e){var $target=$(e.target);var $select=$target.closest('.select2');var $all=$('.select2.select2-container--open');$all.each(function(){var $this=$(this);if(this==$select[0]){return;}
var $element=$this.data('element');$element.select2('close');});});};BaseSelection.prototype._detachCloseHandler=function(container){$(document.body).off('click.select2.'+container.id);};BaseSelection.prototype.position=function($selection,$container){var $selectionContainer=$container.find('.selection');$selectionContainer.append($selection);};BaseSelection.prototype.destroy=function(){this._detachCloseHandler(this.container);};BaseSelection.prototype.update=function(data){throw new Error('The `update` method must be defined in child classes.');};return BaseSelection;});S2.define('select2/selection/single',['jquery','./base','../utils','../keys'],function($,BaseSelection,Utils,KEYS){function SingleSelection(){SingleSelection.__super__.constructor.apply(this,arguments);}
self.trigger('blur',evt);},1);};BaseSelection.prototype._attachCloseHandler=function(container){var self=this;$(document.body).on('mousedown.select2.'+container.id,function(e){var $target=$(e.target);var $select=$target.closest('.select2');var $all=$('.select2.select2-container--open');$all.each(function(){var $this=$(this);if(this==$select[0]){return;}
var $element=$this.data('element');$element.select2('close');});});};BaseSelection.prototype._detachCloseHandler=function(container){$(document.body).off('mousedown.select2.'+container.id);};BaseSelection.prototype.position=function($selection,$container){var $selectionContainer=$container.find('.selection');$selectionContainer.append($selection);};BaseSelection.prototype.destroy=function(){this._detachCloseHandler(this.container);};BaseSelection.prototype.update=function(data){throw new Error('The `update` method must be defined in child classes.');};return BaseSelection;});S2.define('select2/selection/single',['jquery','./base','../utils','../keys'],function($,BaseSelection,Utils,KEYS){function SingleSelection(){SingleSelection.__super__.constructor.apply(this,arguments);}
Utils.Extend(SingleSelection,BaseSelection);SingleSelection.prototype.render=function(){var $selection=SingleSelection.__super__.render.call(this);$selection.addClass('select2-selection--single');$selection.html('<span class="select2-selection__rendered"></span>'+'<span class="select2-selection__arrow" role="presentation">'+'<b role="presentation"></b>'+'</span>');return $selection;};SingleSelection.prototype.bind=function(container,$container){var self=this;SingleSelection.__super__.bind.apply(this,arguments);var id=container.id+'-container';this.$selection.find('.select2-selection__rendered').attr('id',id);this.$selection.attr('aria-labelledby',id);this.$selection.on('mousedown',function(evt){if(evt.which!==1){return;}
self.trigger('toggle',{originalEvent:evt});});this.$selection.on('focus',function(evt){});this.$selection.on('blur',function(evt){});container.on('selection:update',function(params){self.update(params.data);});};SingleSelection.prototype.clear=function(){this.$selection.find('.select2-selection__rendered').empty();};SingleSelection.prototype.display=function(data,container){var template=this.options.get('templateSelection');var escapeMarkup=this.options.get('escapeMarkup');return escapeMarkup(template(data,container));};SingleSelection.prototype.selectionContainer=function(){return $('<span></span>');};SingleSelection.prototype.update=function(data){if(data.length===0){this.clear();return;}
var selection=data[0];var $rendered=this.$selection.find('.select2-selection__rendered');var formatted=this.display(selection,$rendered);$rendered.empty().append(formatted);$rendered.prop('title',selection.title||selection.text);};return SingleSelection;});S2.define('select2/selection/multiple',['jquery','./base','../utils'],function($,BaseSelection,Utils){function MultipleSelection($element,options){MultipleSelection.__super__.constructor.apply(this,arguments);}
@ -1528,7 +1530,7 @@ return'<i class="select-icon '+iconClass+'"></i> '+state.text
if(imageSrc)
return'<img class="select-image" src="'+imageSrc+'" alt="" /> '+state.text
return state.text}
var selectOptions={templateResult:formatSelectOption,templateSelection:formatSelectOption,escapeMarkup:function(m){return m}}
var selectOptions={templateResult:formatSelectOption,templateSelection:formatSelectOption,escapeMarkup:function(m){return m},width:'style'}
$('select.custom-select').each(function(){var $element=$(this),extraOptions={}
if($element.data('select2')!=null){return true;}
$element.attr('data-disposable','data-disposable')
@ -2441,7 +2443,7 @@ return'<i class="select-icon '+iconClass+'"></i> '+state.text
if(imageSrc)
return'<img class="select-image" src="'+imageSrc+'" alt="" /> '+state.text
return state.text}
var selectOptions={templateResult:formatSelectOption,templateSelection:formatSelectOption,escapeMarkup:function(m){return m}}
var selectOptions={templateResult:formatSelectOption,templateSelection:formatSelectOption,escapeMarkup:function(m){return m},width:'style'}
$('select.custom-select').each(function(){var $element=$(this),extraOptions={}
if($element.data('select2')!=null){return true;}
$element.attr('data-disposable','data-disposable')
@ -2578,7 +2580,6 @@ if(this.$overlay)this.$overlay.addClass('in')
$(document.body).addClass('popover-open')
var showEvent=jQuery.Event('show.oc.popover',{relatedTarget:this.$container.get(0)})
this.$el.trigger(showEvent)
this.$container.on('mousedown',function(e){e.stopPropagation();})
this.$container.on('close.oc.popover',function(e){self.hide()})
this.$container.on('click','[data-dismiss=popover]',function(e){self.hide()
return false})
@ -2628,7 +2629,10 @@ $container=$(this.options.container),containerOffset=$container.offset()
result.x-=containerOffset.left
result.y-=containerOffset.top
return result}
Popover.prototype.onDocumentClick=function(){if(this.options.closeOnPageClick)
Popover.prototype.onDocumentClick=function(e){if(!this.options.closeOnPageClick)
return
if($.contains(this.$container.get(0),e.target))
return
this.hide();}
Popover.DEFAULTS={placement:'bottom',fallbackPlacement:'bottom',content:'<p>Popover content<p>',width:false,modal:false,highlightModalTarget:false,closeOnPageClick:true,closeOnEsc:true,container:false,containerClass:null,offset:15,useAnimation:false}
var old=$.fn.ocPopover
@ -4194,26 +4198,17 @@ return result?result:this}
$.fn.dragValue.Constructor=DragValue
$.fn.dragValue.noConflict=function(){$.fn.dragValue=old
return this}
$(document).render(function(){$('[data-control="dragvalue"]').dragValue()});}(window.jQuery);+function($){"use strict";var eventNames,cursorAdjustment,containerDefaults={drag:true,drop:true,exclude:"",nested:true,vertical:true},groupDefaults={afterMove:function($placeholder,container,$closestEl){},containerPath:"",containerSelector:"ol, ul",distance:0,delay:0,handle:"",itemPath:"",useAnimation:false,itemSelector:"li",isValidTarget:function($item,container){return true},onCancel:function($item,container,_super,event){},tweakCursorAdjustment:function(adjustment){return adjustment},onDragStart:function($item,container,_super,event){var offset=$item.offset(),pointer=container.rootGroup.pointer
if(pointer){cursorAdjustment={left:pointer.left-offset.left,top:pointer.top-offset.top}}
else{cursorAdjustment=null}
cursorAdjustment=this.tweakCursorAdjustment(cursorAdjustment)
$item.css({height:$item.height(),width:$item.width()})
if(this.useAnimation)
$item.data('oc.animated',true)
$item.addClass("dragged")
$("body").addClass("dragging")},onDrag:function($item,position,_super,event){if(cursorAdjustment){$item.css({left:position.left-cursorAdjustment.left,top:position.top-cursorAdjustment.top})}
else{$item.css(position)}},onDrop:function($item,container,_super,event){$item.removeClass("dragged").removeAttr("style")
$("body").removeClass("dragging")
if($item.data('oc.animated')){$item.hide()
$item.slideDown(200)}},onMousedown:function($item,_super,event){if(event.target.nodeName!='INPUT'&&event.target.nodeName!='SELECT'){event.preventDefault()
return true}},placeholder:'<li class="placeholder"/>',pullPlaceholder:true,serialize:function($parent,$children,parentIsContainer){var result=$.extend({},$parent.data())
$(document).render(function(){$('[data-control="dragvalue"]').dragValue()});}(window.jQuery);!function($,window,pluginName,undefined){var containerDefaults={drag:true,drop:true,exclude:"",nested:true,vertical:true},groupDefaults={afterMove:function($placeholder,container,$closestItemOrContainer){},containerPath:"",containerSelector:"ol, ul",distance:0,delay:0,handle:"",itemPath:"",itemSelector:"li",bodyClass:"dragging",draggedClass:"dragged",isValidTarget:function($item,container){return true},onCancel:function($item,container,_super,event){},onDrag:function($item,position,_super,event){$item.css(position)},onDragStart:function($item,container,_super,event){$item.css({height:$item.outerHeight(),width:$item.outerWidth()})
$item.addClass(container.group.options.draggedClass)
$("body").addClass(container.group.options.bodyClass)},onDrop:function($item,container,_super,event){$item.removeClass(container.group.options.draggedClass).removeAttr("style")
$("body").removeClass(container.group.options.bodyClass)},onMousedown:function($item,_super,event){if(!event.target.nodeName.match(/^(input|select|textarea)$/i)){event.preventDefault()
return true}},placeholderClass:"placeholder",placeholder:'<li class="placeholder"></li>',pullPlaceholder:true,serialize:function($parent,$children,parentIsContainer){var result=$.extend({},$parent.data())
if(parentIsContainer)
return $children
else if($children[0]){result.children=$children
delete result.subContainer}
return[$children]
else if($children[0]){result.children=$children}
delete result.subContainers
delete result.sortable
return result},tolerance:0},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"}
return result},tolerance:0},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"},subContainerKey="subContainers"
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"
@ -4233,36 +4228,31 @@ distances=distances.sort(function(a,b){return b[1]-a[1]||b[2]-a[2]||b[0]-a[0]})
return distances}
function ContainerGroup(options){this.options=$.extend({},groupDefaults,options)
this.containers=[]
if(!this.options.parentContainer){this.scrollProxy=$.proxy(this.scroll,this)
if(!this.options.rootGroup){this.scrollProxy=$.proxy(this.scroll,this)
this.dragProxy=$.proxy(this.drag,this)
this.dropProxy=$.proxy(this.drop,this)
this.placeholder=$(this.options.placeholder)
if(!options.isValidTarget)
this.options.isValidTarget=undefined}}
ContainerGroup.get=function(options){if(!containerGroups[options.group]){if(!options.group)
ContainerGroup.get=function(options){if(!containerGroups[options.group]){if(options.group===undefined)
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.item=$(e.target).closest(this.options.itemSelector)
this.itemContainer=itemContainer
if(this.item.is(this.options.exclude)||!this.options.onMousedown(this.item,groupDefaults.onMousedown,e)){return}
this.setPointer(e)
this.toggleListeners('on')}else{this.toggleListeners('on',['drop'])}
this.setupDelayTimer()
this.dragInitDone=true},drag:function(e){if(!this.dragging){if(!this.distanceMet(e)||!this.delayMet){return}
var closestItem=$(e.target).closest(this.options.itemSelector);if(closestItem.length){this.item=closestItem;this.itemContainer=itemContainer;if(this.item.is(this.options.exclude)||!this.options.onMousedown(this.item,groupDefaults.onMousedown,e)){return;}
this.setPointer(e);this.toggleListeners('on');this.setupDelayTimer();this.dragInitDone=true;}},drag:function(e){if(!this.dragging){if(!this.distanceMet(e)||!this.delayMet)
return
this.options.onDragStart(this.item,this.itemContainer,groupDefaults.onDragStart,e)
this.item.before(this.placeholder)
this.dragging=true}
this.setPointer(e)
this.options.onDrag(this.item,getRelativePosition(this.pointer,this.item.offsetParent()),groupDefaults.onDrag,e)
var x=e.pageX||e.originalEvent.pageX,y=e.pageY||e.originalEvent.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')
var p=this.getPointer(e),box=this.sameResultBox,t=this.options.tolerance
if(!box||box.top-t>p.top||box.bottom+t<p.top||box.left-t>p.left||box.right+t<p.left)
if(!this.searchValidTarget()){this.placeholder.detach()
this.lastAppendedItem=undefined}},drop:function(e){this.toggleListeners('off')
this.dragInitDone=false
if(this.dragging){if(this.placeholder.closest("html")[0])
this.placeholder.before(this.item).detach()
else
this.options.onCancel(this.item,this.itemContainer,groupDefaults.onCancel,e)
if(this.dragging){if(this.placeholder.closest("html")[0]){this.placeholder.before(this.item).detach()}else{this.options.onCancel(this.item,this.itemContainer,groupDefaults.onCancel,e)}
this.options.onDrop(this.item,this.getContainer(this.item),groupDefaults.onDrop,e)
this.clearDimensions()
this.clearOffsetParent()
@ -4285,8 +4275,8 @@ this.lastAppendedItem=item
this.sameResultBox=sameResultBox
this.options.afterMove(this.placeholder,container,item)},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('oc.sortable')},$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]){offsetParent=false
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.rootGroup){while(i--){if(offsetParent[0]!=this.containers[i].getItemOffsetParent()[0]){offsetParent=false
break;}}}
this.offsetParent=offsetParent}
return this.offsetParent},setPointer:function(e){var pointer=this.getPointer(e)
@ -4295,72 +4285,122 @@ this.lastRelativePointer=this.relativePointer
this.relativePointer=relativePointer}
this.lastPointer=this.pointer
this.pointer=pointer},distanceMet:function(e){var currentPointer=this.getPointer(e)
return(Math.max(Math.abs(this.pointer.left-currentPointer.left),Math.abs(this.pointer.top-currentPointer.top))>=this.options.distance)},getPointer:function(e){return{left:e.pageX||e.originalEvent.pageX,top:e.pageY||e.originalEvent.pageY}},setupDelayTimer:function(){var self=this
return(Math.max(Math.abs(this.pointer.left-currentPointer.left),Math.abs(this.pointer.top-currentPointer.top))>=this.options.distance)},getPointer:function(e){var o=e.originalEvent||e.originalEvent.touches&&e.originalEvent.touches[0]
return{left:e.pageX||o.pageX,top:e.pageY||o.pageY}},setupDelayTimer:function(){var that=this
this.delayMet=!this.options.delay
if(!this.delayMet){clearTimeout(this._mouseDelayTimer);this._mouseDelayTimer=setTimeout(function(){self.delayMet=true},this.options.delay)}},scroll:function(e){this.clearDimensions()
this.clearOffsetParent()},toggleListeners:function(method,events){var self=this
events=events||['drag','drop','scroll']
$.each(events,function(i,event){self.$document[method](eventNames[event],self[event+'Proxy'])})},clearOffsetParent:function(){this.offsetParent=undefined},clearDimensions:function(){this.containerDimensions=undefined
if(!this.delayMet){clearTimeout(this._mouseDelayTimer);this._mouseDelayTimer=setTimeout(function(){that.delayMet=true},this.options.delay)}},scroll:function(e){this.clearDimensions()
this.clearOffsetParent()},toggleListeners:function(method){var that=this,events=['drag','drop','scroll']
$.each(events,function(i,event){that.$document[method](eventNames[event],that[event+'Proxy'])})},clearOffsetParent:function(){this.offsetParent=undefined},clearDimensions:function(){this.traverse(function(object){object._clearDimensions()})},traverse:function(callback){callback(this)
var i=this.containers.length
while(i--){this.containers[i].clearDimensions()}},destroy:function(){containerGroups[this.options.group]=undefined}}
while(i--){this.containers[i].traverse(callback)}},_clearDimensions:function(){this.containerDimensions=undefined},_destroy:function(){containerGroups[this.options.group]=undefined}}
function Container(element,options){this.el=element
this.options=$.extend({},containerDefaults,options)
this.group=ContainerGroup.get(this.options)
this.rootGroup=this.options.rootGroup||this.group
this.parentContainer=this.options.parentContainer
this.handle=this.rootGroup.options.handle||this.rootGroup.options.itemSelector
var itemPath=this.rootGroup.options.itemPath,target=itemPath?this.el.find(itemPath):this.el
target.on(eventNames.start,this.handle,$.proxy(this.dragInit,this))
if(this.options.drop){this.group.containers.push(this)}}
var itemPath=this.rootGroup.options.itemPath
this.target=itemPath?this.el.find(itemPath):this.el
this.target.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&&this.options.drag){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){var itemPath=this.rootGroup.options.itemPath,target=itemPath?this.el.find(itemPath):this.el
rootGroup.movePlaceholder(this,target,"append")
return true}else{while(i--){var index=distances[i][0],distance=distances[i][1]
if(!this.disabled&&!rootGroup.dragInitDone&&this.options.drag&&this.isValidDrag(e)){rootGroup.dragInit(e,this)}},isValidDrag:function(e){return e.which==1||e.type=="touchstart"&&e.originalEvent.touches.length==1},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.target,"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}
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
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()
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(."+this.group.options.placeholderClass+", ."+this.group.options.draggedClass+")").get()
setDimensions(this.items,this.itemDimensions=[],this.options.tolerance)}
return this.itemDimensions},getItemOffsetParent:function(){var offsetParent,el=this.el
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")
return offsetParent},hasChildGroup:function(index){return this.options.nested&&this.getContainerGroup(index)},getContainerGroup:function(index){var childGroup=$.data(this.items[index],subContainerKey)
if(childGroup===undefined){var childContainers=this.$getChildren(this.items[index],"container")
childGroup=false
if(childContainers[0]){var options=$.extend({},this.options,{parentContainer:this,rootGroup:this.rootGroup,group:groupCounter++})
childGroup=childContainers.sortable(options).data('oc.sortable').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"]
if(childContainers[0]){var options=$.extend({},this.options,{rootGroup:this.rootGroup,group:groupCounter++})
childGroup=childContainers[pluginName](options).data(pluginName).group}
$.data(this.items[index],subContainerKey,childGroup)}
return childGroup},$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 self=this,childType=isContainer?"item":"container",children=this.$getChildren(parent,childType).not(this.options.exclude).map(function(){return self._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")
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)},traverse:function(callback){$.each(this.items||[],function(item){var group=$.data(this,subContainerKey)
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)},destroy:function(){this.rootGroup.destroy()
$(this.el).data('oc.sortable')}}
group.traverse(callback)});callback(this)},_clearDimensions:function(){this.itemDimensions=undefined},_destroy:function(){var that=this;this.target.off(eventNames.start,this.handle);this.el.removeData(pluginName)
if(this.options.drop)
this.group.containers=$.grep(this.group.containers,function(val){return val!=that})
$.each(this.items||[],function(){$.removeData(this,subContainerKey)})}}
var API={enable:function(){this.traverse(function(object){object.disabled=false})},disable:function(){this.traverse(function(object){object.disabled=true})},serialize:function(){return this._serialize(this.el,true)},refresh:function(){this.traverse(function(object){object._clearDimensions()})},destroy:function(){this.traverse(function(object){object._destroy();})}}
$.extend(Container.prototype,API)
$.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,'jqSortable');+function($){"use strict";var Base=$.oc.foundation.base,BaseProto=Base.prototype
var Sortable=function(element,options){this.$el=$(element)
this.options=options||{}
this.cursorAdjustment=null
$.oc.foundation.controlUtils.markDisposable(element)
Base.call(this)
this.init()}
Sortable.prototype=Object.create(BaseProto)
Sortable.prototype.constructor=Sortable
Sortable.prototype.init=function(){this.$el.one('dispose-control',this.proxy(this.dispose))
var sortableOptions={onDragStart:this.proxy(this.onDragStart),onDrag:this.proxy(this.onDrag),onDrop:this.proxy(this.onDrop)}
this.$el.jqSortable($.extend(sortableOptions,this.options))}
Sortable.prototype.dispose=function(){this.$el.jqSortable('destroy')
this.$el.off('dispose-control',this.proxy(this.dispose))
this.$el.removeData('oc.sortable')
this.$el=null
this.options=null
this.cursorAdjustment=null
BaseProto.dispose.call(this)}
Sortable.prototype.onDrag=function($item,position,_super,event){if(this.cursorAdjustment){$item.css({left:position.left-this.cursorAdjustment.left,top:position.top-this.cursorAdjustment.top})}
else{$item.css(position)}}
Sortable.prototype.onDragStart=function($item,container,_super,event){var offset=$item.offset(),pointer=container.rootGroup.pointer
if(pointer){this.cursorAdjustment={left:pointer.left-offset.left,top:pointer.top-offset.top}}
else{this.cursorAdjustment=null}
if(this.options.tweakCursorAdjustment){this.cursorAdjustment=this.options.tweakCursorAdjustment(this.cursorAdjustment)}
$item.css({height:$item.height(),width:$item.width()})
$item.addClass('dragged')
$('body').addClass('dragging')
if(this.options.useAnimation){$item.data('oc.animated',true)}
if(this.options.usePlaceholderClone){$(container.rootGroup.placeholder).html($item.html())}}
Sortable.prototype.onDrop=function($item,container,_super,event){$item.removeClass('dragged').removeAttr('style')
$('body').removeClass('dragging')
if($item.data('oc.animated')){$item.hide().slideDown(200)}}
Sortable.prototype.enable=function(){this.$el.jqSortable('enable')}
Sortable.prototype.disable=function(){this.$el.jqSortable('disable')}
Sortable.prototype.refresh=function(){this.$el.jqSortable('refresh')}
Sortable.prototype.serialize=function(){this.$el.jqSortable('serialize')}
Sortable.prototype.destroy=function(){this.dispose()}
Sortable.DEFAULTS={useAnimation:false,usePlaceholderClone:false,tweakCursorAdjustment:null}
var old=$.fn.sortable
$.fn.sortable=function(option){var args=Array.prototype.slice.call(arguments,1)
return this.map(function(){var $this=$(this),object=$this.data('oc.sortable')
if(object&&API[option])
return API[option].apply(object,args)||this
else if(!object&&(option===undefined||typeof option==="object")){$this.data('oc.sortable',new Container($this,option))}
return this});};$.fn.sortable.noConflict=function(){$.fn.sortable=old
$.fn.sortable=function(option){var args=arguments;return this.each(function(){var $this=$(this)
var data=$this.data('oc.sortable')
var options=$.extend({},Sortable.DEFAULTS,$this.data(),typeof option=='object'&&option)
if(!data)$this.data('oc.sortable',(data=new Sortable(this,options)))
if(typeof option=='string')data[option].apply(data,args)})}
$.fn.sortable.Constructor=Sortable
$.fn.sortable.noConflict=function(){$.fn.sortable=old
return this}}(window.jQuery);+function($){'use strict';var Tab=function(element){this.element=$(element)}
Tab.prototype.show=function(){var $this=this.element
var $ul=$this.closest('ul:not(.dropdown-menu)')

View File

@ -2,6 +2,85 @@
Allows the dragging of things.
### JavaScript API:
The `sortable()` method must be invoked on valid containers, meaning they must match the containerSelector option.
`.sortable('enable')`
Enable all instantiated sortables in the set of matched elements
`.sortable('disable')`
Disable all instantiated sortables in the set of matched elements
`.sortable('refresh')`
Reset all cached element dimensions
`.sortable('destroy')`
Remove the sortable plugin from the set of matched elements
`.sortable('serialize')`
Serialize all selected containers. Returns a jQuery object . Use .get() to retrieve the array, if needed.
### Supported options:
- `useAnimation`: Use animation when an item is removed or inserted into the tree.
- `usePlaceholderClone`: Placeholder should be a clone of the item being dragged.
- `afterMove`: This is executed after the placeholder has been moved. $closestItemOrContainer contains the closest item, the placeholder has been put at or the closest empty Container, the placeholder has been appended to.
- `containerPath`: The exact css path between the container and its items, e.g. "> tbody"
- `containerSelector`: The css selector of the containers
- `distance`: Distance the mouse has to travel to start dragging
- `delay`: Time in milliseconds after mousedown until dragging should start. This option can be used to prevent unwanted drags when clicking on an element.
- `handle`: The css selector of the drag handle
- `itemPath`: The exact css path between the item and its subcontainers. It should only match the immediate items of a container. No item of a subcontainer should be matched. E.g. for ol>div>li the itemPath is "> div"
- `itemSelector`: The css selector of the items
- `bodyClass`: The class given to "body" while an item is being dragged
- `draggedClass`: The class giving to an item while being dragged
- `isValidTarget`: 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.
- `onCancel`: Executed before onDrop if placeholder is detached. This happens if pullPlaceholder is set to false and the drop occurs outside a container.
- `onDrag`: Executed at the beginning of a mouse move event. The Placeholder has not been moved yet.
- `onDragStart`: Called after the drag has been started, that is the mouse button is being 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.
- `onDrop`: Called when the mouse button is being released
- `onMousedown`: Called on mousedown. If falsy value is returned, the dragging will not start. Ignore if element clicked is input, select or textarea
- `placeholderClass`: The class of the placeholder (must match placeholder option markup)
- `placeholder`: Template for the placeholder. Can be any valid jQuery input e.g. a string, a DOM element. The placeholder must have the class "placeholder"
- `pullPlaceholder`: 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.
- `serialize`: Specifies serialization of the container group. The pair $parent/$children is either container/items or item/subcontainers.
- `tolerance`: Set tolerance while dragging. Positive values decrease sensitivity, negative values increase it.
### Supported options (container specific):
- `drag`: If true, items can be dragged from this container
- `drop`: If true, items can be droped onto this container
- `exclude`: Exclude items from being draggable, if the selector matches the item
- `nested`: If true, search for nested containers within an item.If you nest containers, either the original selector with which you call the plugin must only match the top containers, or you need to specify a group (see the bootstrap nav example)
- `vertical`: If true, the items are assumed to be arranged vertically
# Example
<ol id="sortExample">

View File

@ -1,707 +1,173 @@
/*
* Sortable plugin, a forked version of johnny's sortable plugin.
*
* Forked from: https://github.com/johnny/jquery-sortable/tree/1563f32858cfe250051ad0a573425569c49d631f
*
* Note: Consider using october.simplelist.js with "is-sortable" class.
*
* Usage:
* <ol class='example'>
* <li>First</li>
* <li>Second</li>
* <li>Third</li>
* </ol>
*
* $(function () {
* $("ol.example").sortable()
* })
*
* Basic CSS requirements:
*
* body.dragging, body.dragging * { cursor: move !important }
* .dragged { position: absolute; opacity: 0.5; z-index: 2000; }
* ol.example li.placeholder { position: relative; }
*
* More examples:
*
* http://johnny.github.io/jquery-sortable/
=require ../vendor/sortable/jquery-sortable.js
*/
/*
* Sortable plugin.
*
* - Documentation: ../docs/drag-sort.md
* - Note: Consider using october.simplelist.js with "is-sortable" class.
*/
+function ($) { "use strict";
+function ($) { "use strict";
var Base = $.oc.foundation.base,
BaseProto = Base.prototype
var eventNames,
cursorAdjustment,
containerDefaults = {
drag: true, // Items can be dragged from this container
drop: true, // Items can be droped onto this container
exclude: "", // Exclude items from being draggable, if the selector matches the item
nested: true, // Search for nested containers within an item
vertical: true // The items are assumed to be arranged vertically
},
groupDefaults = {
afterMove: function ($placeholder, container, $closestEl) {},
// 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,
// Time in milliseconds after mousedown until dragging should start.
// This option can be used to prevent unwanted drags when clicking on an element.
delay: 0,
// The css selector of the drag handle
handle: "",
// The exact css path between the item and its subcontainers
itemPath: "",
// Use animation when an item is removed or inserted into the tree
useAnimation : false,
// 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, event) {
},
var Sortable = function (element, options) {
this.$el = $(element)
this.options = options || {}
this.cursorAdjustment = null
tweakCursorAdjustment: function(adjustment) {
return adjustment
},
// 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, event) {
// Relative cursors position
var offset = $item.offset(),
pointer = container.rootGroup.pointer
if (pointer) {
cursorAdjustment = {
left: pointer.left - offset.left,
top: pointer.top - offset.top
}
}
else {
cursorAdjustment = null
}
cursorAdjustment = this.tweakCursorAdjustment(cursorAdjustment)
$item.css({
height: $item.height(),
width: $item.width()
})
if (this.useAnimation)
$item.data('oc.animated', true)
$item.addClass("dragged")
$("body").addClass("dragging")
},
// Executed at the beginning of a mouse move event.
// The Placeholder has not been moved yet.
onDrag: function ($item, position, _super, event) {
if (cursorAdjustment) {
// Relative cursors position
$item.css({
left: position.left - cursorAdjustment.left,
top: position.top - cursorAdjustment.top
})
}
else {
// Default behavior
$item.css(position)
}
},
// Called when the mouse button is being released
onDrop: function ($item, container, _super, event) {
$item.removeClass("dragged").removeAttr("style")
$("body").removeClass("dragging")
if ($item.data('oc.animated')) {
$item.hide()
$item.slideDown(200)
}
},
// Called on mousedown. If falsy value is returned, the dragging will not start.
onMousedown: function ($item, _super, event) {
if (event.target.nodeName != 'INPUT' && event.target.nodeName != 'SELECT') {
event.preventDefault()
return true
}
},
// 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
},
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;
$.oc.foundation.controlUtils.markDisposable(element)
Base.call(this)
this.init()
}
function setDimensions(array, dimensions, tolerance, useOffset) {
var i = array.length,
offsetMethod = useOffset ? "offset" : "position"
tolerance = tolerance || 0
Sortable.prototype = Object.create(BaseProto)
Sortable.prototype.constructor = Sortable
while (i--) {
var el = array[i].el ? array[i].el : $(array[i]),
pos = el[offsetMethod]() // use fitting method
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
]
Sortable.prototype.init = function() {
this.$el.one('dispose-control', this.proxy(this.dispose))
var sortableOptions = {
onDragStart: this.proxy(this.onDragStart),
onDrag: this.proxy(this.onDrag),
onDrop: this.proxy(this.onDrop)
}
this.$el.jqSortable($.extend(sortableOptions, this.options))
}
Sortable.prototype.dispose = function() {
this.$el.jqSortable('destroy')
this.$el.off('dispose-control', this.proxy(this.dispose))
this.$el.removeData('oc.sortable')
this.$el = null
this.options = null
this.cursorAdjustment = null
BaseProto.dispose.call(this)
}
Sortable.prototype.onDrag = function ($item, position, _super, event) {
if (this.cursorAdjustment) {
/*
* Relative cursor position
*/
$item.css({
left: position.left - this.cursorAdjustment.left,
top: position.top - this.cursorAdjustment.top
})
}
else {
/*
* Default behavior
*/
$item.css(position)
}
}
function getRelativePosition(pointer, element) {
var offset = element.offset()
return {
left: pointer.left - offset.left,
top: pointer.top - offset.top
Sortable.prototype.onDragStart = function ($item, container, _super, event) {
/*
* Relative cursor position
*/
var offset = $item.offset(),
pointer = container.rootGroup.pointer
if (pointer) {
this.cursorAdjustment = {
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)]
else {
this.cursorAdjustment = null
}
distances = distances.sort(function (a, b) {
return b[1] - a[1] || b[2] - a[2] || b[0] - a[0]
if (this.options.tweakCursorAdjustment) {
this.cursorAdjustment = this.options.tweakCursorAdjustment(this.cursorAdjustment)
}
$item.css({
height: $item.height(),
width: $item.width()
})
return distances // last entry is the closest
$item.addClass('dragged')
$('body').addClass('dragging')
/*
* Use animation
*/
if (this.options.useAnimation) {
$item.data('oc.animated', true)
}
/*
* Placeholder clone
*/
if (this.options.usePlaceholderClone) {
$(container.rootGroup.placeholder).html($item.html())
}
}
function ContainerGroup(options) {
this.options = $.extend({}, groupDefaults, options)
this.containers = []
Sortable.prototype.onDrop = function ($item, container, _super, event) {
$item.removeClass('dragged').removeAttr('style')
$('body').removeClass('dragging')
if (!this.options.parentContainer) {
this.scrollProxy = $.proxy(this.scroll, this)
this.dragProxy = $.proxy(this.drag, this)
this.dropProxy = $.proxy(this.drop, this)
this.placeholder = $(this.options.placeholder)
if (!options.isValidTarget)
this.options.isValidTarget = undefined
if ($item.data('oc.animated')) {
$item
.hide()
.slideDown(200)
}
}
ContainerGroup.get = function (options) {
if (!containerGroups[options.group]) {
if (!options.group)
options.group = groupCounter++
//
// Proxy API
//
containerGroups[options.group] = new ContainerGroup(options)
}
return containerGroups[options.group]
Sortable.prototype.enable = function() {
this.$el.jqSortable('enable')
}
ContainerGroup.prototype = {
dragInit: function (e, itemContainer) {
this.$document = $(itemContainer.el[0].ownerDocument)
if (itemContainer.enabled()) {
// get item to drag
this.item = $(e.target).closest(this.options.itemSelector)
this.itemContainer = itemContainer
if (this.item.is(this.options.exclude) ||
!this.options.onMousedown(this.item, groupDefaults.onMousedown, e)){
return
}
this.setPointer(e)
this.toggleListeners('on')
} else {
this.toggleListeners('on', ['drop'])
}
this.setupDelayTimer()
this.dragInitDone = true
},
drag: function (e) {
if (!this.dragging) {
if (!this.distanceMet(e) || !this.delayMet) {
return
}
this.options.onDragStart(this.item, this.itemContainer, groupDefaults.onDragStart, e)
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,
e)
var x = e.pageX || e.originalEvent.pageX,
y = e.pageY || e.originalEvent.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, e)
this.options.onDrop(this.item, this.getContainer(this.item), groupDefaults.onDrop, e)
// 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, item)
},
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('oc.sortable')
},
$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 = this.getPointer(e)
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) {
var currentPointer = this.getPointer(e)
return (Math.max(
Math.abs(this.pointer.left - currentPointer.left),
Math.abs(this.pointer.top - currentPointer.top)
) >= this.options.distance)
},
getPointer: function(e) {
return {
left: e.pageX || e.originalEvent.pageX,
top: e.pageY || e.originalEvent.pageY
}
},
setupDelayTimer: function () {
var self = this
this.delayMet = !this.options.delay
if (!this.delayMet) {
clearTimeout(this._mouseDelayTimer);
this._mouseDelayTimer = setTimeout(function() {
self.delayMet = true
}, this.options.delay)
}
},
scroll: function (e) {
this.clearDimensions()
this.clearOffsetParent()
},
toggleListeners: function (method, events) {
var self = this
events = events || ['drag', 'drop', 'scroll']
$.each(events, function (i, event) {
self.$document[method](eventNames[event], self[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()
}
},
destroy: function () {
// TODO iterate over subgroups and destroy them
// TODO remove all events
containerGroups[this.options.group] = undefined
/*
if (!this.options) {
return
}
var group = this.options.group
containerGroups[group].options = null
containerGroups[group] = undefined
for (var i in containerGroups) {
if (containerGroups[i]) {
containerGroups[i] = undefined
}
}
*/
}
Sortable.prototype.disable = function() {
this.$el.jqSortable('disable')
}
/*************************************************************************/
function Container(element, options) {
this.el = element
this.options = $.extend({}, containerDefaults, options)
this.group = ContainerGroup.get(this.options)
this.rootGroup = this.options.rootGroup || this.group
this.parentContainer = this.options.parentContainer
this.handle = this.rootGroup.options.handle || this.rootGroup.options.itemSelector
var itemPath = this.rootGroup.options.itemPath,
target = itemPath ? this.el.find(itemPath) : this.el
//Start-Event binden
target.on(eventNames.start, this.handle, $.proxy(this.dragInit, this))
if (this.options.drop) {
this.group.containers.push(this)
}
Sortable.prototype.refresh = function() {
this.$el.jqSortable('refresh')
}
Container.prototype = {
dragInit: function (e) {
var rootGroup = this.rootGroup
if (!rootGroup.dragInitDone && this.options.drag) {
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) {
var itemPath = this.rootGroup.options.itemPath,
target = itemPath ? this.el.find(itemPath) : this.el
rootGroup.movePlaceholder(this, target, "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,
rootGroup: this.rootGroup,
group: groupCounter++
})
childGroup = childContainers.sortable(options).data('oc.sortable').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 self = this,
childType = isContainer ? "item" : "container",
children = this.$getChildren(parent, childType).not(this.options.exclude).map(function () {
return self._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()
}
}
}
Sortable.prototype.serialize = function() {
this.$el.jqSortable('serialize')
}
var API = {
enable: function (ignoreChildren) {
this.disabled = false
},
disable: function (ignoreChildren) {
this.disabled = true
},
serialize: function () {
return this._serialize(this.el, true)
},
destroy: function () {
this.rootGroup.destroy()
$(this.el).data('oc.sortable')
}
Sortable.prototype.destroy = function() {
this.dispose()
}
$.extend(Container.prototype, API)
Sortable.DEFAULTS = {
useAnimation: false,
usePlaceholderClone: false,
tweakCursorAdjustment: null
}
// SORTABLE PLUGIN DEFINITION
// PLUGIN DEFINITION
// ============================
var old = $.fn.sortable
$.fn.sortable = function (option) {
var args = Array.prototype.slice.call(arguments, 1)
var args = arguments;
return this.map(function () {
var $this = $(this),
object = $this.data('oc.sortable')
return this.each(function () {
var $this = $(this)
var data = $this.data('oc.sortable')
var options = $.extend({}, Sortable.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('oc.sortable', (data = new Sortable(this, options)))
if (typeof option == 'string') data[option].apply(data, args)
})
}
if (object && API[option])
return API[option].apply(object, args) || this
else if (!object && (option === undefined ||typeof option === "object")) {
$this.data('oc.sortable', new Container($this, option))
}
return this
});
};
// SORTABLE NO CONFLICT
// =================
$.fn.sortable.Constructor = Sortable
$.fn.sortable.noConflict = function () {
$.fn.sortable = old
@ -709,32 +175,3 @@
}
}(window.jQuery);
/* ===================================================
* jquery-sortable.js v0.9.12
* 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.
* ========================================================== */

View File

@ -0,0 +1,692 @@
/* ===================================================
* jquery-sortable.js v0.9.13
* 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, pluginName, undefined){
var 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.If you nest containers,
// either the original selector with which you call the plugin must only match the top containers,
// or you need to specify a group (see the bootstrap nav example)
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.
// $closestItemOrContainer contains the closest item, the placeholder
// has been put at or the closest empty Container, the placeholder has
// been appended to.
afterMove: function ($placeholder, container, $closestItemOrContainer) {
},
// 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,
// Time in milliseconds after mousedown until dragging should start.
// This option can be used to prevent unwanted drags when clicking on an element.
delay: 0,
// The css selector of the drag handle
handle: "",
// The exact css path between the item and its subcontainers.
// It should only match the immediate items of a container.
// No item of a subcontainer should be matched. E.g. for ol>div>li the itemPath is "> div"
itemPath: "",
// The css selector of the items
itemSelector: "li",
// The class given to "body" while an item is being dragged
bodyClass: "dragging",
// The class giving to an item while being dragged
draggedClass: "dragged",
// 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, event) {
},
// Executed at the beginning of a mouse move event.
// The Placeholder has not been moved yet.
onDrag: function ($item, position, _super, event) {
$item.css(position)
},
// Called after the drag has been started,
// that is the mouse button is being 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, event) {
$item.css({
height: $item.outerHeight(),
width: $item.outerWidth()
})
$item.addClass(container.group.options.draggedClass)
$("body").addClass(container.group.options.bodyClass)
},
// Called when the mouse button is being released
onDrop: function ($item, container, _super, event) {
$item.removeClass(container.group.options.draggedClass).removeAttr("style")
$("body").removeClass(container.group.options.bodyClass)
},
// Called on mousedown. If falsy value is returned, the dragging will not start.
// Ignore if element clicked is input, select or textarea
onMousedown: function ($item, _super, event) {
if (!event.target.nodeName.match(/^(input|select|textarea)$/i)) {
event.preventDefault()
return true
}
},
// The class of the placeholder (must match placeholder option markup)
placeholderClass: "placeholder",
// 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"></li>',
// 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.
serialize: function ($parent, $children, parentIsContainer) {
var result = $.extend({}, $parent.data())
if(parentIsContainer)
return [$children]
else if ($children[0]){
result.children = $children
}
delete result.subContainers
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"
},
subContainerKey = "subContainers"
/*
* 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 = []
if(!this.options.rootGroup){
this.scrollProxy = $.proxy(this.scroll, this)
this.dragProxy = $.proxy(this.drag, this)
this.dropProxy = $.proxy(this.drop, this)
this.placeholder = $(this.options.placeholder)
if(!options.isValidTarget)
this.options.isValidTarget = undefined
}
}
ContainerGroup.get = function (options) {
if(!containerGroups[options.group]) {
if(options.group === undefined)
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)
// get item to drag
var closestItem = $(e.target).closest(this.options.itemSelector);
// using the length of this item, prevents the plugin from being started if there is no handle being clicked on.
// this may also be helpful in instantiating multidrag.
if (closestItem.length) {
this.item = closestItem;
this.itemContainer = itemContainer;
if (this.item.is(this.options.exclude) || !this.options.onMousedown(this.item, groupDefaults.onMousedown, e)) {
return;
}
this.setPointer(e);
this.toggleListeners('on');
this.setupDelayTimer();
this.dragInitDone = true;
}
},
drag: function (e) {
if(!this.dragging){
if(!this.distanceMet(e) || !this.delayMet)
return
this.options.onDragStart(this.item, this.itemContainer, groupDefaults.onDragStart, e)
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,
e)
var p = this.getPointer(e),
box = this.sameResultBox,
t = this.options.tolerance
if(!box || box.top - t > p.top || box.bottom + t < p.top || box.left - t > p.left || box.right + t < p.left)
if(!this.searchValidTarget()){
this.placeholder.detach()
this.lastAppendedItem = undefined
}
},
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, e)
}
this.options.onDrop(this.item, this.getContainer(this.item), groupDefaults.onDrop, e)
// 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, item)
},
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.rootGroup){
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 = this.getPointer(e)
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) {
var currentPointer = this.getPointer(e)
return (Math.max(
Math.abs(this.pointer.left - currentPointer.left),
Math.abs(this.pointer.top - currentPointer.top)
) >= this.options.distance)
},
getPointer: function(e) {
var o = e.originalEvent || e.originalEvent.touches && e.originalEvent.touches[0]
return {
left: e.pageX || o.pageX,
top: e.pageY || o.pageY
}
},
setupDelayTimer: function () {
var that = this
this.delayMet = !this.options.delay
// init delay timer if needed
if (!this.delayMet) {
clearTimeout(this._mouseDelayTimer);
this._mouseDelayTimer = setTimeout(function() {
that.delayMet = true
}, this.options.delay)
}
},
scroll: function (e) {
this.clearDimensions()
this.clearOffsetParent() // TODO is this needed?
},
toggleListeners: function (method) {
var that = this,
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.traverse(function(object){
object._clearDimensions()
})
},
traverse: function(callback) {
callback(this)
var i = this.containers.length
while(i--){
this.containers[i].traverse(callback)
}
},
_clearDimensions: function(){
this.containerDimensions = undefined
},
_destroy: function () {
containerGroups[this.options.group] = undefined
}
}
function Container(element, options) {
this.el = element
this.options = $.extend( {}, containerDefaults, options)
this.group = ContainerGroup.get(this.options)
this.rootGroup = this.options.rootGroup || this.group
this.handle = this.rootGroup.options.handle || this.rootGroup.options.itemSelector
var itemPath = this.rootGroup.options.itemPath
this.target = itemPath ? this.el.find(itemPath) : this.el
this.target.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( !this.disabled &&
!rootGroup.dragInitDone &&
this.options.drag &&
this.isValidDrag(e)) {
rootGroup.dragInit(e, this)
}
},
isValidDrag: function(e) {
return e.which == 1 ||
e.type == "touchstart" && e.originalEvent.touches.length == 1
},
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.target, "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(." + this.group.options.placeholderClass + ", ." + this.group.options.draggedClass + ")"
).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], subContainerKey)
if( childGroup === undefined){
var childContainers = this.$getChildren(this.items[index], "container")
childGroup = false
if(childContainers[0]){
var options = $.extend({}, this.options, {
rootGroup: this.rootGroup,
group: groupCounter ++
})
childGroup = childContainers[pluginName](options).data(pluginName).group
}
$.data(this.items[index], subContainerKey, childGroup)
}
return childGroup
},
$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)
},
traverse: function(callback) {
$.each(this.items || [], function(item){
var group = $.data(this, subContainerKey)
if(group)
group.traverse(callback)
});
callback(this)
},
_clearDimensions: function () {
this.itemDimensions = undefined
},
_destroy: function() {
var that = this;
this.target.off(eventNames.start, this.handle);
this.el.removeData(pluginName)
if(this.options.drop)
this.group.containers = $.grep(this.group.containers, function(val){
return val != that
})
$.each(this.items || [], function(){
$.removeData(this, subContainerKey)
})
}
}
var API = {
enable: function() {
this.traverse(function(object){
object.disabled = false
})
},
disable: function (){
this.traverse(function(object){
object.disabled = true
})
},
serialize: function () {
return this._serialize(this.el, true)
},
refresh: function() {
this.traverse(function(object){
object._clearDimensions()
})
},
destroy: function () {
this.traverse(function(object){
object._destroy();
})
}
}
$.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, 'jqSortable');