Some fixes to our jQuery UI skin for buttons
[lhc/web/wiklou.git] / resources / jquery.ui / jquery.ui.draggable.js
1 /*!
2 * jQuery UI Draggable 1.8.20
3 *
4 * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
5 * Dual licensed under the MIT or GPL Version 2 licenses.
6 * http://jquery.org/license
7 *
8 * http://docs.jquery.com/UI/Draggables
9 *
10 * Depends:
11 * jquery.ui.core.js
12 * jquery.ui.mouse.js
13 * jquery.ui.widget.js
14 */
15 (function( $, undefined ) {
16
17 $.widget("ui.draggable", $.ui.mouse, {
18 widgetEventPrefix: "drag",
19 options: {
20 addClasses: true,
21 appendTo: "parent",
22 axis: false,
23 connectToSortable: false,
24 containment: false,
25 cursor: "auto",
26 cursorAt: false,
27 grid: false,
28 handle: false,
29 helper: "original",
30 iframeFix: false,
31 opacity: false,
32 refreshPositions: false,
33 revert: false,
34 revertDuration: 500,
35 scope: "default",
36 scroll: true,
37 scrollSensitivity: 20,
38 scrollSpeed: 20,
39 snap: false,
40 snapMode: "both",
41 snapTolerance: 20,
42 stack: false,
43 zIndex: false
44 },
45 _create: function() {
46
47 if (this.options.helper == 'original' && !(/^(?:r|a|f)/).test(this.element.css("position")))
48 this.element[0].style.position = 'relative';
49
50 (this.options.addClasses && this.element.addClass("ui-draggable"));
51 (this.options.disabled && this.element.addClass("ui-draggable-disabled"));
52
53 this._mouseInit();
54
55 },
56
57 destroy: function() {
58 if(!this.element.data('draggable')) return;
59 this.element
60 .removeData("draggable")
61 .unbind(".draggable")
62 .removeClass("ui-draggable"
63 + " ui-draggable-dragging"
64 + " ui-draggable-disabled");
65 this._mouseDestroy();
66
67 return this;
68 },
69
70 _mouseCapture: function(event) {
71
72 var o = this.options;
73
74 // among others, prevent a drag on a resizable-handle
75 if (this.helper || o.disabled || $(event.target).is('.ui-resizable-handle'))
76 return false;
77
78 //Quit if we're not on a valid handle
79 this.handle = this._getHandle(event);
80 if (!this.handle)
81 return false;
82
83 if ( o.iframeFix ) {
84 $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
85 $('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>')
86 .css({
87 width: this.offsetWidth+"px", height: this.offsetHeight+"px",
88 position: "absolute", opacity: "0.001", zIndex: 1000
89 })
90 .css($(this).offset())
91 .appendTo("body");
92 });
93 }
94
95 return true;
96
97 },
98
99 _mouseStart: function(event) {
100
101 var o = this.options;
102
103 //Create and append the visible helper
104 this.helper = this._createHelper(event);
105
106 //Cache the helper size
107 this._cacheHelperProportions();
108
109 //If ddmanager is used for droppables, set the global draggable
110 if($.ui.ddmanager)
111 $.ui.ddmanager.current = this;
112
113 /*
114 * - Position generation -
115 * This block generates everything position related - it's the core of draggables.
116 */
117
118 //Cache the margins of the original element
119 this._cacheMargins();
120
121 //Store the helper's css position
122 this.cssPosition = this.helper.css("position");
123 this.scrollParent = this.helper.scrollParent();
124
125 //The element's absolute position on the page minus margins
126 this.offset = this.positionAbs = this.element.offset();
127 this.offset = {
128 top: this.offset.top - this.margins.top,
129 left: this.offset.left - this.margins.left
130 };
131
132 $.extend(this.offset, {
133 click: { //Where the click happened, relative to the element
134 left: event.pageX - this.offset.left,
135 top: event.pageY - this.offset.top
136 },
137 parent: this._getParentOffset(),
138 relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
139 });
140
141 //Generate the original position
142 this.originalPosition = this.position = this._generatePosition(event);
143 this.originalPageX = event.pageX;
144 this.originalPageY = event.pageY;
145
146 //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
147 (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
148
149 //Set a containment if given in the options
150 if(o.containment)
151 this._setContainment();
152
153 //Trigger event + callbacks
154 if(this._trigger("start", event) === false) {
155 this._clear();
156 return false;
157 }
158
159 //Recache the helper size
160 this._cacheHelperProportions();
161
162 //Prepare the droppable offsets
163 if ($.ui.ddmanager && !o.dropBehaviour)
164 $.ui.ddmanager.prepareOffsets(this, event);
165
166 this.helper.addClass("ui-draggable-dragging");
167 this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
168
169 //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
170 if ( $.ui.ddmanager ) $.ui.ddmanager.dragStart(this, event);
171
172 return true;
173 },
174
175 _mouseDrag: function(event, noPropagation) {
176
177 //Compute the helpers position
178 this.position = this._generatePosition(event);
179 this.positionAbs = this._convertPositionTo("absolute");
180
181 //Call plugins and callbacks and use the resulting position if something is returned
182 if (!noPropagation) {
183 var ui = this._uiHash();
184 if(this._trigger('drag', event, ui) === false) {
185 this._mouseUp({});
186 return false;
187 }
188 this.position = ui.position;
189 }
190
191 if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
192 if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
193 if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
194
195 return false;
196 },
197
198 _mouseStop: function(event) {
199
200 //If we are using droppables, inform the manager about the drop
201 var dropped = false;
202 if ($.ui.ddmanager && !this.options.dropBehaviour)
203 dropped = $.ui.ddmanager.drop(this, event);
204
205 //if a drop comes from outside (a sortable)
206 if(this.dropped) {
207 dropped = this.dropped;
208 this.dropped = false;
209 }
210
211 //if the original element is no longer in the DOM don't bother to continue (see #8269)
212 var element = this.element[0], elementInDom = false;
213 while ( element && (element = element.parentNode) ) {
214 if (element == document ) {
215 elementInDom = true;
216 }
217 }
218 if ( !elementInDom && this.options.helper === "original" )
219 return false;
220
221 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))) {
222 var self = this;
223 $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
224 if(self._trigger("stop", event) !== false) {
225 self._clear();
226 }
227 });
228 } else {
229 if(this._trigger("stop", event) !== false) {
230 this._clear();
231 }
232 }
233
234 return false;
235 },
236
237 _mouseUp: function(event) {
238 if (this.options.iframeFix === true) {
239 $("div.ui-draggable-iframeFix").each(function() {
240 this.parentNode.removeChild(this);
241 }); //Remove frame helpers
242 }
243
244 //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003)
245 if( $.ui.ddmanager ) $.ui.ddmanager.dragStop(this, event);
246
247 return $.ui.mouse.prototype._mouseUp.call(this, event);
248 },
249
250 cancel: function() {
251
252 if(this.helper.is(".ui-draggable-dragging")) {
253 this._mouseUp({});
254 } else {
255 this._clear();
256 }
257
258 return this;
259
260 },
261
262 _getHandle: function(event) {
263
264 var handle = !this.options.handle || !$(this.options.handle, this.element).length ? true : false;
265 $(this.options.handle, this.element)
266 .find("*")
267 .andSelf()
268 .each(function() {
269 if(this == event.target) handle = true;
270 });
271
272 return handle;
273
274 },
275
276 _createHelper: function(event) {
277
278 var o = this.options;
279 var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper == 'clone' ? this.element.clone().removeAttr('id') : this.element);
280
281 if(!helper.parents('body').length)
282 helper.appendTo((o.appendTo == 'parent' ? this.element[0].parentNode : o.appendTo));
283
284 if(helper[0] != this.element[0] && !(/(fixed|absolute)/).test(helper.css("position")))
285 helper.css("position", "absolute");
286
287 return helper;
288
289 },
290
291 _adjustOffsetFromHelper: function(obj) {
292 if (typeof obj == 'string') {
293 obj = obj.split(' ');
294 }
295 if ($.isArray(obj)) {
296 obj = {left: +obj[0], top: +obj[1] || 0};
297 }
298 if ('left' in obj) {
299 this.offset.click.left = obj.left + this.margins.left;
300 }
301 if ('right' in obj) {
302 this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
303 }
304 if ('top' in obj) {
305 this.offset.click.top = obj.top + this.margins.top;
306 }
307 if ('bottom' in obj) {
308 this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
309 }
310 },
311
312 _getParentOffset: function() {
313
314 //Get the offsetParent and cache its position
315 this.offsetParent = this.helper.offsetParent();
316 var po = this.offsetParent.offset();
317
318 // This is a special case where we need to modify a offset calculated on start, since the following happened:
319 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
320 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
321 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
322 if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) {
323 po.left += this.scrollParent.scrollLeft();
324 po.top += this.scrollParent.scrollTop();
325 }
326
327 if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information
328 || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.browser.msie)) //Ugly IE fix
329 po = { top: 0, left: 0 };
330
331 return {
332 top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
333 left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
334 };
335
336 },
337
338 _getRelativeOffset: function() {
339
340 if(this.cssPosition == "relative") {
341 var p = this.element.position();
342 return {
343 top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
344 left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
345 };
346 } else {
347 return { top: 0, left: 0 };
348 }
349
350 },
351
352 _cacheMargins: function() {
353 this.margins = {
354 left: (parseInt(this.element.css("marginLeft"),10) || 0),
355 top: (parseInt(this.element.css("marginTop"),10) || 0),
356 right: (parseInt(this.element.css("marginRight"),10) || 0),
357 bottom: (parseInt(this.element.css("marginBottom"),10) || 0)
358 };
359 },
360
361 _cacheHelperProportions: function() {
362 this.helperProportions = {
363 width: this.helper.outerWidth(),
364 height: this.helper.outerHeight()
365 };
366 },
367
368 _setContainment: function() {
369
370 var o = this.options;
371 if(o.containment == 'parent') o.containment = this.helper[0].parentNode;
372 if(o.containment == 'document' || o.containment == 'window') this.containment = [
373 o.containment == 'document' ? 0 : $(window).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
374 o.containment == 'document' ? 0 : $(window).scrollTop() - this.offset.relative.top - this.offset.parent.top,
375 (o.containment == 'document' ? 0 : $(window).scrollLeft()) + $(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left,
376 (o.containment == 'document' ? 0 : $(window).scrollTop()) + ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
377 ];
378
379 if(!(/^(document|window|parent)$/).test(o.containment) && o.containment.constructor != Array) {
380 var c = $(o.containment);
381 var ce = c[0]; if(!ce) return;
382 var co = c.offset();
383 var over = ($(ce).css("overflow") != 'hidden');
384
385 this.containment = [
386 (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0),
387 (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0),
388 (over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left - this.margins.right,
389 (over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top - this.margins.bottom
390 ];
391 this.relative_container = c;
392
393 } else if(o.containment.constructor == Array) {
394 this.containment = o.containment;
395 }
396
397 },
398
399 _convertPositionTo: function(d, pos) {
400
401 if(!pos) pos = this.position;
402 var mod = d == "absolute" ? 1 : -1;
403 var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
404
405 return {
406 top: (
407 pos.top // The absolute mouse position
408 + this.offset.relative.top * mod // Only for relative positioned nodes: Relative offset from element to offset parent
409 + this.offset.parent.top * mod // The offsetParent's offset without borders (offset + border)
410 - ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
411 ),
412 left: (
413 pos.left // The absolute mouse position
414 + this.offset.relative.left * mod // Only for relative positioned nodes: Relative offset from element to offset parent
415 + this.offset.parent.left * mod // The offsetParent's offset without borders (offset + border)
416 - ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
417 )
418 };
419
420 },
421
422 _generatePosition: function(event) {
423
424 var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
425 var pageX = event.pageX;
426 var pageY = event.pageY;
427
428 /*
429 * - Position constraining -
430 * Constrain the position to a mix of grid, containment.
431 */
432
433 if(this.originalPosition) { //If we are not dragging yet, we won't check for options
434 var containment;
435 if(this.containment) {
436 if (this.relative_container){
437 var co = this.relative_container.offset();
438 containment = [ this.containment[0] + co.left,
439 this.containment[1] + co.top,
440 this.containment[2] + co.left,
441 this.containment[3] + co.top ];
442 }
443 else {
444 containment = this.containment;
445 }
446
447 if(event.pageX - this.offset.click.left < containment[0]) pageX = containment[0] + this.offset.click.left;
448 if(event.pageY - this.offset.click.top < containment[1]) pageY = containment[1] + this.offset.click.top;
449 if(event.pageX - this.offset.click.left > containment[2]) pageX = containment[2] + this.offset.click.left;
450 if(event.pageY - this.offset.click.top > containment[3]) pageY = containment[3] + this.offset.click.top;
451 }
452
453 if(o.grid) {
454 //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950)
455 var top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY;
456 pageY = containment ? (!(top - this.offset.click.top < containment[1] || top - this.offset.click.top > containment[3]) ? top : (!(top - this.offset.click.top < containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
457
458 var left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX;
459 pageX = containment ? (!(left - this.offset.click.left < containment[0] || left - this.offset.click.left > containment[2]) ? left : (!(left - this.offset.click.left < containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
460 }
461
462 }
463
464 return {
465 top: (
466 pageY // The absolute mouse position
467 - this.offset.click.top // Click offset (relative to the element)
468 - this.offset.relative.top // Only for relative positioned nodes: Relative offset from element to offset parent
469 - this.offset.parent.top // The offsetParent's offset without borders (offset + border)
470 + ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
471 ),
472 left: (
473 pageX // The absolute mouse position
474 - this.offset.click.left // Click offset (relative to the element)
475 - this.offset.relative.left // Only for relative positioned nodes: Relative offset from element to offset parent
476 - this.offset.parent.left // The offsetParent's offset without borders (offset + border)
477 + ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
478 )
479 };
480
481 },
482
483 _clear: function() {
484 this.helper.removeClass("ui-draggable-dragging");
485 if(this.helper[0] != this.element[0] && !this.cancelHelperRemoval) this.helper.remove();
486 //if($.ui.ddmanager) $.ui.ddmanager.current = null;
487 this.helper = null;
488 this.cancelHelperRemoval = false;
489 },
490
491 // From now on bulk stuff - mainly helpers
492
493 _trigger: function(type, event, ui) {
494 ui = ui || this._uiHash();
495 $.ui.plugin.call(this, type, [event, ui]);
496 if(type == "drag") this.positionAbs = this._convertPositionTo("absolute"); //The absolute position has to be recalculated after plugins
497 return $.Widget.prototype._trigger.call(this, type, event, ui);
498 },
499
500 plugins: {},
501
502 _uiHash: function(event) {
503 return {
504 helper: this.helper,
505 position: this.position,
506 originalPosition: this.originalPosition,
507 offset: this.positionAbs
508 };
509 }
510
511 });
512
513 $.extend($.ui.draggable, {
514 version: "1.8.20"
515 });
516
517 $.ui.plugin.add("draggable", "connectToSortable", {
518 start: function(event, ui) {
519
520 var inst = $(this).data("draggable"), o = inst.options,
521 uiSortable = $.extend({}, ui, { item: inst.element });
522 inst.sortables = [];
523 $(o.connectToSortable).each(function() {
524 var sortable = $.data(this, 'sortable');
525 if (sortable && !sortable.options.disabled) {
526 inst.sortables.push({
527 instance: sortable,
528 shouldRevert: sortable.options.revert
529 });
530 sortable.refreshPositions(); // Call the sortable's refreshPositions at drag start to refresh the containerCache since the sortable container cache is used in drag and needs to be up to date (this will ensure it's initialised as well as being kept in step with any changes that might have happened on the page).
531 sortable._trigger("activate", event, uiSortable);
532 }
533 });
534
535 },
536 stop: function(event, ui) {
537
538 //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
539 var inst = $(this).data("draggable"),
540 uiSortable = $.extend({}, ui, { item: inst.element });
541
542 $.each(inst.sortables, function() {
543 if(this.instance.isOver) {
544
545 this.instance.isOver = 0;
546
547 inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
548 this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
549
550 //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: 'valid/invalid'
551 if(this.shouldRevert) this.instance.options.revert = true;
552
553 //Trigger the stop of the sortable
554 this.instance._mouseStop(event);
555
556 this.instance.options.helper = this.instance.options._helper;
557
558 //If the helper has been the original item, restore properties in the sortable
559 if(inst.options.helper == 'original')
560 this.instance.currentItem.css({ top: 'auto', left: 'auto' });
561
562 } else {
563 this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
564 this.instance._trigger("deactivate", event, uiSortable);
565 }
566
567 });
568
569 },
570 drag: function(event, ui) {
571
572 var inst = $(this).data("draggable"), self = this;
573
574 var checkPos = function(o) {
575 var dyClick = this.offset.click.top, dxClick = this.offset.click.left;
576 var helperTop = this.positionAbs.top, helperLeft = this.positionAbs.left;
577 var itemHeight = o.height, itemWidth = o.width;
578 var itemTop = o.top, itemLeft = o.left;
579
580 return $.ui.isOver(helperTop + dyClick, helperLeft + dxClick, itemTop, itemLeft, itemHeight, itemWidth);
581 };
582
583 $.each(inst.sortables, function(i) {
584
585 //Copy over some variables to allow calling the sortable's native _intersectsWith
586 this.instance.positionAbs = inst.positionAbs;
587 this.instance.helperProportions = inst.helperProportions;
588 this.instance.offset.click = inst.offset.click;
589
590 if(this.instance._intersectsWith(this.instance.containerCache)) {
591
592 //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
593 if(!this.instance.isOver) {
594
595 this.instance.isOver = 1;
596 //Now we fake the start of dragging for the sortable instance,
597 //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
598 //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)
599 this.instance.currentItem = $(self).clone().removeAttr('id').appendTo(this.instance.element).data("sortable-item", true);
600 this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
601 this.instance.options.helper = function() { return ui.helper[0]; };
602
603 event.target = this.instance.currentItem[0];
604 this.instance._mouseCapture(event, true);
605 this.instance._mouseStart(event, true, true);
606
607 //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
608 this.instance.offset.click.top = inst.offset.click.top;
609 this.instance.offset.click.left = inst.offset.click.left;
610 this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
611 this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
612
613 inst._trigger("toSortable", event);
614 inst.dropped = this.instance.element; //draggable revert needs that
615 //hack so receive/update callbacks work (mostly)
616 inst.currentItem = inst.element;
617 this.instance.fromOutside = inst;
618
619 }
620
621 //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
622 if(this.instance.currentItem) this.instance._mouseDrag(event);
623
624 } else {
625
626 //If it doesn't intersect with the sortable, and it intersected before,
627 //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
628 if(this.instance.isOver) {
629
630 this.instance.isOver = 0;
631 this.instance.cancelHelperRemoval = true;
632
633 //Prevent reverting on this forced stop
634 this.instance.options.revert = false;
635
636 // The out event needs to be triggered independently
637 this.instance._trigger('out', event, this.instance._uiHash(this.instance));
638
639 this.instance._mouseStop(event, true);
640 this.instance.options.helper = this.instance.options._helper;
641
642 //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
643 this.instance.currentItem.remove();
644 if(this.instance.placeholder) this.instance.placeholder.remove();
645
646 inst._trigger("fromSortable", event);
647 inst.dropped = false; //draggable revert needs that
648 }
649
650 };
651
652 });
653
654 }
655 });
656
657 $.ui.plugin.add("draggable", "cursor", {
658 start: function(event, ui) {
659 var t = $('body'), o = $(this).data('draggable').options;
660 if (t.css("cursor")) o._cursor = t.css("cursor");
661 t.css("cursor", o.cursor);
662 },
663 stop: function(event, ui) {
664 var o = $(this).data('draggable').options;
665 if (o._cursor) $('body').css("cursor", o._cursor);
666 }
667 });
668
669 $.ui.plugin.add("draggable", "opacity", {
670 start: function(event, ui) {
671 var t = $(ui.helper), o = $(this).data('draggable').options;
672 if(t.css("opacity")) o._opacity = t.css("opacity");
673 t.css('opacity', o.opacity);
674 },
675 stop: function(event, ui) {
676 var o = $(this).data('draggable').options;
677 if(o._opacity) $(ui.helper).css('opacity', o._opacity);
678 }
679 });
680
681 $.ui.plugin.add("draggable", "scroll", {
682 start: function(event, ui) {
683 var i = $(this).data("draggable");
684 if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') i.overflowOffset = i.scrollParent.offset();
685 },
686 drag: function(event, ui) {
687
688 var i = $(this).data("draggable"), o = i.options, scrolled = false;
689
690 if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') {
691
692 if(!o.axis || o.axis != 'x') {
693 if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
694 i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
695 else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity)
696 i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
697 }
698
699 if(!o.axis || o.axis != 'y') {
700 if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
701 i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
702 else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity)
703 i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
704 }
705
706 } else {
707
708 if(!o.axis || o.axis != 'x') {
709 if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
710 scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
711 else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
712 scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
713 }
714
715 if(!o.axis || o.axis != 'y') {
716 if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
717 scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
718 else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
719 scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
720 }
721
722 }
723
724 if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
725 $.ui.ddmanager.prepareOffsets(i, event);
726
727 }
728 });
729
730 $.ui.plugin.add("draggable", "snap", {
731 start: function(event, ui) {
732
733 var i = $(this).data("draggable"), o = i.options;
734 i.snapElements = [];
735
736 $(o.snap.constructor != String ? ( o.snap.items || ':data(draggable)' ) : o.snap).each(function() {
737 var $t = $(this); var $o = $t.offset();
738 if(this != i.element[0]) i.snapElements.push({
739 item: this,
740 width: $t.outerWidth(), height: $t.outerHeight(),
741 top: $o.top, left: $o.left
742 });
743 });
744
745 },
746 drag: function(event, ui) {
747
748 var inst = $(this).data("draggable"), o = inst.options;
749 var d = o.snapTolerance;
750
751 var x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
752 y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
753
754 for (var i = inst.snapElements.length - 1; i >= 0; i--){
755
756 var l = inst.snapElements[i].left, r = l + inst.snapElements[i].width,
757 t = inst.snapElements[i].top, b = t + inst.snapElements[i].height;
758
759 //Yes, I know, this is insane ;)
760 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))) {
761 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 })));
762 inst.snapElements[i].snapping = false;
763 continue;
764 }
765
766 if(o.snapMode != 'inner') {
767 var ts = Math.abs(t - y2) <= d;
768 var bs = Math.abs(b - y1) <= d;
769 var ls = Math.abs(l - x2) <= d;
770 var rs = Math.abs(r - x1) <= d;
771 if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
772 if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
773 if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
774 if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
775 }
776
777 var first = (ts || bs || ls || rs);
778
779 if(o.snapMode != 'outer') {
780 var ts = Math.abs(t - y1) <= d;
781 var bs = Math.abs(b - y2) <= d;
782 var ls = Math.abs(l - x1) <= d;
783 var rs = Math.abs(r - x2) <= d;
784 if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
785 if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
786 if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
787 if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
788 }
789
790 if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first))
791 (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
792 inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
793
794 };
795
796 }
797 });
798
799 $.ui.plugin.add("draggable", "stack", {
800 start: function(event, ui) {
801
802 var o = $(this).data("draggable").options;
803
804 var group = $.makeArray($(o.stack)).sort(function(a,b) {
805 return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0);
806 });
807 if (!group.length) { return; }
808
809 var min = parseInt(group[0].style.zIndex) || 0;
810 $(group).each(function(i) {
811 this.style.zIndex = min + i;
812 });
813
814 this[0].style.zIndex = min + group.length;
815
816 }
817 });
818
819 $.ui.plugin.add("draggable", "zIndex", {
820 start: function(event, ui) {
821 var t = $(ui.helper), o = $(this).data("draggable").options;
822 if(t.css("zIndex")) o._zIndex = t.css("zIndex");
823 t.css('zIndex', o.zIndex);
824 },
825 stop: function(event, ui) {
826 var o = $(this).data("draggable").options;
827 if(o._zIndex) $(ui.helper).css('zIndex', o._zIndex);
828 }
829 });
830
831 })(jQuery);