4 * Copyright (c) 2008 AUTHORS.txt (http://ui.jquery.com/about)
5 * Dual licensed under the MIT (MIT-LICENSE.txt)
6 * and GPL (GPL-LICENSE.txt) licenses.
8 * http://docs.jquery.com/UI
12 var _remove
= $.fn
.remove
,
13 isFF2
= $.browser
.mozilla
&& (parseFloat($.browser
.version
) < 1.9);
15 //Helper functions and ui object
20 // $.ui.plugin is deprecated. Use the proxy pattern instead.
22 add: function(module
, option
, set) {
23 var proto
= $.ui
[module
].prototype;
25 proto
.plugins
[i
] = proto
.plugins
[i
] || [];
26 proto
.plugins
[i
].push([option
, set[i
]]);
29 call: function(instance
, name
, args
) {
30 var set = instance
.plugins
[name
];
33 for (var i
= 0; i
< set.length
; i
++) {
34 if (instance
.options
[set[i
][0]]) {
35 set[i
][1].apply(instance
.element
, args
);
41 contains: function(a
, b
) {
42 var safari2
= $.browser
.safari
&& $.browser
.version
< 522;
43 if (a
.contains
&& !safari2
) {
46 if (a
.compareDocumentPosition
)
47 return !!(a
.compareDocumentPosition(b
) & 16);
48 while (b
= b
.parentNode
)
49 if (b
== a
) return true;
55 if ($.ui
.cssCache
[name
]) { return $.ui
.cssCache
[name
]; }
56 var tmp
= $('<div class="ui-gen">').addClass(name
).css({position
:'absolute', top
:'-5000px', left
:'-5000px', display
:'block'}).appendTo('body');
58 //if (!$.browser.safari)
59 //tmp.appendTo('body');
61 //Opera and Safari set width and height to 0px instead of auto
62 //Safari returns rgba(0,0,0,0) when bgcolor is not set
63 $.ui
.cssCache
[name
] = !!(
64 (!(/auto|default/).test(tmp
.css('cursor')) || (/^[1-9]/).test(tmp
.css('height')) || (/^[1-9]/).test(tmp
.css('width')) ||
65 !(/none/).test(tmp
.css('backgroundImage')) || !(/transparent|rgba\(0, 0, 0, 0\)/).test(tmp
.css('backgroundColor')))
67 try { $('body').get(0).removeChild(tmp
.get(0)); } catch(e
){}
68 return $.ui
.cssCache
[name
];
71 hasScroll: function(el
, a
) {
73 //If overflow is hidden, the element might have extra content, but the user wants to hide it
74 if ($(el
).css('overflow') == 'hidden') { return false; }
76 var scroll
= (a
&& a
== 'left') ? 'scrollLeft' : 'scrollTop',
79 if (el
[scroll
] > 0) { return true; }
81 // TODO: determine which cases actually cause this to happen
82 // if the element doesn't have the scroll set, see if it's possible to
85 has
= (el
[scroll
] > 0);
90 isOverAxis: function(x
, reference
, size
) {
91 //Determines when x coordinate is over "b" element axis
92 return (x
> reference
) && (x
< (reference
+ size
));
95 isOver: function(y
, x
, top
, left
, height
, width
) {
96 //Determines when x, y coordinates is over "b" element
97 return $.ui
.isOverAxis(y
, top
, height
) && $.ui
.isOverAxis(x
, left
, width
);
117 NUMPAD_MULTIPLY
: 106,
118 NUMPAD_SUBTRACT
: 109,
131 // WAI-ARIA normalization
134 removeAttr
= $.fn
.removeAttr
,
135 ariaNS
= "http://www.w3.org/2005/07/aaa",
136 ariaState
= /^aria-/,
137 ariaRole
= /^wairole:/;
139 $.attr = function(elem
, name
, value
) {
140 var set = value
!== undefined;
142 return (name
== 'role'
144 ? attr
.call(this, elem
, name
, "wairole:" + value
)
145 : (attr
.apply(this, arguments
) || "").replace(ariaRole
, ""))
146 : (ariaState
.test(name
)
148 ? elem
.setAttributeNS(ariaNS
,
149 name
.replace(ariaState
, "aaa:"), value
)
150 : attr
.call(this, elem
, name
.replace(ariaState
, "aaa:")))
151 : attr
.apply(this, arguments
)));
154 $.fn
.removeAttr = function(name
) {
155 return (ariaState
.test(name
)
156 ? this.each(function() {
157 this.removeAttributeNS(ariaNS
, name
.replace(ariaState
, ""));
158 }) : removeAttr
.call(this, name
));
166 // Safari has a native remove event which actually removes DOM elements,
167 // so we have to use triggerHandler instead of trigger (#3037).
168 $("*", this).add(this).each(function() {
169 $(this).triggerHandler("remove");
171 return _remove
.apply(this, arguments
);
174 enableSelection: function() {
176 .attr('unselectable', 'off')
177 .css('MozUserSelect', '')
178 .unbind('selectstart.ui');
181 disableSelection: function() {
183 .attr('unselectable', 'on')
184 .css('MozUserSelect', 'none')
185 .bind('selectstart.ui', function() { return false; });
188 scrollParent: function() {
191 if(($.browser
.msie
&& (/(static|relative)/).test(this.css('position'))) || (/absolute/).test(this.css('position'))) {
192 scrollParent
= this.parents().filter(function() {
193 return (/(relative|absolute|fixed)/).test($.curCSS(this,'position',1)) && (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
196 scrollParent
= this.parents().filter(function() {
197 return (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
201 return (/fixed/).test(this.css('position')) || !scrollParent
.length
? $(document
) : scrollParent
;
209 //Additional selectors
210 $.extend($.expr
[':'], {
212 data: function(a
, i
, m
) {
213 return $.data(a
, m
[3]);
216 // TODO: add support for object, area
217 tabbable: function(a
, i
, m
) {
219 var nodeName
= a
.nodeName
.toLowerCase();
220 function isVisible(element
) {
221 return !($(element
).is(':hidden') || $(element
).parents(':hidden').length
);
228 ( // filter node types that participate in the tab order
231 ('a' == nodeName
&& a
.href
) ||
233 // enabled form element
234 (/input|select|textarea|button/.test(nodeName
) &&
235 'hidden' != a
.type
&& !a
.disabled
)
247 // $.widget is a factory to create jQuery plugins
248 // taking some boilerplate code out of the plugin code
249 function getter(namespace, plugin
, method
, args
) {
250 function getMethods(type
) {
251 var methods
= $[namespace][plugin
][type
] || [];
252 return (typeof methods
== 'string' ? methods
.split(/,?\s+/) : methods
);
255 var methods
= getMethods('getter');
256 if (args
.length
== 1 && typeof args
[0] == 'string') {
257 methods
= methods
.concat(getMethods('getterSetter'));
259 return ($.inArray(method
, methods
) != -1);
262 $.widget = function(name
, prototype) {
263 var namespace = name
.split(".")[0];
264 name
= name
.split(".")[1];
266 // create plugin method
267 $.fn
[name
] = function(options
) {
268 var isMethodCall
= (typeof options
== 'string'),
269 args
= Array
.prototype.slice
.call(arguments
, 1);
271 // prevent calls to internal methods
272 if (isMethodCall
&& options
.substring(0, 1) == '_') {
276 // handle getter methods
277 if (isMethodCall
&& getter(namespace, name
, options
, args
)) {
278 var instance
= $.data(this[0], name
);
279 return (instance
? instance
[options
].apply(instance
, args
)
283 // handle initialization and non-getter methods
284 return this.each(function() {
285 var instance
= $.data(this, name
);
288 (!instance
&& !isMethodCall
&&
289 $.data(this, name
, new $[namespace][name
](this, options
)));
292 (instance
&& isMethodCall
&& $.isFunction(instance
[options
]) &&
293 instance
[options
].apply(instance
, args
));
297 // create widget constructor
298 $[namespace] = $[namespace] || {};
299 $[namespace][name
] = function(element
, options
) {
302 this.widgetName
= name
;
303 this.widgetEventPrefix
= $[namespace][name
].eventPrefix
|| name
;
304 this.widgetBaseClass
= namespace + '-' + name
;
306 this.options
= $.extend({},
308 $[namespace][name
].defaults
,
309 $.metadata
&& $.metadata
.get(element
)[name
],
312 this.element
= $(element
)
313 .bind('setData.' + name
, function(event
, key
, value
) {
314 return self
._setData(key
, value
);
316 .bind('getData.' + name
, function(event
, key
) {
317 return self
._getData(key
);
319 .bind('remove', function() {
320 return self
.destroy();
326 // add widget prototype
327 $[namespace][name
].prototype = $.extend({}, $.widget
.prototype, prototype);
329 // TODO: merge getter and getterSetter properties from widget prototype
330 // and plugin prototype
331 $[namespace][name
].getterSetter
= 'option';
334 $.widget
.prototype = {
335 _init: function() {},
336 destroy: function() {
337 this.element
.removeData(this.widgetName
);
340 option: function(key
, value
) {
344 if (typeof key
== "string") {
345 if (value
=== undefined) {
346 return this._getData(key
);
349 options
[key
] = value
;
352 $.each(options
, function(key
, value
) {
353 self
._setData(key
, value
);
356 _getData: function(key
) {
357 return this.options
[key
];
359 _setData: function(key
, value
) {
360 this.options
[key
] = value
;
362 if (key
== 'disabled') {
363 this.element
[value
? 'addClass' : 'removeClass'](
364 this.widgetBaseClass
+ '-disabled');
369 this._setData('disabled', false);
371 disable: function() {
372 this._setData('disabled', true);
375 _trigger: function(type
, event
, data
) {
376 var eventName
= (type
== this.widgetEventPrefix
377 ? type
: this.widgetEventPrefix
+ type
);
378 event
= event
|| $.event
.fix({ type
: eventName
, target
: this.element
[0] });
379 return this.element
.triggerHandler(eventName
, [event
, data
], this.options
[type
]);
383 $.widget
.defaults
= {
388 /** Mouse Interaction Plugin **/
391 _mouseInit: function() {
395 .bind('mousedown.'+this.widgetName
, function(event
) {
396 return self
._mouseDown(event
);
398 .bind('click.'+this.widgetName
, function(event
) {
399 if(self
._preventClickEvent
) {
400 self
._preventClickEvent
= false;
405 // Prevent text selection in IE
406 if ($.browser
.msie
) {
407 this._mouseUnselectable
= this.element
.attr('unselectable');
408 this.element
.attr('unselectable', 'on');
411 this.started
= false;
414 // TODO: make sure destroying one instance of mouse doesn't mess with
415 // other instances of mouse
416 _mouseDestroy: function() {
417 this.element
.unbind('.'+this.widgetName
);
419 // Restore text selection in IE
421 && this.element
.attr('unselectable', this._mouseUnselectable
));
424 _mouseDown: function(event
) {
425 // we may have missed mouseup (out of window)
426 (this._mouseStarted
&& this._mouseUp(event
));
428 this._mouseDownEvent
= event
;
431 btnIsLeft
= (event
.which
== 1),
432 elIsCancel
= (typeof this.options
.cancel
== "string" ? $(event
.target
).parents().add(event
.target
).filter(this.options
.cancel
).length
: false);
433 if (!btnIsLeft
|| elIsCancel
|| !this._mouseCapture(event
)) {
437 this.mouseDelayMet
= !this.options
.delay
;
438 if (!this.mouseDelayMet
) {
439 this._mouseDelayTimer
= setTimeout(function() {
440 self
.mouseDelayMet
= true;
441 }, this.options
.delay
);
444 if (this._mouseDistanceMet(event
) && this._mouseDelayMet(event
)) {
445 this._mouseStarted
= (this._mouseStart(event
) !== false);
446 if (!this._mouseStarted
) {
447 event
.preventDefault();
452 // these delegates are required to keep context
453 this._mouseMoveDelegate = function(event
) {
454 return self
._mouseMove(event
);
456 this._mouseUpDelegate = function(event
) {
457 return self
._mouseUp(event
);
460 .bind('mousemove.'+this.widgetName
, this._mouseMoveDelegate
)
461 .bind('mouseup.'+this.widgetName
, this._mouseUpDelegate
);
463 // preventDefault() is used to prevent the selection of text here -
464 // however, in Safari, this causes select boxes not to be selectable
465 // anymore, so this fix is needed
466 if(!$.browser
.safari
) event
.preventDefault();
470 _mouseMove: function(event
) {
471 // IE mouseup check - mouseup happened when mouse was out of window
472 if ($.browser
.msie
&& !event
.button
) {
473 return this._mouseUp(event
);
476 if (this._mouseStarted
) {
477 this._mouseDrag(event
);
478 return event
.preventDefault();
481 if (this._mouseDistanceMet(event
) && this._mouseDelayMet(event
)) {
483 (this._mouseStart(this._mouseDownEvent
, event
) !== false);
484 (this._mouseStarted
? this._mouseDrag(event
) : this._mouseUp(event
));
487 return !this._mouseStarted
;
490 _mouseUp: function(event
) {
492 .unbind('mousemove.'+this.widgetName
, this._mouseMoveDelegate
)
493 .unbind('mouseup.'+this.widgetName
, this._mouseUpDelegate
);
495 if (this._mouseStarted
) {
496 this._mouseStarted
= false;
497 this._preventClickEvent
= true;
498 this._mouseStop(event
);
504 _mouseDistanceMet: function(event
) {
506 Math
.abs(this._mouseDownEvent
.pageX
- event
.pageX
),
507 Math
.abs(this._mouseDownEvent
.pageY
- event
.pageY
)
508 ) >= this.options
.distance
512 _mouseDelayMet: function(event
) {
513 return this.mouseDelayMet
;
516 // These are placeholder methods, to be overriden by extending plugin
517 _mouseStart: function(event
) {},
518 _mouseDrag: function(event
) {},
519 _mouseStop: function(event
) {},
520 _mouseCapture: function(event
) { return true; }
523 $.ui
.mouse
.defaults
= {
531 * jQuery UI Draggable 1.6
533 * Copyright (c) 2008 AUTHORS.txt (http://ui.jquery.com/about)
534 * Dual licensed under the MIT (MIT-LICENSE.txt)
535 * and GPL (GPL-LICENSE.txt) licenses.
537 * http://docs.jquery.com/UI/Draggables
544 $.widget("ui.draggable", $.extend({}, $.ui
.mouse
, {
548 if (this.options
.helper
== 'original' && !(/^(?:r|a|f)/).test(this.element
.css("position")))
549 this.element
[0].style
.position
= 'relative';
551 (this.options
.cssNamespace
&& this.element
.addClass(this.options
.cssNamespace
+"-draggable"));
552 (this.options
.disabled
&& this.element
.addClass('ui-draggable-disabled'));
558 destroy: function() {
559 if(!this.element
.data('draggable')) return;
560 this.element
.removeData("draggable").unbind(".draggable").removeClass('ui-draggable ui-draggable-dragging ui-draggable-disabled');
561 this._mouseDestroy();
564 _mouseCapture: function(event
) {
566 var o
= this.options
;
568 if (this.helper
|| o
.disabled
|| $(event
.target
).is('.ui-resizable-handle'))
571 //Quit if we're not on a valid handle
572 this.handle
= this._getHandle(event
);
580 _mouseStart: function(event
) {
582 var o
= this.options
;
584 //Create and append the visible helper
585 this.helper
= this._createHelper(event
);
587 //Cache the helper size
588 this._cacheHelperProportions();
590 //If ddmanager is used for droppables, set the global draggable
592 $.ui
.ddmanager
.current
= this;
595 * - Position generation -
596 * This block generates everything position related - it's the core of draggables.
599 //Cache the margins of the original element
600 this._cacheMargins();
602 //Store the helper's css position
603 this.cssPosition
= this.helper
.css("position");
604 this.scrollParent
= this.helper
.scrollParent();
606 //The element's absolute position on the page minus margins
607 this.offset
= this.element
.offset();
609 top
: this.offset
.top
- this.margins
.top
,
610 left
: this.offset
.left
- this.margins
.left
613 $.extend(this.offset
, {
614 click
: { //Where the click happened, relative to the element
615 left
: event
.pageX
- this.offset
.left
,
616 top
: event
.pageY
- this.offset
.top
618 parent
: this._getParentOffset(),
619 relative
: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
622 //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
624 this._adjustOffsetFromHelper(o
.cursorAt
);
626 //Generate the original position
627 this.originalPosition
= this._generatePosition(event
);
629 //Set a containment if given in the options
631 this._setContainment();
633 //Call plugins and callbacks
634 this._propagate("start", event
);
636 //Recache the helper size
637 this._cacheHelperProportions();
639 //Prepare the droppable offsets
640 if ($.ui
.ddmanager
&& !o
.dropBehaviour
)
641 $.ui
.ddmanager
.prepareOffsets(this, event
);
643 this.helper
.addClass("ui-draggable-dragging");
644 this._mouseDrag(event
, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
648 _mouseDrag: function(event
, noPropagation
) {
650 //Compute the helpers position
651 this.position
= this._generatePosition(event
);
652 this.positionAbs
= this._convertPositionTo("absolute");
654 //Call plugins and callbacks and use the resulting position if something is returned
655 if(!noPropagation
) this.position
= this._propagate("drag", event
) || this.position
;
657 if(!this.options
.axis
|| this.options
.axis
!= "y") this.helper
[0].style
.left
= this.position
.left
+'px';
658 if(!this.options
.axis
|| this.options
.axis
!= "x") this.helper
[0].style
.top
= this.position
.top
+'px';
659 if($.ui
.ddmanager
) $.ui
.ddmanager
.drag(this, event
);
664 _mouseStop: function(event
) {
666 //If we are using droppables, inform the manager about the drop
668 if ($.ui
.ddmanager
&& !this.options
.dropBehaviour
)
669 var dropped
= $.ui
.ddmanager
.drop(this, event
);
671 if((this.options
.revert
== "invalid" && !dropped
) || (this.options
.revert
== "valid" && dropped
) || this.options
.revert
=== true || ($.isFunction(this.options
.revert
) && this.options
.revert
.call(this.element
, dropped
))) {
673 $(this.helper
).animate(this.originalPosition
, parseInt(this.options
.revertDuration
, 10), function() {
674 self
._propagate("stop", event
);
678 this._propagate("stop", event
);
685 _getHandle: function(event
) {
687 var handle
= !this.options
.handle
|| !$(this.options
.handle
, this.element
).length
? true : false;
688 $(this.options
.handle
, this.element
)
692 if(this == event
.target
) handle
= true;
699 _createHelper: function(event
) {
701 var o
= this.options
;
702 var helper
= $.isFunction(o
.helper
) ? $(o
.helper
.apply(this.element
[0], [event
])) : (o
.helper
== 'clone' ? this.element
.clone() : this.element
);
704 if(!helper
.parents('body').length
)
705 helper
.appendTo((o
.appendTo
== 'parent' ? this.element
[0].parentNode
: o
.appendTo
));
707 if(helper
[0] != this.element
[0] && !(/(fixed|absolute)/).test(helper
.css("position")))
708 helper
.css("position", "absolute");
714 _adjustOffsetFromHelper: function(obj
) {
715 if(obj
.left
!= undefined) this.offset
.click
.left
= obj
.left
+ this.margins
.left
;
716 if(obj
.right
!= undefined) this.offset
.click
.left
= this.helperProportions
.width
- obj
.right
+ this.margins
.left
;
717 if(obj
.top
!= undefined) this.offset
.click
.top
= obj
.top
+ this.margins
.top
;
718 if(obj
.bottom
!= undefined) this.offset
.click
.top
= this.helperProportions
.height
- obj
.bottom
+ this.margins
.top
;
721 _getParentOffset: function() {
723 this.offsetParent
= this.helper
.offsetParent(); var po
= this.offsetParent
.offset(); //Get the offsetParent and cache its position
725 if((this.offsetParent
[0] == document
.body
&& $.browser
.mozilla
) //Ugly FF3 fix
726 || (this.offsetParent
[0].tagName
&& this.offsetParent
[0].tagName
.toLowerCase() == 'html' && $.browser
.msie
)) //Ugly IE fix
727 po
= { top
: 0, left
: 0 };
730 top
: po
.top
+ (parseInt(this.offsetParent
.css("borderTopWidth"),10) || 0),
731 left
: po
.left
+ (parseInt(this.offsetParent
.css("borderLeftWidth"),10) || 0)
736 _getRelativeOffset: function() {
738 if(this.cssPosition
== "relative") {
739 var p
= this.element
.position();
741 top
: p
.top
- (parseInt(this.helper
.css("top"),10) || 0) + this.scrollParent
.scrollTop(),
742 left
: p
.left
- (parseInt(this.helper
.css("left"),10) || 0) + this.scrollParent
.scrollLeft()
745 return { top
: 0, left
: 0 };
750 _cacheMargins: function() {
752 left
: (parseInt(this.element
.css("marginLeft"),10) || 0),
753 top
: (parseInt(this.element
.css("marginTop"),10) || 0)
757 _cacheHelperProportions: function() {
758 this.helperProportions
= {
759 width
: this.helper
.outerWidth(),
760 height
: this.helper
.outerHeight()
764 _setContainment: function() {
766 var o
= this.options
;
767 if(o
.containment
== 'parent') o
.containment
= this.helper
[0].parentNode
;
768 if(o
.containment
== 'document' || o
.containment
== 'window') this.containment
= [
769 0 - this.offset
.relative
.left
- this.offset
.parent
.left
,
770 0 - this.offset
.relative
.top
- this.offset
.parent
.top
,
771 $(o
.containment
== 'document' ? document
: window
).width() - this.offset
.relative
.left
- this.offset
.parent
.left
- this.helperProportions
.width
- this.margins
.left
- (parseInt(this.element
.css("marginRight"),10) || 0),
772 ($(o
.containment
== 'document' ? document
: window
).height() || document
.body
.parentNode
.scrollHeight
) - this.offset
.relative
.top
- this.offset
.parent
.top
- this.helperProportions
.height
- this.margins
.top
- (parseInt(this.element
.css("marginBottom"),10) || 0)
775 if(!(/^(document|window|parent)$/).test(o
.containment
)) {
776 var ce
= $(o
.containment
)[0];
777 var co
= $(o
.containment
).offset();
778 var over
= ($(ce
).css("overflow") != 'hidden');
781 co
.left
+ (parseInt($(ce
).css("borderLeftWidth"),10) || 0) - this.offset
.relative
.left
- this.offset
.parent
.left
- this.margins
.left
,
782 co
.top
+ (parseInt($(ce
).css("borderTopWidth"),10) || 0) - this.offset
.relative
.top
- this.offset
.parent
.top
- this.margins
.top
,
783 co
.left
+(over
? Math
.max(ce
.scrollWidth
,ce
.offsetWidth
) : ce
.offsetWidth
) - (parseInt($(ce
).css("borderLeftWidth"),10) || 0) - this.offset
.relative
.left
- this.offset
.parent
.left
- this.helperProportions
.width
- this.margins
.left
,
784 co
.top
+(over
? Math
.max(ce
.scrollHeight
,ce
.offsetHeight
) : ce
.offsetHeight
) - (parseInt($(ce
).css("borderTopWidth"),10) || 0) - this.offset
.relative
.top
- this.offset
.parent
.top
- this.helperProportions
.height
- this.margins
.top
790 _convertPositionTo: function(d
, pos
) {
792 if(!pos
) pos
= this.position
;
793 var mod
= d
== "absolute" ? 1 : -1;
794 var scroll
= this[(this.cssPosition
== 'absolute' ? 'offset' : 'scroll')+'Parent'], scrollIsRootNode
= (/(html|body)/i).test(scroll
[0].tagName
);
798 pos
.top
// the calculated relative position
799 + this.offset
.relative
.top
* mod
// Only for relative positioned nodes: Relative offset from element to offset parent
800 + this.offset
.parent
.top
* mod
// The offsetParent's offset without borders (offset + border)
801 + ( this.cssPosition
== 'fixed' ? -this.scrollParent
.scrollTop() : ( scrollIsRootNode
? 0 : scroll
.scrollTop() ) ) * mod
802 + this.margins
.top
* mod
//Add the margin (you don't want the margin counting in intersection methods)
805 pos
.left
// the calculated relative position
806 + this.offset
.relative
.left
* mod
// Only for relative positioned nodes: Relative offset from element to offset parent
807 + this.offset
.parent
.left
* mod
// The offsetParent's offset without borders (offset + border)
808 + ( this.cssPosition
== 'fixed' ? -this.scrollParent
.scrollLeft() : ( scrollIsRootNode
? 0 : scroll
.scrollLeft() ) ) * mod
809 + this.margins
.left
* mod
//Add the margin (you don't want the margin counting in intersection methods)
814 _generatePosition: function(event
) {
816 var o
= this.options
, scroll
= this[(this.cssPosition
== 'absolute' ? 'offset' : 'scroll')+'Parent'], scrollIsRootNode
= (/(html|body)/i).test(scroll
[0].tagName
);
820 event
.pageY
// The absolute mouse position
821 - this.offset
.click
.top
// Click offset (relative to the element)
822 - this.offset
.relative
.top
// Only for relative positioned nodes: Relative offset from element to offset parent
823 - this.offset
.parent
.top
// The offsetParent's offset without borders (offset + border)
824 + ( this.cssPosition
== 'fixed' ? -this.scrollParent
.scrollTop() : ( scrollIsRootNode
? 0 : scroll
.scrollTop() ) )
827 event
.pageX
// The absolute mouse position
828 - this.offset
.click
.left
// Click offset (relative to the element)
829 - this.offset
.relative
.left
// Only for relative positioned nodes: Relative offset from element to offset parent
830 - this.offset
.parent
.left
// The offsetParent's offset without borders (offset + border)
831 + ( this.cssPosition
== 'fixed' ? -this.scrollParent
.scrollLeft() : scrollIsRootNode
? 0 : scroll
.scrollLeft() )
835 if(!this.originalPosition
) return position
; //If we are not dragging yet, we won't check for options
838 * - Position constraining -
839 * Constrain the position to a mix of grid, containment.
841 if(this.containment
) {
842 if(position
.left
< this.containment
[0]) position
.left
= this.containment
[0];
843 if(position
.top
< this.containment
[1]) position
.top
= this.containment
[1];
844 if(position
.left
> this.containment
[2]) position
.left
= this.containment
[2];
845 if(position
.top
> this.containment
[3]) position
.top
= this.containment
[3];
849 var top
= this.originalPosition
.top
+ Math
.round((position
.top
- this.originalPosition
.top
) / o
.grid
[1]) * o
.grid
[1];
850 position
.top
= this.containment
? (!(top
< this.containment
[1] || top
> this.containment
[3]) ? top
: (!(top
< this.containment
[1]) ? top
- o
.grid
[1] : top
+ o
.grid
[1])) : top
;
852 var left
= this.originalPosition
.left
+ Math
.round((position
.left
- this.originalPosition
.left
) / o
.grid
[0]) * o
.grid
[0];
853 position
.left
= this.containment
? (!(left
< this.containment
[0] || left
> this.containment
[2]) ? left
: (!(left
< this.containment
[0]) ? left
- o
.grid
[0] : left
+ o
.grid
[0])) : left
;
860 this.helper
.removeClass("ui-draggable-dragging");
861 if(this.helper
[0] != this.element
[0] && !this.cancelHelperRemoval
) this.helper
.remove();
862 //if($.ui.ddmanager) $.ui.ddmanager.current = null;
864 this.cancelHelperRemoval
= false;
867 // From now on bulk stuff - mainly helpers
869 _propagate: function(n
, event
) {
870 $.ui
.plugin
.call(this, n
, [event
, this._uiHash()]);
871 if(n
== "drag") this.positionAbs
= this._convertPositionTo("absolute"); //The absolute position has to be recalculated after plugins
872 return this.element
.triggerHandler(n
== "drag" ? n
: "drag"+n
, [event
, this._uiHash()], this.options
[n
]);
877 _uiHash: function(event
) {
880 position
: this.position
,
881 absolutePosition
: this.positionAbs
,
882 options
: this.options
888 $.extend($.ui
.draggable
, {
894 connectToSortable
: false,
906 refreshPositions
: false,
911 scrollSensitivity
: 20,
921 $.ui
.plugin
.add("draggable", "connectToSortable", {
922 start: function(event
, ui
) {
924 var inst
= $(this).data("draggable");
926 $(ui
.options
.connectToSortable
).each(function() {
927 // 'this' points to a string, and should therefore resolved as query, but instead, if the string is assigned to a variable, it loops through the strings properties,
928 // so we have to append '' to make it anonymous again
929 $(this+'').each(function() {
930 if($.data(this, 'sortable')) {
931 var sortable
= $.data(this, 'sortable');
932 inst
.sortables
.push({
934 shouldRevert
: sortable
.options
.revert
936 sortable
._refreshItems(); //Do a one-time refresh at start to refresh the containerCache
937 sortable
._propagate("activate", event
, inst
);
943 stop: function(event
, ui
) {
945 //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
946 var inst
= $(this).data("draggable");
948 $.each(inst
.sortables
, function() {
949 if(this.instance
.isOver
) {
950 this.instance
.isOver
= 0;
951 inst
.cancelHelperRemoval
= true; //Don't remove the helper in the draggable instance
952 this.instance
.cancelHelperRemoval
= false; //Remove it in the sortable instance (so sortable plugins like revert still work)
953 if(this.shouldRevert
) this.instance
.options
.revert
= true; //revert here
954 this.instance
._mouseStop(event
);
956 //Also propagate receive event, since the sortable is actually receiving a element
957 this.instance
.element
.triggerHandler("sortreceive", [event
, $.extend(this.instance
._ui(), { sender
: inst
.element
})], this.instance
.options
["receive"]);
959 this.instance
.options
.helper
= this.instance
.options
._helper
;
961 if(inst
.options
.helper
== 'original') {
962 this.instance
.currentItem
.css({ top
: 'auto', left
: 'auto' });
966 this.instance
.cancelHelperRemoval
= false; //Remove the helper in the sortable instance
967 this.instance
._propagate("deactivate", event
, inst
);
973 drag: function(event
, ui
) {
975 var inst
= $(this).data("draggable"), self
= this;
977 var checkPos = function(o
) {
978 var dyClick
= this.offset
.click
.top
, dxClick
= this.offset
.click
.left
;
979 var helperTop
= this.positionAbs
.top
, helperLeft
= this.positionAbs
.left
;
980 var itemHeight
= o
.height
, itemWidth
= o
.width
;
981 var itemTop
= o
.top
, itemLeft
= o
.left
;
983 return $.ui
.isOver(helperTop
+ dyClick
, helperLeft
+ dxClick
, itemTop
, itemLeft
, itemHeight
, itemWidth
);
986 $.each(inst
.sortables
, function(i
) {
988 if(checkPos
.call(inst
, this.instance
.containerCache
)) {
990 //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
991 if(!this.instance
.isOver
) {
992 this.instance
.isOver
= 1;
993 //Now we fake the start of dragging for the sortable instance,
994 //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
995 //We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one)
996 this.instance
.currentItem
= $(self
).clone().appendTo(this.instance
.element
).data("sortable-item", true);
997 this.instance
.options
._helper
= this.instance
.options
.helper
; //Store helper option to later restore it
998 this.instance
.options
.helper = function() { return ui
.helper
[0]; };
1000 event
.target
= this.instance
.currentItem
[0];
1001 this.instance
._mouseCapture(event
, true);
1002 this.instance
._mouseStart(event
, true, true);
1004 //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
1005 this.instance
.offset
.click
.top
= inst
.offset
.click
.top
;
1006 this.instance
.offset
.click
.left
= inst
.offset
.click
.left
;
1007 this.instance
.offset
.parent
.left
-= inst
.offset
.parent
.left
- this.instance
.offset
.parent
.left
;
1008 this.instance
.offset
.parent
.top
-= inst
.offset
.parent
.top
- this.instance
.offset
.parent
.top
;
1010 inst
._propagate("toSortable", event
);
1014 //Provided we did all the previous steps, we can fire the drag event of the sortable on every draggable drag, when it intersects with the sortable
1015 if(this.instance
.currentItem
) this.instance
._mouseDrag(event
);
1019 //If it doesn't intersect with the sortable, and it intersected before,
1020 //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
1021 if(this.instance
.isOver
) {
1022 this.instance
.isOver
= 0;
1023 this.instance
.cancelHelperRemoval
= true;
1024 this.instance
.options
.revert
= false; //No revert here
1025 this.instance
._mouseStop(event
, true);
1026 this.instance
.options
.helper
= this.instance
.options
._helper
;
1028 //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
1029 this.instance
.currentItem
.remove();
1030 if(this.instance
.placeholder
) this.instance
.placeholder
.remove();
1032 inst
._propagate("fromSortable", event
);
1042 $.ui
.plugin
.add("draggable", "cursor", {
1043 start: function(event
, ui
) {
1045 if (t
.css("cursor")) ui
.options
._cursor
= t
.css("cursor");
1046 t
.css("cursor", ui
.options
.cursor
);
1048 stop: function(event
, ui
) {
1049 if (ui
.options
._cursor
) $('body').css("cursor", ui
.options
._cursor
);
1053 $.ui
.plugin
.add("draggable", "iframeFix", {
1054 start: function(event
, ui
) {
1055 $(ui
.options
.iframeFix
=== true ? "iframe" : ui
.options
.iframeFix
).each(function() {
1056 $('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>')
1058 width
: this.offsetWidth
+"px", height
: this.offsetHeight
+"px",
1059 position
: "absolute", opacity
: "0.001", zIndex
: 1000
1061 .css($(this).offset())
1065 stop: function(event
, ui
) {
1066 $("div.ui-draggable-iframeFix").each(function() { this.parentNode
.removeChild(this); }); //Remove frame helpers
1070 $.ui
.plugin
.add("draggable", "opacity", {
1071 start: function(event
, ui
) {
1072 var t
= $(ui
.helper
);
1073 if(t
.css("opacity")) ui
.options
._opacity
= t
.css("opacity");
1074 t
.css('opacity', ui
.options
.opacity
);
1076 stop: function(event
, ui
) {
1077 if(ui
.options
._opacity
) $(ui
.helper
).css('opacity', ui
.options
._opacity
);
1081 $.ui
.plugin
.add("draggable", "scroll", {
1082 start: function(event
, ui
) {
1084 var i
= $(this).data("draggable");
1086 if(i
.scrollParent
[0] != document
&& i
.scrollParent
[0].tagName
!= 'HTML') i
.overflowOffset
= i
.scrollParent
.offset();
1089 drag: function(event
, ui
) {
1091 var o
= ui
.options
, scrolled
= false;
1092 var i
= $(this).data("draggable");
1094 if(i
.scrollParent
[0] != document
&& i
.scrollParent
[0].tagName
!= 'HTML') {
1096 if((i
.overflowOffset
.top
+ i
.scrollParent
[0].offsetHeight
) - event
.pageY
< o
.scrollSensitivity
)
1097 i
.scrollParent
[0].scrollTop
= scrolled
= i
.scrollParent
[0].scrollTop
+ o
.scrollSpeed
;
1098 else if(event
.pageY
- i
.overflowOffset
.top
< o
.scrollSensitivity
)
1099 i
.scrollParent
[0].scrollTop
= scrolled
= i
.scrollParent
[0].scrollTop
- o
.scrollSpeed
;
1101 if((i
.overflowOffset
.left
+ i
.scrollParent
[0].offsetWidth
) - event
.pageX
< o
.scrollSensitivity
)
1102 i
.scrollParent
[0].scrollLeft
= scrolled
= i
.scrollParent
[0].scrollLeft
+ o
.scrollSpeed
;
1103 else if(event
.pageX
- i
.overflowOffset
.left
< o
.scrollSensitivity
)
1104 i
.scrollParent
[0].scrollLeft
= scrolled
= i
.scrollParent
[0].scrollLeft
- o
.scrollSpeed
;
1108 if(event
.pageY
- $(document
).scrollTop() < o
.scrollSensitivity
)
1109 scrolled
= $(document
).scrollTop($(document
).scrollTop() - o
.scrollSpeed
);
1110 else if($(window
).height() - (event
.pageY
- $(document
).scrollTop()) < o
.scrollSensitivity
)
1111 scrolled
= $(document
).scrollTop($(document
).scrollTop() + o
.scrollSpeed
);
1113 if(event
.pageX
- $(document
).scrollLeft() < o
.scrollSensitivity
)
1114 scrolled
= $(document
).scrollLeft($(document
).scrollLeft() - o
.scrollSpeed
);
1115 else if($(window
).width() - (event
.pageX
- $(document
).scrollLeft()) < o
.scrollSensitivity
)
1116 scrolled
= $(document
).scrollLeft($(document
).scrollLeft() + o
.scrollSpeed
);
1120 if(scrolled
!== false && $.ui
.ddmanager
&& !o
.dropBehaviour
)
1121 $.ui
.ddmanager
.prepareOffsets(i
, event
);
1125 // This is a special case where we need to modify a offset calculated on start, since the following happened:
1126 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
1127 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
1128 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
1129 if(scrolled
!== false && i
.cssPosition
== 'absolute' && i
.scrollParent
[0] != document
&& $.ui
.contains(i
.scrollParent
[0], i
.offsetParent
[0])) {
1130 i
.offset
.parent
= i
._getParentOffset();
1134 // This is another very weird special case that only happens for relative elements:
1135 // 1. If the css position is relative
1136 // 2. and the scroll parent is the document or similar to the offset parent
1137 // we have to refresh the relative offset during the scroll so there are no jumps
1138 if(scrolled
!== false && i
.cssPosition
== 'relative' && !(i
.scrollParent
[0] != document
&& i
.scrollParent
[0] != i
.offsetParent
[0])) {
1139 i
.offset
.relative
= i
._getRelativeOffset();
1146 $.ui
.plugin
.add("draggable", "snap", {
1147 start: function(event
, ui
) {
1149 var inst
= $(this).data("draggable");
1150 inst
.snapElements
= [];
1152 $(ui
.options
.snap
.constructor != String
? ( ui
.options
.snap
.items
|| ':data(draggable)' ) : ui
.options
.snap
).each(function() {
1153 var $t
= $(this); var $o
= $t
.offset();
1154 if(this != inst
.element
[0]) inst
.snapElements
.push({
1156 width
: $t
.outerWidth(), height
: $t
.outerHeight(),
1157 top
: $o
.top
, left
: $o
.left
1162 drag: function(event
, ui
) {
1164 var inst
= $(this).data("draggable");
1165 var d
= ui
.options
.snapTolerance
;
1167 var x1
= ui
.absolutePosition
.left
, x2
= x1
+ inst
.helperProportions
.width
,
1168 y1
= ui
.absolutePosition
.top
, y2
= y1
+ inst
.helperProportions
.height
;
1170 for (var i
= inst
.snapElements
.length
- 1; i
>= 0; i
--){
1172 var l
= inst
.snapElements
[i
].left
, r
= l
+ inst
.snapElements
[i
].width
,
1173 t
= inst
.snapElements
[i
].top
, b
= t
+ inst
.snapElements
[i
].height
;
1175 //Yes, I know, this is insane ;)
1176 if(!((l
-d
< x1
&& x1
< r
+d
&& t
-d
< y1
&& y1
< b
+d
) || (l
-d
< x1
&& x1
< r
+d
&& t
-d
< y2
&& y2
< b
+d
) || (l
-d
< x2
&& x2
< r
+d
&& t
-d
< y1
&& y1
< b
+d
) || (l
-d
< x2
&& x2
< r
+d
&& t
-d
< y2
&& y2
< b
+d
))) {
1177 if(inst
.snapElements
[i
].snapping
) (inst
.options
.snap
.release
&& inst
.options
.snap
.release
.call(inst
.element
, event
, $.extend(inst
._uiHash(), { snapItem
: inst
.snapElements
[i
].item
})));
1178 inst
.snapElements
[i
].snapping
= false;
1182 if(ui
.options
.snapMode
!= 'inner') {
1183 var ts
= Math
.abs(t
- y2
) <= d
;
1184 var bs
= Math
.abs(b
- y1
) <= d
;
1185 var ls
= Math
.abs(l
- x2
) <= d
;
1186 var rs
= Math
.abs(r
- x1
) <= d
;
1187 if(ts
) ui
.position
.top
= inst
._convertPositionTo("relative", { top
: t
- inst
.helperProportions
.height
, left
: 0 }).top
;
1188 if(bs
) ui
.position
.top
= inst
._convertPositionTo("relative", { top
: b
, left
: 0 }).top
;
1189 if(ls
) ui
.position
.left
= inst
._convertPositionTo("relative", { top
: 0, left
: l
- inst
.helperProportions
.width
}).left
;
1190 if(rs
) ui
.position
.left
= inst
._convertPositionTo("relative", { top
: 0, left
: r
}).left
;
1193 var first
= (ts
|| bs
|| ls
|| rs
);
1195 if(ui
.options
.snapMode
!= 'outer') {
1196 var ts
= Math
.abs(t
- y1
) <= d
;
1197 var bs
= Math
.abs(b
- y2
) <= d
;
1198 var ls
= Math
.abs(l
- x1
) <= d
;
1199 var rs
= Math
.abs(r
- x2
) <= d
;
1200 if(ts
) ui
.position
.top
= inst
._convertPositionTo("relative", { top
: t
, left
: 0 }).top
;
1201 if(bs
) ui
.position
.top
= inst
._convertPositionTo("relative", { top
: b
- inst
.helperProportions
.height
, left
: 0 }).top
;
1202 if(ls
) ui
.position
.left
= inst
._convertPositionTo("relative", { top
: 0, left
: l
}).left
;
1203 if(rs
) ui
.position
.left
= inst
._convertPositionTo("relative", { top
: 0, left
: r
- inst
.helperProportions
.width
}).left
;
1206 if(!inst
.snapElements
[i
].snapping
&& (ts
|| bs
|| ls
|| rs
|| first
))
1207 (inst
.options
.snap
.snap
&& inst
.options
.snap
.snap
.call(inst
.element
, event
, $.extend(inst
._uiHash(), { snapItem
: inst
.snapElements
[i
].item
})));
1208 inst
.snapElements
[i
].snapping
= (ts
|| bs
|| ls
|| rs
|| first
);
1215 $.ui
.plugin
.add("draggable", "stack", {
1216 start: function(event
, ui
) {
1217 var group
= $.makeArray($(ui
.options
.stack
.group
)).sort(function(a
,b
) {
1218 return (parseInt($(a
).css("zIndex"),10) || ui
.options
.stack
.min
) - (parseInt($(b
).css("zIndex"),10) || ui
.options
.stack
.min
);
1221 $(group
).each(function(i
) {
1222 this.style
.zIndex
= ui
.options
.stack
.min
+ i
;
1225 this[0].style
.zIndex
= ui
.options
.stack
.min
+ group
.length
;
1229 $.ui
.plugin
.add("draggable", "zIndex", {
1230 start: function(event
, ui
) {
1231 var t
= $(ui
.helper
);
1232 if(t
.css("zIndex")) ui
.options
._zIndex
= t
.css("zIndex");
1233 t
.css('zIndex', ui
.options
.zIndex
);
1235 stop: function(event
, ui
) {
1236 if(ui
.options
._zIndex
) $(ui
.helper
).css('zIndex', ui
.options
._zIndex
);
1242 * jQuery UI Sortable 1.6
1244 * Copyright (c) 2008 AUTHORS.txt (http://ui.jquery.com/about)
1245 * Dual licensed under the MIT (MIT-LICENSE.txt)
1246 * and GPL (GPL-LICENSE.txt) licenses.
1248 * http://docs.jquery.com/UI/Sortables
1255 $.widget("ui.sortable", $.extend({}, $.ui
.mouse
, {
1258 var o
= this.options
;
1259 this.containerCache
= {};
1260 this.element
.addClass("ui-sortable");
1265 //Let's determine if the items are floating
1266 this.floating
= this.items
.length
? (/left|right/).test(this.items
[0].item
.css('float')) : false;
1268 //Let's determine the parent's offset
1269 this.offset
= this.element
.offset();
1271 //Initialize mouse events for interaction
1276 destroy: function() {
1278 .removeClass("ui-sortable ui-sortable-disabled")
1279 .removeData("sortable")
1280 .unbind(".sortable");
1281 this._mouseDestroy();
1283 for ( var i
= this.items
.length
- 1; i
>= 0; i
-- )
1284 this.items
[i
].item
.removeData("sortable-item");
1287 _mouseCapture: function(event
, overrideHandle
) {
1289 if (this.reverting
) {
1293 if(this.options
.disabled
|| this.options
.type
== 'static') return false;
1295 //We have to refresh the items data once first
1296 this._refreshItems(event
);
1298 //Find out if the clicked node (or one of its parents) is a actual item in this.items
1299 var currentItem
= null, self
= this, nodes
= $(event
.target
).parents().each(function() {
1300 if($.data(this, 'sortable-item') == self
) {
1301 currentItem
= $(this);
1305 if($.data(event
.target
, 'sortable-item') == self
) currentItem
= $(event
.target
);
1307 if(!currentItem
) return false;
1308 if(this.options
.handle
&& !overrideHandle
) {
1309 var validHandle
= false;
1311 $(this.options
.handle
, currentItem
).find("*").andSelf().each(function() { if(this == event
.target
) validHandle
= true; });
1312 if(!validHandle
) return false;
1315 this.currentItem
= currentItem
;
1316 this._removeCurrentsFromItems();
1321 _mouseStart: function(event
, overrideHandle
, noActivation
) {
1323 var o
= this.options
;
1324 this.currentContainer
= this;
1326 //We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture
1327 this.refreshPositions();
1329 //Create and append the visible helper
1330 this.helper
= this._createHelper(event
);
1332 //Cache the helper size
1333 this._cacheHelperProportions();
1336 * - Position generation -
1337 * This block generates everything position related - it's the core of draggables.
1340 //Cache the margins of the original element
1341 this._cacheMargins();
1343 //Get the next scrolling parent
1344 this.scrollParent
= this.helper
.scrollParent();
1346 //The element's absolute position on the page minus margins
1347 this.offset
= this.currentItem
.offset();
1350 top
: this.offset
.top
- this.margins
.top
,
1351 left
: this.offset
.left
- this.margins
.left
1354 // Only after we got the offset, we can change the helper's position to absolute
1355 // TODO: Still need to figure out a way to make relative sorting possible
1356 this.helper
.css("position", "absolute");
1357 this.cssPosition
= this.helper
.css("position");
1359 $.extend(this.offset
, {
1360 click
: { //Where the click happened, relative to the element
1361 left
: event
.pageX
- this.offset
.left
,
1362 top
: event
.pageY
- this.offset
.top
1364 parent
: this._getParentOffset(),
1365 relative
: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
1368 //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
1370 this._adjustOffsetFromHelper(o
.cursorAt
);
1372 //Generate the original position
1373 this.originalPosition
= this._generatePosition(event
);
1375 //Cache the former DOM position
1376 this.domPosition
= { prev
: this.currentItem
.prev()[0], parent
: this.currentItem
.parent()[0] };
1378 //If the helper is not the original, hide the original so it's not playing any role during the drag, won't cause anything bad this way
1379 if(this.helper
[0] != this.currentItem
[0]) {
1380 this.currentItem
.hide();
1383 //Create the placeholder
1384 this._createPlaceholder();
1386 //Set a containment if given in the options
1388 this._setContainment();
1390 //Call plugins and callbacks
1391 this._propagate("start", event
);
1393 //Recache the helper size
1394 if(!this._preserveHelperProportions
)
1395 this._cacheHelperProportions();
1398 //Post 'activate' events to possible containers
1400 for (var i
= this.containers
.length
- 1; i
>= 0; i
--) { this.containers
[i
]._propagate("activate", event
, this); }
1403 //Prepare possible droppables
1405 $.ui
.ddmanager
.current
= this;
1407 if ($.ui
.ddmanager
&& !o
.dropBehaviour
)
1408 $.ui
.ddmanager
.prepareOffsets(this, event
);
1410 this.dragging
= true;
1412 this.helper
.addClass('ui-sortable-helper');
1413 this._mouseDrag(event
); //Execute the drag once - this causes the helper not to be visible before getting its correct position
1418 _mouseDrag: function(event
) {
1420 //Compute the helpers position
1421 this.position
= this._generatePosition(event
);
1422 this.positionAbs
= this._convertPositionTo("absolute");
1424 if (!this.lastPositionAbs
) {
1425 this.lastPositionAbs
= this.positionAbs
;
1428 //Call the internal plugins
1429 $.ui
.plugin
.call(this, "sort", [event
, this._ui()]);
1431 //Regenerate the absolute position used for position checks
1432 this.positionAbs
= this._convertPositionTo("absolute");
1434 //Set the helper position
1435 if(!this.options
.axis
|| this.options
.axis
!= "y") this.helper
[0].style
.left
= this.position
.left
+'px';
1436 if(!this.options
.axis
|| this.options
.axis
!= "x") this.helper
[0].style
.top
= this.position
.top
+'px';
1439 for (var i
= this.items
.length
- 1; i
>= 0; i
--) {
1441 //Cache variables and intersection, continue if no intersection
1442 var item
= this.items
[i
], itemElement
= item
.item
[0], intersection
= this._intersectsWithPointer(item
);
1443 if (!intersection
) continue;
1445 if(itemElement
!= this.currentItem
[0] //cannot intersect with itself
1446 && this.placeholder
[intersection
== 1 ? "next" : "prev"]()[0] != itemElement
//no useless actions that have been done before
1447 && !$.ui
.contains(this.placeholder
[0], itemElement
) //no action if the item moved is the parent of the item checked
1448 && (this.options
.type
== 'semi-dynamic' ? !$.ui
.contains(this.element
[0], itemElement
) : true)
1451 this.direction
= intersection
== 1 ? "down" : "up";
1453 if (this.options
.tolerance
== "pointer" || this._intersectsWithSides(item
)) {
1454 this.options
.sortIndicator
.call(this, event
, item
);
1459 this._propagate("change", event
); //Call plugins and callbacks
1464 //Post events to containers
1465 this._contactContainers(event
);
1467 //Interconnect with droppables
1468 if($.ui
.ddmanager
) $.ui
.ddmanager
.drag(this, event
);
1471 this._trigger('sort', event
, this._ui());
1473 this.lastPositionAbs
= this.positionAbs
;
1478 _mouseStop: function(event
, noPropagation
) {
1482 //If we are using droppables, inform the manager about the drop
1483 if ($.ui
.ddmanager
&& !this.options
.dropBehaviour
)
1484 $.ui
.ddmanager
.drop(this, event
);
1486 if(this.options
.revert
) {
1488 var cur
= self
.placeholder
.offset();
1490 self
.reverting
= true;
1492 $(this.helper
).animate({
1493 left
: cur
.left
- this.offset
.parent
.left
- self
.margins
.left
+ (this.offsetParent
[0] == document
.body
? 0 : this.offsetParent
[0].scrollLeft
),
1494 top
: cur
.top
- this.offset
.parent
.top
- self
.margins
.top
+ (this.offsetParent
[0] == document
.body
? 0 : this.offsetParent
[0].scrollTop
)
1495 }, parseInt(this.options
.revert
, 10) || 500, function() {
1499 this._clear(event
, noPropagation
);
1506 cancel: function() {
1512 if(this.options
.helper
== "original")
1513 this.currentItem
.css(this._storedCSS
).removeClass("ui-sortable-helper");
1515 this.currentItem
.show();
1517 //Post deactivating events to containers
1518 for (var i
= this.containers
.length
- 1; i
>= 0; i
--){
1519 this.containers
[i
]._propagate("deactivate", null, this);
1520 if(this.containers
[i
].containerCache
.over
) {
1521 this.containers
[i
]._propagate("out", null, this);
1522 this.containers
[i
].containerCache
.over
= 0;
1528 //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
1529 if(this.placeholder
[0].parentNode
) this.placeholder
[0].parentNode
.removeChild(this.placeholder
[0]);
1530 if(this.options
.helper
!= "original" && this.helper
&& this.helper
[0].parentNode
) this.helper
.remove();
1539 if(this.domPosition
.prev
) {
1540 $(this.domPosition
.prev
).after(this.currentItem
);
1542 $(this.domPosition
.parent
).prepend(this.currentItem
);
1549 serialize: function(o
) {
1551 var items
= this._getItemsAsjQuery(o
&& o
.connected
);
1552 var str
= []; o
= o
|| {};
1554 $(items
).each(function() {
1555 var res
= ($(o
.item
|| this).attr(o
.attribute
|| 'id') || '').match(o
.expression
|| (/(.+)[-=_](.+)/));
1556 if(res
) str
.push((o
.key
|| res
[1]+'[]')+'='+(o
.key
&& o
.expression
? res
[1] : res
[2]));
1559 return str
.join('&');
1563 toArray: function(o
) {
1565 var items
= this._getItemsAsjQuery(o
&& o
.connected
);
1566 var ret
= []; o
= o
|| {};
1568 items
.each(function() { ret
.push($(o
.item
|| this).attr(o
.attribute
|| 'id') || ''); });
1573 /* Be careful with the following core functions */
1574 _intersectsWith: function(item
) {
1576 var x1
= this.positionAbs
.left
,
1577 x2
= x1
+ this.helperProportions
.width
,
1578 y1
= this.positionAbs
.top
,
1579 y2
= y1
+ this.helperProportions
.height
;
1584 b
= t
+ item
.height
;
1586 var dyClick
= this.offset
.click
.top
,
1587 dxClick
= this.offset
.click
.left
;
1589 var isOverElement
= (y1
+ dyClick
) > t
&& (y1
+ dyClick
) < b
&& (x1
+ dxClick
) > l
&& (x1
+ dxClick
) < r
;
1591 if( this.options
.tolerance
== "pointer"
1592 || this.options
.forcePointerForContainers
1593 || (this.options
.tolerance
!= "pointer" && this.helperProportions
[this.floating
? 'width' : 'height'] > item
[this.floating
? 'width' : 'height'])
1595 return isOverElement
;
1598 return (l
< x1
+ (this.helperProportions
.width
/ 2) // Right Half
1599 && x2
- (this.helperProportions
.width
/ 2) < r
// Left Half
1600 && t
< y1
+ (this.helperProportions
.height
/ 2) // Bottom Half
1601 && y2
- (this.helperProportions
.height
/ 2) < b
); // Top Half
1606 _intersectsWithPointer: function(item
) {
1608 var isOverElementHeight
= $.ui
.isOverAxis(this.positionAbs
.top
+ this.offset
.click
.top
, item
.top
, item
.height
),
1609 isOverElementWidth
= $.ui
.isOverAxis(this.positionAbs
.left
+ this.offset
.click
.left
, item
.left
, item
.width
),
1610 isOverElement
= isOverElementHeight
&& isOverElementWidth
,
1611 verticalDirection
= this._getDragVerticalDirection(),
1612 horizontalDirection
= this._getDragHorizontalDirection();
1617 return this.floating
?
1618 ( ((horizontalDirection
&& horizontalDirection
== "right") || verticalDirection
== "down") ? 2 : 1 )
1619 : ( verticalDirection
&& (verticalDirection
== "down" ? 2 : 1) );
1623 _intersectsWithSides: function(item
) {
1625 var isOverBottomHalf
= $.ui
.isOverAxis(this.positionAbs
.top
+ this.offset
.click
.top
, item
.top
+ (item
.height
/2), item
.height
),
1626 isOverRightHalf
= $.ui
.isOverAxis(this.positionAbs
.left
+ this.offset
.click
.left
, item
.left
+ (item
.width
/2), item
.width
),
1627 verticalDirection
= this._getDragVerticalDirection(),
1628 horizontalDirection
= this._getDragHorizontalDirection();
1630 if (this.floating
&& horizontalDirection
) {
1631 return ((horizontalDirection
== "right" && isOverRightHalf
) || (horizontalDirection
== "left" && !isOverRightHalf
));
1633 return verticalDirection
&& ((verticalDirection
== "down" && isOverBottomHalf
) || (verticalDirection
== "up" && !isOverBottomHalf
));
1638 _getDragVerticalDirection: function() {
1639 var delta
= this.positionAbs
.top
- this.lastPositionAbs
.top
;
1640 return delta
!= 0 && (delta
> 0 ? "down" : "up");
1643 _getDragHorizontalDirection: function() {
1644 var delta
= this.positionAbs
.left
- this.lastPositionAbs
.left
;
1645 return delta
!= 0 && (delta
> 0 ? "right" : "left");
1648 refresh: function(event
) {
1649 this._refreshItems(event
);
1650 this.refreshPositions();
1653 _getItemsAsjQuery: function(connected
) {
1659 if(this.options
.connectWith
&& connected
) {
1660 for (var i
= this.options
.connectWith
.length
- 1; i
>= 0; i
--){
1661 var cur
= $(this.options
.connectWith
[i
]);
1662 for (var j
= cur
.length
- 1; j
>= 0; j
--){
1663 var inst
= $.data(cur
[j
], 'sortable');
1664 if(inst
&& inst
!= this && !inst
.options
.disabled
) {
1665 queries
.push([$.isFunction(inst
.options
.items
) ? inst
.options
.items
.call(inst
.element
) : $(inst
.options
.items
, inst
.element
).not(".ui-sortable-helper"), inst
]);
1671 queries
.push([$.isFunction(this.options
.items
) ? this.options
.items
.call(this.element
, null, { options
: this.options
, item
: this.currentItem
}) : $(this.options
.items
, this.element
).not(".ui-sortable-helper"), this]);
1673 for (var i
= queries
.length
- 1; i
>= 0; i
--){
1674 queries
[i
][0].each(function() {
1683 _removeCurrentsFromItems: function() {
1685 var list
= this.currentItem
.find(":data(sortable-item)");
1687 for (var i
=0; i
< this.items
.length
; i
++) {
1689 for (var j
=0; j
< list
.length
; j
++) {
1690 if(list
[j
] == this.items
[i
].item
[0])
1691 this.items
.splice(i
,1);
1698 _refreshItems: function(event
) {
1701 this.containers
= [this];
1702 var items
= this.items
;
1704 var queries
= [[$.isFunction(this.options
.items
) ? this.options
.items
.call(this.element
[0], event
, { item
: this.currentItem
}) : $(this.options
.items
, this.element
), this]];
1706 if(this.options
.connectWith
) {
1707 for (var i
= this.options
.connectWith
.length
- 1; i
>= 0; i
--){
1708 var cur
= $(this.options
.connectWith
[i
]);
1709 for (var j
= cur
.length
- 1; j
>= 0; j
--){
1710 var inst
= $.data(cur
[j
], 'sortable');
1711 if(inst
&& inst
!= this && !inst
.options
.disabled
) {
1712 queries
.push([$.isFunction(inst
.options
.items
) ? inst
.options
.items
.call(inst
.element
[0], event
, { item
: this.currentItem
}) : $(inst
.options
.items
, inst
.element
), inst
]);
1713 this.containers
.push(inst
);
1719 for (var i
= queries
.length
- 1; i
>= 0; i
--) {
1720 var targetData
= queries
[i
][1];
1721 var _queries
= queries
[i
][0];
1723 for (var j
=0, queriesLength
= _queries
.length
; j
< queriesLength
; j
++) {
1724 var item
= $(_queries
[j
]);
1726 item
.data('sortable-item', targetData
); // Data for target checking (mouse manager)
1730 instance
: targetData
,
1731 width
: 0, height
: 0,
1739 refreshPositions: function(fast
) {
1741 //This has to be redone because due to the item being moved out/into the offsetParent, the offsetParent's position will change
1742 if(this.offsetParent
&& this.helper
) {
1743 this.offset
.parent
= this._getParentOffset();
1746 for (var i
= this.items
.length
- 1; i
>= 0; i
--){
1747 var item
= this.items
[i
];
1749 //We ignore calculating positions of all connected containers when we're not over them
1750 if(item
.instance
!= this.currentContainer
&& this.currentContainer
&& item
.item
[0] != this.currentItem
[0])
1753 var t
= this.options
.toleranceElement
? $(this.options
.toleranceElement
, item
.item
) : item
.item
;
1756 if (this.options
.accurateIntersection
) {
1757 item
.width
= t
.outerWidth();
1758 item
.height
= t
.outerHeight();
1761 item
.width
= t
[0].offsetWidth
;
1762 item
.height
= t
[0].offsetHeight
;
1771 if(this.options
.custom
&& this.options
.custom
.refreshContainers
) {
1772 this.options
.custom
.refreshContainers
.call(this);
1774 for (var i
= this.containers
.length
- 1; i
>= 0; i
--){
1775 var p
= this.containers
[i
].element
.offset();
1776 this.containers
[i
].containerCache
.left
= p
.left
;
1777 this.containers
[i
].containerCache
.top
= p
.top
;
1778 this.containers
[i
].containerCache
.width
= this.containers
[i
].element
.outerWidth();
1779 this.containers
[i
].containerCache
.height
= this.containers
[i
].element
.outerHeight();
1785 _createPlaceholder: function(that
) {
1787 var self
= that
|| this, o
= self
.options
;
1789 if(!o
.placeholder
|| o
.placeholder
.constructor == String
) {
1790 var className
= o
.placeholder
;
1792 element: function() {
1794 var el
= $(document
.createElement(self
.currentItem
[0].nodeName
))
1795 .addClass(className
|| self
.currentItem
[0].className
+" ui-sortable-placeholder")
1796 .removeClass('ui-sortable-helper')[0];
1799 el
.style
.visibility
= "hidden";
1800 document
.body
.appendChild(el
);
1801 // Name attributes are removed, otherwice causes elements to be unchecked
1802 // Expando attributes also have to be removed because of stupid IE (no condition, doesn't hurt in other browsers)
1803 el
.innerHTML
= self
.currentItem
[0].innerHTML
.replace(/name\=\"[^\"\']+\"/g, '').replace(/jQuery[0-9]+\=\"[^\"\']+\"/g, '');
1804 document
.body
.removeChild(el
);
1809 update: function(container
, p
) {
1810 if(className
&& !o
.forcePlaceholderSize
) return;
1811 if(!p
.height()) { p
.height(self
.currentItem
.innerHeight() - parseInt(self
.currentItem
.css('paddingTop')||0, 10) - parseInt(self
.currentItem
.css('paddingBottom')||0, 10)); };
1812 if(!p
.width()) { p
.width(self
.currentItem
.innerWidth() - parseInt(self
.currentItem
.css('paddingLeft')||0, 10) - parseInt(self
.currentItem
.css('paddingRight')||0, 10)); };
1817 //Create the placeholder
1818 self
.placeholder
= $(o
.placeholder
.element
.call(self
.element
, self
.currentItem
));
1820 //Append it after the actual current item
1821 self
.currentItem
.after(self
.placeholder
);
1823 //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317)
1824 o
.placeholder
.update(self
, self
.placeholder
);
1828 _contactContainers: function(event
) {
1829 for (var i
= this.containers
.length
- 1; i
>= 0; i
--){
1831 if(this._intersectsWith(this.containers
[i
].containerCache
)) {
1832 if(!this.containers
[i
].containerCache
.over
) {
1834 if(this.currentContainer
!= this.containers
[i
]) {
1836 //When entering a new container, we will find the item with the least distance and append our item near it
1837 var dist
= 10000; var itemWithLeastDistance
= null; var base
= this.positionAbs
[this.containers
[i
].floating
? 'left' : 'top'];
1838 for (var j
= this.items
.length
- 1; j
>= 0; j
--) {
1839 if(!$.ui
.contains(this.containers
[i
].element
[0], this.items
[j
].item
[0])) continue;
1840 var cur
= this.items
[j
][this.containers
[i
].floating
? 'left' : 'top'];
1841 if(Math
.abs(cur
- base
) < dist
) {
1842 dist
= Math
.abs(cur
- base
); itemWithLeastDistance
= this.items
[j
];
1846 if(!itemWithLeastDistance
&& !this.options
.dropOnEmpty
) //Check if dropOnEmpty is enabled
1849 this.currentContainer
= this.containers
[i
];
1850 itemWithLeastDistance
? this.options
.sortIndicator
.call(this, event
, itemWithLeastDistance
, null, true) : this.options
.sortIndicator
.call(this, event
, null, this.containers
[i
].element
, true);
1851 this._propagate("change", event
); //Call plugins and callbacks
1852 this.containers
[i
]._propagate("change", event
, this); //Call plugins and callbacks
1854 //Update the placeholder
1855 this.options
.placeholder
.update(this.currentContainer
, this.placeholder
);
1859 this.containers
[i
]._propagate("over", event
, this);
1860 this.containers
[i
].containerCache
.over
= 1;
1863 if(this.containers
[i
].containerCache
.over
) {
1864 this.containers
[i
]._propagate("out", event
, this);
1865 this.containers
[i
].containerCache
.over
= 0;
1872 _createHelper: function(event
) {
1874 var o
= this.options
;
1875 var helper
= $.isFunction(o
.helper
) ? $(o
.helper
.apply(this.element
[0], [event
, this.currentItem
])) : (o
.helper
== 'clone' ? this.currentItem
.clone() : this.currentItem
);
1877 if(!helper
.parents('body').length
) //Add the helper to the DOM if that didn't happen already
1878 $(o
.appendTo
!= 'parent' ? o
.appendTo
: this.currentItem
[0].parentNode
)[0].appendChild(helper
[0]);
1880 if(helper
[0] == this.currentItem
[0])
1881 this._storedCSS
= { width
: this.currentItem
[0].style
.width
, height
: this.currentItem
[0].style
.height
, position
: this.currentItem
.css("position"), top
: this.currentItem
.css("top"), left
: this.currentItem
.css("left") };
1883 if(helper
[0].style
.width
== '' || o
.forceHelperSize
) helper
.width(this.currentItem
.width());
1884 if(helper
[0].style
.height
== '' || o
.forceHelperSize
) helper
.height(this.currentItem
.height());
1890 _adjustOffsetFromHelper: function(obj
) {
1891 if(obj
.left
!= undefined) this.offset
.click
.left
= obj
.left
+ this.margins
.left
;
1892 if(obj
.right
!= undefined) this.offset
.click
.left
= this.helperProportions
.width
- obj
.right
+ this.margins
.left
;
1893 if(obj
.top
!= undefined) this.offset
.click
.top
= obj
.top
+ this.margins
.top
;
1894 if(obj
.bottom
!= undefined) this.offset
.click
.top
= this.helperProportions
.height
- obj
.bottom
+ this.margins
.top
;
1897 _getParentOffset: function() {
1899 //Get the offsetParent and cache its position
1900 this.offsetParent
= this.helper
.offsetParent(); var po
= this.offsetParent
.offset();
1902 if((this.offsetParent
[0] == document
.body
&& $.browser
.mozilla
) //Ugly FF3 fix
1903 || (this.offsetParent
[0].tagName
&& this.offsetParent
[0].tagName
.toLowerCase() == 'html' && $.browser
.msie
)) //Ugly IE fix
1904 po
= { top
: 0, left
: 0 };
1907 top
: po
.top
+ (parseInt(this.offsetParent
.css("borderTopWidth"),10) || 0),
1908 left
: po
.left
+ (parseInt(this.offsetParent
.css("borderLeftWidth"),10) || 0)
1913 _getRelativeOffset: function() {
1915 if(this.cssPosition
== "relative") {
1916 var p
= this.currentItem
.position();
1918 top
: p
.top
- (parseInt(this.helper
.css("top"),10) || 0) + this.scrollParent
.scrollTop(),
1919 left
: p
.left
- (parseInt(this.helper
.css("left"),10) || 0) + this.scrollParent
.scrollLeft()
1922 return { top
: 0, left
: 0 };
1927 _cacheMargins: function() {
1929 left
: (parseInt(this.currentItem
.css("marginLeft"),10) || 0),
1930 top
: (parseInt(this.currentItem
.css("marginTop"),10) || 0)
1934 _cacheHelperProportions: function() {
1935 this.helperProportions
= {
1936 width
: this.helper
.outerWidth(),
1937 height
: this.helper
.outerHeight()
1941 _setContainment: function() {
1943 var o
= this.options
;
1944 if(o
.containment
== 'parent') o
.containment
= this.helper
[0].parentNode
;
1945 if(o
.containment
== 'document' || o
.containment
== 'window') this.containment
= [
1946 0 - this.offset
.relative
.left
- this.offset
.parent
.left
,
1947 0 - this.offset
.relative
.top
- this.offset
.parent
.top
,
1948 $(o
.containment
== 'document' ? document
: window
).width() - this.offset
.relative
.left
- this.offset
.parent
.left
- this.margins
.left
- (parseInt(this.currentItem
.css("marginRight"),10) || 0),
1949 ($(o
.containment
== 'document' ? document
: window
).height() || document
.body
.parentNode
.scrollHeight
) - this.offset
.relative
.top
- this.offset
.parent
.top
- this.margins
.top
- (parseInt(this.currentItem
.css("marginBottom"),10) || 0)
1952 if(!(/^(document|window|parent)$/).test(o
.containment
)) {
1953 var ce
= $(o
.containment
)[0];
1954 var co
= $(o
.containment
).offset();
1955 var over
= ($(ce
).css("overflow") != 'hidden');
1957 this.containment
= [
1958 co
.left
+ (parseInt($(ce
).css("borderLeftWidth"),10) || 0) - this.offset
.relative
.left
- this.offset
.parent
.left
- this.margins
.left
,
1959 co
.top
+ (parseInt($(ce
).css("borderTopWidth"),10) || 0) - this.offset
.relative
.top
- this.offset
.parent
.top
- this.margins
.top
,
1960 co
.left
+ (over
? Math
.max(ce
.scrollWidth
,ce
.offsetWidth
) : ce
.offsetWidth
) - (parseInt($(ce
).css("borderLeftWidth"),10) || 0) - this.offset
.relative
.left
- this.offset
.parent
.left
- this.margins
.left
,
1961 co
.top
+ (over
? Math
.max(ce
.scrollHeight
,ce
.offsetHeight
) : ce
.offsetHeight
) - (parseInt($(ce
).css("borderTopWidth"),10) || 0) - this.offset
.relative
.top
- this.offset
.parent
.top
- this.margins
.top
1967 _convertPositionTo: function(d
, pos
) {
1969 if(!pos
) pos
= this.position
;
1970 var mod
= d
== "absolute" ? 1 : -1;
1971 var scroll
= this[(this.cssPosition
== 'absolute' ? 'offset' : 'scroll')+'Parent'], scrollIsRootNode
= (/(html|body)/i).test(scroll
[0].tagName
);
1975 pos
.top
// the calculated relative position
1976 + this.offset
.relative
.top
* mod
// Only for relative positioned nodes: Relative offset from element to offset parent
1977 + this.offset
.parent
.top
* mod
// The offsetParent's offset without borders (offset + border)
1978 + ( this.cssPosition
== 'fixed' ? -this.scrollParent
.scrollTop() : ( scrollIsRootNode
? 0 : scroll
.scrollTop() ) ) * mod
1979 + this.margins
.top
* mod
//Add the margin (you don't want the margin counting in intersection methods)
1982 pos
.left
// the calculated relative position
1983 + this.offset
.relative
.left
* mod
// Only for relative positioned nodes: Relative offset from element to offset parent
1984 + this.offset
.parent
.left
* mod
// The offsetParent's offset without borders (offset + border)
1985 + ( this.cssPosition
== 'fixed' ? -this.scrollParent
.scrollLeft() : ( scrollIsRootNode
? 0 : scroll
.scrollLeft() ) ) * mod
1986 + this.margins
.left
* mod
//Add the margin (you don't want the margin counting in intersection methods)
1991 _generatePosition: function(event
) {
1993 var o
= this.options
, scroll
= this[(this.cssPosition
== 'absolute' ? 'offset' : 'scroll')+'Parent'], scrollIsRootNode
= (/(html|body)/i).test(scroll
[0].tagName
);
1997 event
.pageY
// The absolute mouse position
1998 - this.offset
.click
.top
// Click offset (relative to the element)
1999 - this.offset
.relative
.top
// Only for relative positioned nodes: Relative offset from element to offset parent
2000 - this.offset
.parent
.top
// The offsetParent's offset without borders (offset + border)
2001 + ( this.cssPosition
== 'fixed' ? -this.scrollParent
.scrollTop() : ( scrollIsRootNode
? 0 : scroll
.scrollTop() ) )
2004 event
.pageX
// The absolute mouse position
2005 - this.offset
.click
.left
// Click offset (relative to the element)
2006 - this.offset
.relative
.left
// Only for relative positioned nodes: Relative offset from element to offset parent
2007 - this.offset
.parent
.left
// The offsetParent's offset without borders (offset + border)
2008 + ( this.cssPosition
== 'fixed' ? -this.scrollParent
.scrollLeft() : ( scrollIsRootNode
? 0 : scroll
.scrollLeft() ) )
2012 if(!this.originalPosition
) return position
; //If we are not dragging yet, we won't check for options
2015 * - Position constraining -
2016 * Constrain the position to a mix of grid, containment.
2018 if(this.containment
) {
2019 if(position
.left
< this.containment
[0]) position
.left
= this.containment
[0];
2020 if(position
.top
< this.containment
[1]) position
.top
= this.containment
[1];
2021 if(position
.left
+ this.helperProportions
.width
> this.containment
[2]) position
.left
= this.containment
[2] - this.helperProportions
.width
;
2022 if(position
.top
+ this.helperProportions
.height
> this.containment
[3]) position
.top
= this.containment
[3] - this.helperProportions
.height
;
2026 var top
= this.originalPosition
.top
+ Math
.round((position
.top
- this.originalPosition
.top
) / o
.grid
[1]) * o
.grid
[1];
2027 position
.top
= this.containment
? (!(top
< this.containment
[1] || top
> this.containment
[3]) ? top
: (!(top
< this.containment
[1]) ? top
- o
.grid
[1] : top
+ o
.grid
[1])) : top
;
2029 var left
= this.originalPosition
.left
+ Math
.round((position
.left
- this.originalPosition
.left
) / o
.grid
[0]) * o
.grid
[0];
2030 position
.left
= this.containment
? (!(left
< this.containment
[0] || left
> this.containment
[2]) ? left
: (!(left
< this.containment
[0]) ? left
- o
.grid
[0] : left
+ o
.grid
[0])) : left
;
2036 _rearrange: function(event
, i
, a
, hardRefresh
) {
2038 a
? a
[0].appendChild(this.placeholder
[0]) : i
.item
[0].parentNode
.insertBefore(this.placeholder
[0], (this.direction
== 'down' ? i
.item
[0] : i
.item
[0].nextSibling
));
2040 //Various things done here to improve the performance:
2041 // 1. we create a setTimeout, that calls refreshPositions
2042 // 2. on the instance, we have a counter variable, that get's higher after every append
2043 // 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same
2044 // 4. this lets only the last addition to the timeout stack through
2045 this.counter
= this.counter
? ++this.counter
: 1;
2046 var self
= this, counter
= this.counter
;
2048 window
.setTimeout(function() {
2049 if(counter
== self
.counter
) self
.refreshPositions(!hardRefresh
); //Precompute after each DOM insertion, NOT on mousemove
2054 _clear: function(event
, noPropagation
) {
2056 this.reverting
= false;
2058 //We first have to update the dom position of the actual currentItem
2059 if(!this._noFinalSort
) this.placeholder
.before(this.currentItem
);
2060 this._noFinalSort
= null;
2062 if(this.helper
[0] == this.currentItem
[0]) {
2063 for(var i
in this._storedCSS
) {
2064 if(this._storedCSS
[i
] == 'auto' || this._storedCSS
[i
] == 'static') this._storedCSS
[i
] = '';
2066 this.currentItem
.css(this._storedCSS
).removeClass("ui-sortable-helper");
2068 this.currentItem
.show();
2071 if(this.domPosition
.prev
!= this.currentItem
.prev().not(".ui-sortable-helper")[0] || this.domPosition
.parent
!= this.currentItem
.parent()[0]) this._propagate("update", event
, null, noPropagation
); //Trigger update callback if the DOM position has changed
2072 if(!$.ui
.contains(this.element
[0], this.currentItem
[0])) { //Node was moved out of the current element
2073 this._propagate("remove", event
, null, noPropagation
);
2074 for (var i
= this.containers
.length
- 1; i
>= 0; i
--){
2075 if($.ui
.contains(this.containers
[i
].element
[0], this.currentItem
[0])) {
2076 this.containers
[i
]._propagate("update", event
, this, noPropagation
);
2077 this.containers
[i
]._propagate("receive", event
, this, noPropagation
);
2082 //Post events to containers
2083 for (var i
= this.containers
.length
- 1; i
>= 0; i
--){
2084 this.containers
[i
]._propagate("deactivate", event
, this, noPropagation
);
2085 if(this.containers
[i
].containerCache
.over
) {
2086 this.containers
[i
]._propagate("out", event
, this);
2087 this.containers
[i
].containerCache
.over
= 0;
2091 this.dragging
= false;
2092 if(this.cancelHelperRemoval
) {
2093 this._propagate("beforeStop", event
, null, noPropagation
);
2094 this._propagate("stop", event
, null, noPropagation
);
2098 this._propagate("beforeStop", event
, null, noPropagation
);
2100 //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
2101 this.placeholder
[0].parentNode
.removeChild(this.placeholder
[0]);
2103 if(this.options
.helper
!= "original") this.helper
.remove(); this.helper
= null;
2104 this._propagate("stop", event
, null, noPropagation
);
2110 _propagate: function(n
, event
, inst
, noPropagation
) {
2111 $.ui
.plugin
.call(this, n
, [event
, this._ui(inst
)]);
2112 var dontCancel
= !noPropagation
? this.element
.triggerHandler(n
== "sort" ? n
: "sort"+n
, [event
, this._ui(inst
)], this.options
[n
]) : true;
2113 if(dontCancel
=== false) this.cancel();
2118 _ui: function(inst
) {
2119 var self
= inst
|| this;
2121 helper
: self
.helper
,
2122 placeholder
: self
.placeholder
|| $([]),
2123 position
: self
.position
,
2124 absolutePosition
: self
.positionAbs
,
2125 item
: self
.currentItem
,
2126 sender
: inst
? inst
.element
: null
2132 $.extend($.ui
.sortable
, {
2133 getter
: "serialize toArray",
2136 accurateIntersection
: true,
2142 forcePlaceholderSize
: false,
2143 forceHelperSize
: false,
2148 scrollSensitivity
: 20,
2150 sortIndicator
: $.ui
.sortable
.prototype._rearrange
,
2151 tolerance
: "default",
2157 * Sortable Extensions
2160 $.ui
.plugin
.add("sortable", "cursor", {
2161 start: function(event
, ui
) {
2162 var t
= $('body'), i
= $(this).data('sortable');
2163 if (t
.css("cursor")) i
.options
._cursor
= t
.css("cursor");
2164 t
.css("cursor", i
.options
.cursor
);
2166 beforeStop: function(event
, ui
) {
2167 var i
= $(this).data('sortable');
2168 if (i
.options
._cursor
) $('body').css("cursor", i
.options
._cursor
);
2172 $.ui
.plugin
.add("sortable", "opacity", {
2173 start: function(event
, ui
) {
2174 var t
= ui
.helper
, i
= $(this).data('sortable');
2175 if(t
.css("opacity")) i
.options
._opacity
= t
.css("opacity");
2176 t
.css('opacity', i
.options
.opacity
);
2178 beforeStop: function(event
, ui
) {
2179 var i
= $(this).data('sortable');
2180 if(i
.options
._opacity
) $(ui
.helper
).css('opacity', i
.options
._opacity
);
2184 $.ui
.plugin
.add("sortable", "scroll", {
2185 start: function(event
, ui
) {
2186 var i
= $(this).data("sortable"), o
= i
.options
;
2187 if(i
.scrollParent
[0] != document
&& i
.scrollParent
[0].tagName
!= 'HTML') i
.overflowOffset
= i
.scrollParent
.offset();
2189 sort: function(event
, ui
) {
2191 var i
= $(this).data("sortable"), o
= i
.options
, scrolled
= false;
2193 if(i
.scrollParent
[0] != document
&& i
.scrollParent
[0].tagName
!= 'HTML') {
2195 if((i
.overflowOffset
.top
+ i
.scrollParent
[0].offsetHeight
) - event
.pageY
< o
.scrollSensitivity
)
2196 i
.scrollParent
[0].scrollTop
= scrolled
= i
.scrollParent
[0].scrollTop
+ o
.scrollSpeed
;
2197 else if(event
.pageY
- i
.overflowOffset
.top
< o
.scrollSensitivity
)
2198 i
.scrollParent
[0].scrollTop
= scrolled
= i
.scrollParent
[0].scrollTop
- o
.scrollSpeed
;
2200 if((i
.overflowOffset
.left
+ i
.scrollParent
[0].offsetWidth
) - event
.pageX
< o
.scrollSensitivity
)
2201 i
.scrollParent
[0].scrollLeft
= scrolled
= i
.scrollParent
[0].scrollLeft
+ o
.scrollSpeed
;
2202 else if(event
.pageX
- i
.overflowOffset
.left
< o
.scrollSensitivity
)
2203 i
.scrollParent
[0].scrollLeft
= scrolled
= i
.scrollParent
[0].scrollLeft
- o
.scrollSpeed
;
2207 if(event
.pageY
- $(document
).scrollTop() < o
.scrollSensitivity
)
2208 scrolled
= $(document
).scrollTop($(document
).scrollTop() - o
.scrollSpeed
);
2209 else if($(window
).height() - (event
.pageY
- $(document
).scrollTop()) < o
.scrollSensitivity
)
2210 scrolled
= $(document
).scrollTop($(document
).scrollTop() + o
.scrollSpeed
);
2212 if(event
.pageX
- $(document
).scrollLeft() < o
.scrollSensitivity
)
2213 scrolled
= $(document
).scrollLeft($(document
).scrollLeft() - o
.scrollSpeed
);
2214 else if($(window
).width() - (event
.pageX
- $(document
).scrollLeft()) < o
.scrollSensitivity
)
2215 scrolled
= $(document
).scrollLeft($(document
).scrollLeft() + o
.scrollSpeed
);
2219 if(scrolled
!== false && $.ui
.ddmanager
&& !o
.dropBehaviour
)
2220 $.ui
.ddmanager
.prepareOffsets(i
, event
);
2224 //This is a special case where we need to modify a offset calculated on start, since the following happened:
2225 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
2226 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
2227 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
2228 if(scrolled
!== false && i
.cssPosition
== 'absolute' && i
.scrollParent
[0] != document
&& $.ui
.contains(i
.scrollParent
[0], i
.offsetParent
[0])) {
2229 i
.offset
.parent
= i
._getParentOffset();
2232 // This is another very weird special case that only happens for relative elements:
2233 // 1. If the css position is relative
2234 // 2. and the scroll parent is the document or similar to the offset parent
2235 // we have to refresh the relative offset during the scroll so there are no jumps
2236 if(scrolled
!== false && i
.cssPosition
== 'relative' && !(i
.scrollParent
[0] != document
&& i
.scrollParent
[0] != i
.offsetParent
[0])) {
2237 i
.offset
.relative
= i
._getRelativeOffset();
2243 $.ui
.plugin
.add("sortable", "zIndex", {
2244 start: function(event
, ui
) {
2245 var t
= ui
.helper
, i
= $(this).data('sortable');
2246 if(t
.css("zIndex")) i
.options
._zIndex
= t
.css("zIndex");
2247 t
.css('zIndex', i
.options
.zIndex
);
2249 beforeStop: function(event
, ui
) {
2250 var i
= $(this).data('sortable');
2251 if(i
.options
._zIndex
) $(ui
.helper
).css('zIndex', i
.options
._zIndex
== 'auto' ? '' : i
.options
._zIndex
);