Bug Tracker

Changeset 5380

Show
Ignore:
Timestamp:
05/02/08 14:45:25 (8 months ago)
Author:
paul.bakaus
Message:

- completely refactored draggable (caution: this is only half-way done - the next block will come in the next two hours)

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • trunk/ui/ui.draggable.js

    r5261 r5380  
    1313 * Revision: $Id$ 
    1414 */ 
     15 
    1516;(function($) { 
    16  
    17     $.fn.extend({ 
    18         draggable: function(options) { 
    19             var args = Array.prototype.slice.call(arguments, 1); 
    20              
    21             return this.each(function() { 
    22                 if (typeof options == "string") { 
    23                     var drag = $.data(this, "draggable"); 
    24                     if(drag) drag[options].apply(drag, args); 
    25  
    26                 } else if(!$.data(this, "draggable")) 
    27                     new $.ui.draggable(this, options); 
     17     
     18    $.widget("ui", "draggable", { 
     19        init: function() { 
     20             
     21            //Initialize needed constants 
     22            var self = this, o = this.options; 
     23 
     24            //Initialize mouse events for interaction 
     25            this.element.mouseInteraction({ 
     26                executor: this, 
     27                delay: o.delay, 
     28                distance: o.distance, 
     29                dragPrevention: o.cancel, 
     30                start: this.start, 
     31                stop: this.stop, 
     32                drag: this.drag, 
     33                condition: function(e) { 
     34                    var handle = !this.options.handle || !$(this.options.handle, this.element).length ? true : false; 
     35                    if(!handle) $(this.options.handle, this.element).each(function() { if(this == e.target) handle = true; }); 
     36                    return !(e.target.className.indexOf("ui-resizable-handle") != -1 || this.options.disabled) && handle; 
     37                } 
    2838            }); 
    29         } 
    30     }); 
    31      
    32     $.ui.draggable = function(element, options) { 
    33         //Initialize needed constants 
    34         var self = this; 
     39             
     40            //Position the node 
     41            if(o.helper == 'original' && !(/(relative|absolute|fixed)/).test(this.element.css('position'))) 
     42                this.element.css('position', 'relative'); 
     43             
     44        }, 
     45        start: function(e) { 
     46 
     47            var o = this.options; 
     48            if($.ui.ddmanager) $.ui.ddmanager.current = this; 
     49             
     50            //Create and append the visible helper 
     51            this.helper = typeof o.helper == 'function' ? $(o.helper.apply(this.element[0], [e])) : (o.helper == 'clone' ? this.element.clone() : this.element); 
     52            if(!this.helper.parents('body').length) this.helper.appendTo((o.appendTo == 'parent' ? this.element[0].parentNode : o.appendTo)); 
     53 
     54            /* 
     55             * - Position generation - 
     56             * This block generates everything position related - it's the core of draggables. 
     57             */          
     58             
     59            this.cssPosition = this.helper.css("position");                                                 //Store the helper's css position 
     60            this.offset = this.element.offset();                                                            //The element's absolute position on the page 
     61            this.offset = {                                                                                 //Substract the margins from the element's absolute offset 
     62                top: this.offset.top - parseInt(this.element.css("marginTop") || 0,10), 
     63                left: this.offset.left - parseInt(this.element.css("marginLeft") || 0,10) 
     64            }; 
     65             
     66            this.offset.click = {                                                                           //Where the click happened, relative to the element 
     67                left: e.pageX - this.offset.left, 
     68                top: e.pageY - this.offset.top 
     69            }; 
     70             
     71            var p = this.element.position();                                                                //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helpers 
     72            this.offset.relative = this.cssPosition == "relative" ? { 
     73                top: p.top - parseInt(this.helper.css("top") || 0,10), 
     74                left: p.left - parseInt(this.helper.css("left") || 0,10) 
     75            } : { top: 0, left: 0 }; 
     76             
     77            this.offsetParent = this.helper.offsetParent(); var po = this.offsetParent.offset();            //Get the offsetParent and cache its position 
     78            this.offset.parent = {                                                                          //Store its position plus border 
     79                top: po.top + parseInt(this.offsetParent.css("borderTopWidth") || 0,10), 
     80                left: po.left + parseInt(this.offsetParent.css("borderLeftWidth") || 0,10) 
     81            }; 
     82             
     83            this.originalPosition = this.generatePosition(e);                                               //Generate the original position 
     84            this.helperProportions = { width: this.helper.outerWidth(), height: this.helper.outerHeight() };//Cache the helper size 
     85 
     86 
     87            /* 
     88             * - Position constraining - 
     89             * Here we prepare position constraining like grid and containment. 
     90             */  
     91             
     92            if(o.containment) { 
     93                if(o.containment == 'parent') o.containment = this.helper[0].parentNode; 
     94                if(o.containment == 'document') this.containment = [0,0,$(document).width(), ($(document).height() || document.body.parentNode.scrollHeight)]; 
     95                if(!(/(document|window|parent)/).test(o.containment)) { 
     96                    var ce = $(o.containment)[0]; 
     97                    var co = $(o.containment).offset(); 
     98 
     99                    this.containment = [ 
     100                        co.left + parseInt($(ce).css("borderLeftWidth"),10) - this.offset.relative.left - this.offset.parent.left, 
     101                        co.top + parseInt($(ce).css("borderTopWidth"),10) - this.offset.relative.top - this.offset.parent.top, 
     102                        co.left+Math.max(ce.scrollWidth,ce.offsetWidth) - parseInt($(ce).css("borderRightWidth"),10) - this.offset.relative.left - this.offset.parent.left - this.helperProportions.width - parseInt(this.element.css("marginLeft") || 0,10) - parseInt(this.element.css("marginRight") || 0,10), 
     103                        co.top+Math.max(ce.scrollHeight,ce.offsetHeight) - parseInt($(ce).css("borderBottomWidth"),10) - this.offset.relative.top - this.offset.parent.top - this.helperProportions.height - parseInt(this.element.css("marginTop") || 0,10) - parseInt(this.element.css("marginTop") || 0,10) 
     104                    ]; 
     105                } 
     106            } 
     107 
     108 
     109            //Call plugins and callbacks 
     110            this.propagate("start", e); 
     111 
     112            this.helperProportions = { width: this.helper.outerWidth(), height: this.helper.outerHeight() };//Recache the helper size 
     113            if ($.ui.ddmanager && !o.dropBehaviour) $.ui.ddmanager.prepareOffsets(this, e); 
     114 
     115            return false; 
     116 
     117        }, 
     118        generatePosition: function(e) { 
     119             
     120            var o = this.options; 
     121            var position = { 
     122                top: ( 
     123                    e.pageY                                                                 // The absolute mouse position 
     124                    - this.offset.click.top                                                 // Click offset (relative to the element) 
     125                    - this.offset.relative.top                                              // Only for relative positioned nodes: Relative offset from element to offset parent 
     126                    - this.offset.parent.top                                                // The offsetParent's offset without borders (offset + border) 
     127                    + (this.cssPosition == "fixed" ? 0 : this.offsetParent[0].scrollTop)    // The offsetParent's scroll position, not if the element is fixed 
     128                ), 
     129                left: ( 
     130                    e.pageX                                                                 // The absolute mouse position 
     131                    - this.offset.click.left                                                // Click offset (relative to the element) 
     132                    - this.offset.relative.left                                             // Only for relative positioned nodes: Relative offset from element to offset parent 
     133                    - this.offset.parent.left                                               // The offsetParent's offset without borders (offset + border) 
     134                    + (this.cssPosition == "fixed" ? 0 : this.offsetParent[0].scrollLeft)   // The offsetParent's scroll position, not if the element is fixed 
     135                ) 
     136            }; 
     137             
     138            if(!this.originalPosition) return position;                                     //If we are not dragging yet, we won't check for options 
     139             
     140             
     141            /* 
     142             * - Position constraining - 
     143             * Constrain the position to a mix of grid, containment. 
     144             */ 
     145            if(this.containment) { 
     146                if(position.left  < this.containment[0]) position.left = this.containment[0]; 
     147                if(position.top < this.containment[1]) position.top = this.containment[1]; 
     148                if(position.left > this.containment[2]) position.left = this.containment[2]; 
     149                if(position.top > this.containment[3]) position.top = this.containment[3]; 
     150            } 
     151              
     152            if(o.grid) { 
     153                var top = this.originalPosition.top + Math.round((position.top - this.originalPosition.top) / o.grid[1]) * o.grid[1]; 
     154                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; 
     155 
     156                var left = this.originalPosition.left + Math.round((position.left - this.originalPosition.left) / o.grid[0]) * o.grid[0]; 
     157                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; 
     158            } 
     159             
     160            return position; 
     161        }, 
     162        drag: function(e) { 
     163 
     164            //Compute the helpers position 
     165            this.position = this.generatePosition(e); 
     166 
     167            //Call plugins and callbacks and use the resulting position if something is returned         
     168            this.position = this.propagate("drag", e) || this.position; 
     169             
     170            if(!this.options.axis || this.options.axis == "x") this.helper[0].style.left = this.position.left+'px'; 
     171            if(!this.options.axis || this.options.axis == "y") this.helper[0].style.top = this.position.top+'px'; 
     172            if($.ui.ddmanager) $.ui.ddmanager.drag(this, e); 
     173            return false; 
     174 
     175        }, 
     176        stop: function(e) { 
     177             
     178            //If we are using droppables, inform the manager about the drop 
     179            if ($.ui.ddmanager && !this.options.dropBehaviour) 
     180                $.ui.ddmanager.drop(this, e); 
     181                 
     182            if(this.options.revert) { 
     183                var self = this; 
     184                $(this.helper).animate(this.originalPosition, parseInt(this.options.revert, 10) || 500, function() { 
     185                    self.propagate("stop", e); 
     186                    self.clear(); 
     187                }); 
     188            } else { 
     189                this.propagate("stop", e); 
     190                this.clear(); 
     191            } 
     192 
     193            return false; 
     194             
     195        }, 
     196        clear: function() { 
     197            if(this.options.helper != 'original') this.helper.remove(); 
     198            if($.ui.ddmanager) $.ui.ddmanager.current = null; 
     199            this.helper = null; 
     200        }, 
    35201         
    36         this.element = $(element); 
    37          
    38         $.data(element, "draggable", this); 
    39         this.element.addClass("ui-draggable"); 
    40          
    41         //Prepare the passed options 
    42         this.options = $.extend({}, options); 
    43         var o = this.options; 
    44         $.extend(o, { 
    45             helper: o.ghosting == true ? 'clone' : (o.helper || 'original'), 
    46             handle : o.handle ? ($(o.handle, element)[0] ? $(o.handle, element) : this.element) : this.element, 
    47             appendTo: o.appendTo || 'parent'         
    48         }); 
    49          
    50         $(element).bind("setData.draggable", function(event, key, value){ 
    51             self.options[key] = value; 
    52         }).bind("getData.draggable", function(event, key){ 
    53             return self.options[key]; 
    54         }); 
    55          
    56         //Initialize mouse events for interaction 
    57         $(o.handle).mouseInteraction({ 
    58             executor: this, 
    59             delay: o.delay, 
    60             distance: o.distance || 1, 
    61             dragPrevention: o.cancel || o.cancel === '' ? o.cancel.toLowerCase().split(',') : ['input','textarea','button','select','option'], 
    62             start: this.start, 
    63             stop: this.stop, 
    64             drag: this.drag, 
    65             condition: function(e) { return !(e.target.className.indexOf("ui-resizable-handle") != -1 || this.options.disabled); } 
    66         }); 
    67          
    68         //Position the node 
    69         if(o.helper == 'original' && (this.element.css('position') == 'static' || this.element.css('position') == '')) 
    70             this.element.css('position', 'relative'); 
    71              
    72         //Prepare cursorAt 
    73         if(o.cursorAt && o.cursorAt.constructor == Array) 
    74             o.cursorAt = { left: o.cursorAt[0], top: o.cursorAt[1] }; 
    75          
    76     }; 
    77      
    78     $.extend($.ui.draggable.prototype, { 
     202        // From now on bulk stuff - mainly helpers 
    79203        plugins: {}, 
    80204        ui: function(e) { 
     
    82206                helper: this.helper, 
    83207                position: this.position, 
    84                 absolutePosition: this.positionAbs, 
    85                 instance: this, 
    86                 options: this.options, 
    87                 element: this.element                
     208                options: this.options            
    88209            }; 
    89210        }, 
     
    93214        }, 
    94215        destroy: function() { 
    95             if(!$.data(this.element[0], 'draggable')) return; 
    96             this.options.handle.removeMouseInteraction(); 
    97             this.element 
    98                 .removeClass("ui-draggable ui-draggable-disabled") 
    99                 .removeData("draggable") 
    100                 .unbind(".draggable"); 
     216            if(!this.element.data('draggable')) return; 
     217            this.element.removeData("draggable").unbind(".draggable").removeMouseInteraction(); 
    101218        }, 
    102219        enable: function() { 
    103             this.element.removeClass("ui-draggable-disabled"); 
    104220            this.options.disabled = false; 
    105221        }, 
    106222        disable: function() { 
    107             this.element.addClass("ui-draggable-disabled"); 
    108223            this.options.disabled = true; 
    109         }, 
    110         setContrains: function(minLeft,maxLeft,minTop,maxTop) { 
    111             this.minLeft = minLeft; this.maxLeft = maxLeft; 
    112             this.minTop = minTop; this.maxTop = maxTop; 
    113             this.constrainsSet = true; 
    114         }, 
    115         checkConstrains: function() { 
    116             if(!this.constrainsSet) return; 
    117             if(this.position.left < this.minLeft) this.position.left = this.minLeft; 
    118             if(this.position.left > this.maxLeft - this.helperProportions.width) this.position.left = this.maxLeft - this.helperProportions.width; 
    119             if(this.position.top < this.minTop) this.position.top = this.minTop; 
    120             if(this.position.top > this.maxTop - this.helperProportions.height) this.position.top = this.maxTop - this.helperProportions.height; 
    121         }, 
    122         recallOffset: function(e) { 
    123  
    124             var elementPosition = { left: this.elementOffset.left - this.offsetParentOffset.left, top: this.elementOffset.top - this.offsetParentOffset.top }; 
    125             var r = this.helper.css('position') == 'relative'; var o = this.options; 
    126  
    127             //Generate the original position 
    128             this.originalPosition = { 
    129                 left: (elementPosition.left + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollLeft)), 
    130                 top: (elementPosition.top + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollTop)) 
    131             }; 
    132              
    133             //Generate a flexible offset that will later be subtracted from e.pageX/Y 
    134             this.offset = {left: this._pageX - this.originalPosition.left, top: this._pageY - this.originalPosition.top }; 
    135              
    136             //Substract margins 
    137             if(this.element[0] != this.helper[0]) { 
    138                 this.offset.left += parseInt(this.element.css('marginLeft'),10) || 0; 
    139                 this.offset.top += parseInt(this.element.css('marginTop'),10) || 0; 
    140             } 
    141                  
    142         }, 
    143         start: function(e) { 
    144             var o = this.options; 
    145             if($.ui.ddmanager) $.ui.ddmanager.current = this; 
    146              
    147             //Create and append the visible helper 
    148             this.helper = typeof o.helper == 'function' ? $(o.helper.apply(this.element[0], [e])) : (o.helper == 'clone' ? this.element.clone().appendTo((o.appendTo == 'parent' ? this.element[0].parentNode : o.appendTo)) : this.element); 
    149             if(this.helper[0] != this.element[0]) this.helper.css('position', 'absolute'); 
    150             if(!this.helper.parents('body').length) this.helper.appendTo((o.appendTo == 'parent' ? this.element[0].parentNode : o.appendTo)); 
    151              
    152              
    153             //Find out the next positioned parent 
    154             this.offsetParent = (function(cp) { 
    155                 while(cp) { 
    156                     if(cp.style && (/(absolute|relative|fixed)/).test($.css(cp,'position'))) return $(cp); 
    157                     cp = cp.parentNode ? cp.parentNode : null; 
    158                 }; return $("body");         
    159             })(this.helper[0].parentNode); 
    160              
    161             //Prepare variables for position generation 
    162             this.elementOffset = this.element.offset(); 
    163             this.offsetParentOffset = this.offsetParent.offset(); 
    164             var elementPosition = { left: this.elementOffset.left - this.offsetParentOffset.left, top: this.elementOffset.top - this.offsetParentOffset.top }; 
    165             this._pageX = e.pageX; this._pageY = e.pageY; 
    166             this.clickOffset = { left: e.pageX - this.elementOffset.left, top: e.pageY - this.elementOffset.top }; 
    167             var r = this.helper.css('position') == 'relative'; 
    168  
    169             //Generate the original position 
    170             this.originalPosition = { 
    171                 left: (r ? parseInt(this.helper.css('left'),10) || 0 : elementPosition.left + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollLeft)), 
    172                 top: (r ? parseInt(this.helper.css('top'),10) || 0 : elementPosition.top + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollTop)) 
    173             }; 
    174              
    175             //If we have a fixed element, we must subtract the scroll offset again 
    176             if(this.element.css('position') == 'fixed') { 
    177                 this.originalPosition.top -= this.offsetParent[0] == document.body ? $(document).scrollTop() : this.offsetParent[0].scrollTop; 
    178                 this.originalPosition.left -= this.offsetParent[0] == document.body ? $(document).scrollLeft() : this.offsetParent[0].scrollLeft; 
    179             } 
    180              
    181             //Generate a flexible offset that will later be subtracted from e.pageX/Y 
    182             this.offset = {left: e.pageX - this.originalPosition.left, top: e.pageY - this.originalPosition.top }; 
    183              
    184             //Substract margins 
    185             if(this.element[0] != this.helper[0]) { 
    186                 this.offset.left += parseInt(this.element.css('marginLeft'),10) || 0; 
    187                 this.offset.top += parseInt(this.element.css('marginTop'),10) || 0; 
    188             } 
    189              
    190             //Call plugins and callbacks 
    191             this.propagate("start", e); 
    192  
    193             this.helperProportions = { width: this.helper.outerWidth(), height: this.helper.outerHeight() }; 
    194             if ($.ui.ddmanager && !o.dropBehaviour) $.ui.ddmanager.prepareOffsets(this, e); 
    195              
    196             //If we have something in cursorAt, we'll use it 
    197             if(o.cursorAt) { 
    198                 if(o.cursorAt.top != undefined || o.cursorAt.bottom != undefined) { 
    199                     this.offset.top -= this.clickOffset.top - (o.cursorAt.top != undefined ? o.cursorAt.top : (this.helperProportions.height - o.cursorAt.bottom)); 
    200                     this.clickOffset.top = (o.cursorAt.top != undefined ? o.cursorAt.top : (this.helperProportions.height - o.cursorAt.bottom)); 
    201                 } 
    202                 if(o.cursorAt.left != undefined || o.cursorAt.right != undefined) { 
    203                     this.offset.left -= this.clickOffset.left - (o.cursorAt.left != undefined ? o.cursorAt.left : (this.helperProportions.width - o.cursorAt.right)); 
    204                     this.clickOffset.left = (o.cursorAt.left != undefined ? o.cursorAt.left : (this.helperProportions.width - o.cursorAt.right)); 
    205                 } 
    206             } 
    207  
    208             return false; 
    209  
    210         }, 
    211         clear: function() { 
    212             if($.ui.ddmanager) $.ui.ddmanager.current = null; 
    213             this.helper = null; 
    214         }, 
    215         stop: function(e) { 
    216  
    217             //If we are using droppables, inform the manager about the drop 
    218             if ($.ui.ddmanager && !this.options.dropBehaviour) 
    219                 $.ui.ddmanager.drop(this, e); 
    220                  
    221             //Call plugins and trigger callbacks 
    222             this.propagate("stop", e); 
    223              
    224             if(this.cancelHelperRemoval) return false;           
    225             if(this.options.helper != 'original') this.helper.remove(); 
    226             this.clear(); 
    227  
    228             return false; 
    229         }, 
    230         drag: function(e) { 
    231  
    232             //Compute the helpers position 
    233             this.position = { top: e.pageY - this.offset.top, left: e.pageX - this.offset.left }; 
    234             this.positionAbs = { left: e.pageX - this.clickOffset.left, top: e.pageY - this.clickOffset.top }; 
    235  
    236             //Call plugins and callbacks 
    237             this.checkConstrains();          
    238             this.position = this.propagate("drag", e) || this.position; 
    239             this.checkConstrains(); 
    240              
    241             $(this.helper).css({ left: this.position.left+'px', top: this.position.top+'px' }); // Stick the helper to the cursor 
    242             if($.ui.ddmanager) $.ui.ddmanager.drag(this, e); 
    243             return false; 
    244              
    245         } 
    246     }); 
    247      
    248 /* 
    249  * Draggable Extensions 
    250  */ 
    251       
     224        } 
     225    }); 
     226     
     227    $.ui.draggable.defaults = { 
     228        helper: "original", 
     229        appendTo: "parent", 
     230        cancel: ['input','textarea','button','select','option'], 
     231        distance: 1, 
     232        delay: 0 
     233    }; 
     234     
     235     
    252236    $.ui.plugin.add("draggable", "cursor", { 
    253237        start: function(e, ui) { 
     
    282266        } 
    283267    }); 
    284  
    285  
    286     $.ui.plugin.add("draggable", "revert", { 
    287         stop: function(e, ui) { 
    288             var self = ui.instance, helper = $(self.helper); 
    289             self.cancelHelperRemoval = true; 
    290              
    291             $(ui.helper).animate({ left: self.originalPosition.left, top: self.originalPosition.top }, parseInt(ui.options.revert, 10) || 500, function() { 
    292                 if(ui.options.helper != 'original') helper.remove(); 
    293                 if (!helper) self.clear(); 
     268     
     269    $.ui.plugin.add("draggable", "iframeFix", { 
     270        start: function(e, ui) { 
     271            $(ui.options.iframeFix === true ? "iframe" : ui.options.iframeFix).each(function() {                     
     272                $('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>') 
     273                .css({ 
     274                    width: this.offsetWidth+"px", height: this.offsetHeight+"px", 
     275                    position: "absolute", opacity: "0.001", zIndex: 1000 
     276                }) 
     277                .css($(this).offset()) 
     278                .appendTo("body"); 
    294279            }); 
    295         } 
    296     }); 
    297  
    298     $.ui.plugin.add("draggable", "iframeFix", { 
    299         start: function(e, ui) { 
    300  
    301             var o = ui.options; 
    302             if(ui.instance.slowMode) return; // Make clones on top of iframes (only if we are not in slowMode) 
    303              
    304             if(o.iframeFix.constructor == Array) { 
    305                 for(var i=0;i<o.iframeFix.length;i++) { 
    306                     var co = $(o.iframeFix[i]).offset({ border: false }); 
    307                     $('<div class="DragDropIframeFix"" style="background: #fff;"></div>').css("width", $(o.iframeFix[i])[0].offsetWidth+"px").css("height", $(o.iframeFix[i])[0].offsetHeight+"px").css("position", "absolute").css("opacity", "0.001").css("z-index", "1000").css("top", co.top+"px").css("left", co.left+"px").appendTo("body"); 
    308                 }        
    309             } else { 
    310                 $("iframe").each(function() {                    
    311                     var co = $(this).offset({ border: false }); 
    312                     $('<div class="DragDropIframeFix" style="background: #fff;"></div>').css("width", this.offsetWidth+"px").css("height", this.offsetHeight+"px").css("position", "absolute").css("opacity", "0.001").css("z-index", "1000").css("top", co.top+"px").css("left", co.left+"px").appendTo("body"); 
    313                 });                          
    314             } 
    315  
    316         }, 
    317         stop: function(e, ui) { 
    318             if(ui.options.iframeFix) $("div.DragDropIframeFix").each(function() { this.parentNode.removeChild(this); }); //Remove frame helpers  
    319         } 
    320     }); 
    321      
    322     $.ui.plugin.add("draggable", "containment", { 
    323         start: function(e, ui) { 
    324  
    325             var o = ui.options; 
    326             var self = ui.instance; 
    327             if((o.containment.left != undefined || o.containment.constructor == Array) && !o._containment) return; 
    328             if(!o._containment) o._containment = o.containment; 
    329  
    330             if(o._containment == 'parent') o._containment = this[0].parentNode; 
    331             if(o._containment == 'document') { 
    332                 o.containment = [ 
    333                     0, 
    334                     0, 
    335                     $(document).width(), 
    336                     ($(document).height() || document.body.parentNode.scrollHeight) 
    337                 ]; 
    338             } else { //I'm a node, so compute top/left/right/bottom 
    339  
    340                 var ce = $(o._containment)[0]; 
    341                 var co = $(o._containment).offset(); 
    342  
    343                 o.containment = [ 
    344                     co.left + parseInt($(ce).css("borderLeftWidth")), 
    345                     co.top + parseInt($(ce).css("borderTopWidth")), 
    346                     co.left+(ce.offsetWidth || ce.scrollWidth) - parseInt($(ce).css("borderRightWidth")), 
    347                     co.top+(ce.offsetHeight || ce.scrollHeight) - parseInt($(ce).css("borderBottomWidth")) 
    348                 ]; 
    349             } 
    350              
    351             var c = o.containment; 
    352             ui.instance.setContrains( 
    353                 c[0] - (self.offset.left - self.clickOffset.left), //min left 
    354                 c[2] - (self.offset.left - self.clickOffset.left), //max left 
    355                 c[1] - (self.offset.top - self.clickOffset.top), //min top 
    356                 c[3] - (self.offset.top - self.clickOffset.top) //max top 
    357             ); 
    358  
    359         } 
    360     }); 
    361  
    362     $.ui.plugin.add("draggable", "grid", { 
    363         drag: function(e, ui) { 
    364             var o = ui.options; 
    365             var newLeft = ui.instance.originalPosition.left + Math.round((e.pageX - ui.instance._pageX) / o.grid[0]) * o.grid[0]; 
    366             var newTop = ui.instance.originalPosition.top + Math.round((e.pageY - ui.instance._pageY) / o.grid[1]) * o.grid[1]; 
    367              
    368             ui.instance.position.left = newLeft; 
    369             ui.instance.position.top = newTop; 
    370  
    371         } 
    372     }); 
    373  
    374     $.ui.plugin.add("draggable", "axis", { 
    375         drag: function(e, ui) { 
    376             var o = ui.options; 
    377             if(o.constraint) o.axis = o.constraint; //Legacy check 
    378             switch (o.axis) { 
    379                 case 'x' : ui.instance.position.top = ui.instance.originalPosition.top; break; 
    380                 case 'y' : ui.instance.position.left = ui.instance.originalPosition.left; break; 
    381             } 
    382         } 
    383     }); 
    384  
    385     $.ui.plugin.add("draggable", "scroll", { 
    386         start: function(e, ui) { 
    387             var o = ui.options; 
    388             o.scrollSensitivity = o.scrollSensitivity || 20; 
    389             o.scrollSpeed       = o.scrollSpeed || 20; 
    390  
    391             ui.instance.overflowY = function(el) { 
    392                 do { if(/auto|scroll/.test(el.css('overflow')) || (/auto|scroll/).test(el.css('overflow-y'))) return el; el = el.parent(); } while (el[0].parentNode); 
    393                 return $(document); 
    394             }(this); 
    395             ui.instance.overflowX = function(el) { 
    396                 do { if(/auto|scroll/.test(el.css('overflow')) || (/auto|scroll/).test(el.css('overflow-x'))) return el; el = el.parent(); } while (el[0].parentNode); 
    397                 return $(document); 
    398             }(this); 
    399         }, 
    400         drag: function(e, ui) { 
    401              
    402             var o = ui.options; 
    403             var i = ui.instance; 
    404  
    405             if(i.overflowY[0] != document && i.overflowY[0].tagName != 'HTML') { 
    406                 if(i.overflowY[0].offsetHeight - (ui.position.top - i.overflowY[0].scrollTop + i.clickOffset.top) < o.scrollSensitivity) 
    407                     i.overflowY[0].scrollTop = i.overflowY[0].scrollTop + o.scrollSpeed; 
    408                 if((ui.position.top - i.overflowY[0].scrollTop + i.clickOffset.top) < o.scrollSensitivity) 
    409                     i.overflowY[0].scrollTop = i.overflowY[0].scrollTop - o.scrollSpeed;                 
    410             } else { 
    411                 //$(document.body).append('<p>'+(e.pageY - $(document).scrollTop())+'</p>'); 
    412                 if(e.pageY - $(document).scrollTop() < o.scrollSensitivity) 
    413                     $(document).scrollTop($(document).scrollTop() - o.scrollSpeed); 
    414                 if($(window).height() - (e.pageY - $(document).scrollTop()) < o.scrollSensitivity) 
    415                     $(document).scrollTop($(document).scrollTop() + o.scrollSpeed); 
    416             } 
    417              
    418             if(i.overflowX[0] != document && i.overflowX[0].tagName != 'HTML') { 
    419                 if(i.overflowX[0].offsetWidth - (ui.position.left - i.overflowX[0].scrollLeft + i.clickOffset.left) < o.scrollSensitivity) 
    420                     i.overflowX[0].scrollLeft = i.overflowX[0].scrollLeft + o.scrollSpeed; 
    421                 if((ui.position.top - i.overflowX[0].scrollLeft + i.clickOffset.left) < o.scrollSensitivity) 
    422                     i.overflowX[0].scrollLeft = i.overflowX[0].scrollLeft - o.scrollSpeed;               
    423             } else { 
    424                 if(e.pageX - $(document).scrollLeft() < o.scrollSensitivity) 
    425                     $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed); 
    426                 if($(window).width() - (e.pageX - $(document).scrollLeft()) < o.scrollSensitivity) 
    427                     $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed); 
    428             } 
    429              
    430             ui.instance.recallOffset(e); 
    431  
    432         } 
    433