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