Update and decouple drag.sort, add usePlaceholderClone option, recompile assets
This commit is contained in:
parent
f0f1c549c9
commit
095721bd40
File diff suppressed because it is too large
Load Diff
|
|
@ -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)')
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
* ========================================================== */
|
||||
|
|
@ -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');
|
||||
Loading…
Reference in New Issue