Make WordPress Core

Changeset 8032


Ignore:
Timestamp:
06/02/2008 09:01:42 PM (18 years ago)
Author:
ryan
Message:

Don't unpublish posts when a user edit who can edit publised posts but not publih new posts edits a post. Props jeremyclarke. see #7070 for trunk

Location:
trunk
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/wp-admin/edit-form-advanced.php

    r8011 r8032  
    9292<p>
    9393<select name='post_status' id='post_status' tabindex='4'>
    94 <?php if ( current_user_can('publish_posts') ) : // Contributors only get "Unpublished" and "Pending Review" ?>
     94<?php
     95// only show the publish menu item if they are allowed to publish posts or they are allowed to edit this post (accounts for 'edit_published_posts' capability)
     96if ( current_user_can('publish_posts') OR ( $post->post_status == 'publish' AND current_user_can('edit_post', $post->ID) ) ) :
     97?>
    9598<option<?php selected( $post->post_status, 'publish' ); selected( $post->post_status, 'private' );?> value='publish'><?php _e('Published') ?></option>
    9699<?php if ( 'future' == $post->post_status ) : ?>
  • trunk/wp-admin/includes/post.php

    r8011 r8032  
    5858        $_POST['post_status'] = 'draft';
    5959
     60    $previous_status = get_post_field('post_status',  $_POST['ID']);
     61
     62    // Posts 'submitted for approval' present are submitted to $_POST the same as if they were being published.
     63    // Change status from 'publish' to 'pending' if user lacks permissions to publish or to resave published posts.
    6064    if ( 'page' == $_POST['post_type'] ) {
    6165        if ( 'publish' == $_POST['post_status'] && !current_user_can( 'publish_pages' ) )
    6266            $_POST['post_status'] = 'pending';
    6367    } else {
    64         if ( 'publish' == $_POST['post_status'] && !current_user_can( 'publish_posts' ) )
    65             $_POST['post_status'] = 'pending';
     68        if ( 'publish' == $_POST['post_status'] && !current_user_can( 'publish_posts' ) ) :
     69            // Stop attempts to publish new posts, but allow already published posts to be saved if appropriate.
     70            if ( $previous_status != 'publish' OR !current_user_can( 'edit_published_posts') )
     71                $_POST['post_status'] = 'pending';
     72        endif;
    6673    }
    6774
  • trunk/wp-includes/js/jquery/ui.core.js

    r7973 r8032  
    88 * http://docs.jquery.com/UI
    99 *
    10  * $Id: ui.core.js 5634 2008-05-19 20:53:51Z joern.zaefferer $
     10 * $Id: ui.core.js 5587 2008-05-13 19:56:42Z scott.gonzalez $
    1111 */
    1212;(function($) {
    13    
    14     $.ui = {
    15         plugin: {
    16             add: function(module, option, set) {
    17                 var proto = $.ui[module].prototype;
    18                 for(var i in set) {
    19                     proto.plugins[i] = proto.plugins[i] || [];
    20                     proto.plugins[i].push([option, set[i]]);
    21                 }
    22             },
    23             call: function(instance, name, args) {
    24                 var set = instance.plugins[name];
    25                 if(!set) { return; }
    26                
    27                 for (var i = 0; i < set.length; i++) {
    28                     if (instance.options[set[i][0]]) {
    29                         set[i][1].apply(instance.element, args);
    30                     }
    31                 }
    32             }   
    33         },
    34         cssCache: {},
    35         css: function(name) {
    36             if ($.ui.cssCache[name]) { return $.ui.cssCache[name]; }
    37             var tmp = $('<div class="ui-resizable-gen">').addClass(name).css({position:'absolute', top:'-5000px', left:'-5000px', display:'block'}).appendTo('body');
    38            
    39             //if (!$.browser.safari)
    40                 //tmp.appendTo('body');
    41            
    42             //Opera and Safari set width and height to 0px instead of auto
    43             //Safari returns rgba(0,0,0,0) when bgcolor is not set
    44             $.ui.cssCache[name] = !!(
    45                 (!(/auto|default/).test(tmp.css('cursor')) || (/^[1-9]/).test(tmp.css('height')) || (/^[1-9]/).test(tmp.css('width')) ||
    46                 !(/none/).test(tmp.css('backgroundImage')) || !(/transparent|rgba\(0, 0, 0, 0\)/).test(tmp.css('backgroundColor')))
    47             );
    48             try { $('body').get(0).removeChild(tmp.get(0)); } catch(e){}
    49             return $.ui.cssCache[name];
    50         },
    51         disableSelection: function(e) {
    52             e.unselectable = "on";
    53             e.onselectstart = function() { return false; };
    54             if (e.style) { e.style.MozUserSelect = "none"; }
    55         },
    56         enableSelection: function(e) {
    57             e.unselectable = "off";
    58             e.onselectstart = function() { return true; };
    59             if (e.style) { e.style.MozUserSelect = ""; }
    60         },
    61         hasScroll: function(e, a) {
    62             var scroll = /top/.test(a||"top") ? 'scrollTop' : 'scrollLeft', has = false;
    63             if (e[scroll] > 0) return true; e[scroll] = 1;
    64             has = e[scroll] > 0 ? true : false; e[scroll] = 0;
    65             return has;
    66         }
    67     };
    68    
    69    
    70     /** jQuery core modifications and additions **/
    71    
    72     var _remove = $.fn.remove;
    73     $.fn.remove = function() {
    74         $("*", this).add(this).trigger("remove");
    75         return _remove.apply(this, arguments );
    76     };
    77    
    78     // $.widget is a factory to create jQuery plugins
    79     // taking some boilerplate code out of the plugin code
    80     // created by Scott González and Jörn Zaefferer
    81     function getter(namespace, plugin, method) {
    82         var methods = $[namespace][plugin].getter || [];
    83         methods = (typeof methods == "string" ? methods.split(/,?\s+/) : methods);
    84         return ($.inArray(method, methods) != -1);
    85     }
    86    
    87     var widgetPrototype = {
    88         init: function() {},
    89         destroy: function() {
    90             this.element.removeData(this.widgetName);
    91         },
    92        
    93         getData: function(key) {
    94             return this.options[key];
    95         },
    96         setData: function(key, value) {
    97             this.options[key] = value;
    98         },
    99        
    100         enable: function() {
    101             this.setData('disabled', false);
    102         },
    103         disable: function() {
    104             this.setData('disabled', true);
    105         }
    106     };
    107    
    108     $.widget = function(name, prototype) {
    109         var namespace = name.split(".")[0];
    110         name = name.split(".")[1];
    111         // create plugin method
    112         $.fn[name] = function(options) {
    113             var isMethodCall = (typeof options == 'string'),
    114                 args = Array.prototype.slice.call(arguments, 1);
    115            
    116             if (isMethodCall && getter(namespace, name, options)) {
    117                 var instance = $.data(this[0], name);
    118                 return (instance ? instance[options].apply(instance, args)
    119                     : undefined);
    120             }
    121            
    122             return this.each(function() {
    123                 var instance = $.data(this, name);
    124                 if (!instance) {
    125                     $.data(this, name, new $[namespace][name](this, options));
    126                 } else if (isMethodCall) {
    127                     instance[options].apply(instance, args);
    128                 }
    129             });
    130         };
    131        
    132         // create widget constructor
    133         $[namespace][name] = function(element, options) {
    134             var self = this;
    135            
    136             this.widgetName = name;
    137            
    138             this.options = $.extend({}, $[namespace][name].defaults, options);
    139             this.element = $(element)
    140                 .bind('setData.' + name, function(e, key, value) {
    141                     return self.setData(key, value);
    142                 })
    143                 .bind('getData.' + name, function(e, key) {
    144                     return self.getData(key);
    145                 })
    146                 .bind('remove', function() {
    147                     return self.destroy();
    148                 });
    149             this.init();
    150         };
    151        
    152         // add widget prototype
    153         $[namespace][name].prototype = $.extend({}, widgetPrototype, prototype);
    154     };
    155    
    156    
    157     /** Mouse Interaction Plugin **/
    158    
    159     $.widget("ui.mouse", {
    160         init: function() {
    161             var self = this;
    162            
    163             this.element
    164                 .bind('mousedown.mouse', function() { return self.click.apply(self, arguments); })
    165                 .bind('mouseup.mouse', function() { (self.timer && clearTimeout(self.timer)); })
    166                 .bind('click.mouse', function() { if(self.initialized) { self.initialized = false; return false; } });
    167             //Prevent text selection in IE
    168             if ($.browser.msie) {
    169                 this.unselectable = this.element.attr('unselectable');
    170                 this.element.attr('unselectable', 'on');
     13
     14$.ui = {
     15    plugin: {
     16        add: function(module, option, set) {
     17            var proto = $.ui[module].prototype;
     18            for(var i in set) {
     19                proto.plugins[i] = proto.plugins[i] || [];
     20                proto.plugins[i].push([option, set[i]]);
    17121            }
    17222        },
    173         destroy: function() {
    174             this.element.unbind('.mouse').removeData("mouse");
    175             ($.browser.msie && this.element.attr('unselectable', this.unselectable));
    176         },
    177         trigger: function() { return this.click.apply(this, arguments); },
    178         click: function(e) {
    179        
    180             if(    e.which != 1 //only left click starts dragging
    181                 || $.inArray(e.target.nodeName.toLowerCase(), this.options.dragPrevention || []) != -1 // Prevent execution on defined elements
    182                 || (this.options.condition && !this.options.condition.apply(this.options.executor || this, [e, this.element])) //Prevent execution on condition
    183             ) { return true; }
    184        
    185             var self = this;
    186             this.initialized = false;
    187             var initialize = function() {
    188                 self._MP = { left: e.pageX, top: e.pageY }; // Store the click mouse position
    189                 $(document).bind('mouseup.mouse', function() { return self.stop.apply(self, arguments); });
    190                 $(document).bind('mousemove.mouse', function() { return self.drag.apply(self, arguments); });
    191        
    192                 if(!self.initalized && Math.abs(self._MP.left-e.pageX) >= self.options.distance || Math.abs(self._MP.top-e.pageY) >= self.options.distance) {
    193                     (self.options.start && self.options.start.call(self.options.executor || self, e, self.element));
    194                     (self.options.drag && self.options.drag.call(self.options.executor || self, e, this.element)); //This is actually not correct, but expected
    195                     self.initialized = true;
     23        call: function(instance, name, args) {
     24            var set = instance.plugins[name];
     25            if(!set) { return; }
     26           
     27            for (var i = 0; i < set.length; i++) {
     28                if (instance.options[set[i][0]]) {
     29                    set[i][1].apply(instance.element, args);
    19630                }
    197             };
    198 
    199             if(this.options.delay) {
    200                 if(this.timer) { clearTimeout(this.timer); }
    201                 this.timer = setTimeout(initialize, this.options.delay);
    202             } else {
    203                 initialize();
    20431            }
    205                
     32        }   
     33    },
     34    cssCache: {},
     35    css: function(name) {
     36        if ($.ui.cssCache[name]) { return $.ui.cssCache[name]; }
     37        var tmp = $('<div class="ui-resizable-gen">').addClass(name).css({position:'absolute', top:'-5000px', left:'-5000px', display:'block'}).appendTo('body');
     38       
     39        //if (!$.browser.safari)
     40            //tmp.appendTo('body');
     41       
     42        //Opera and Safari set width and height to 0px instead of auto
     43        //Safari returns rgba(0,0,0,0) when bgcolor is not set
     44        $.ui.cssCache[name] = !!(
     45            (!(/auto|default/).test(tmp.css('cursor')) || (/^[1-9]/).test(tmp.css('height')) || (/^[1-9]/).test(tmp.css('width')) ||
     46            !(/none/).test(tmp.css('backgroundImage')) || !(/transparent|rgba\(0, 0, 0, 0\)/).test(tmp.css('backgroundColor')))
     47        );
     48        try { $('body').get(0).removeChild(tmp.get(0)); } catch(e){}
     49        return $.ui.cssCache[name];
     50    },
     51    disableSelection: function(e) {
     52        e.unselectable = "on";
     53        e.onselectstart = function() { return false; };
     54        if (e.style) { e.style.MozUserSelect = "none"; }
     55    },
     56    enableSelection: function(e) {
     57        e.unselectable = "off";
     58        e.onselectstart = function() { return true; };
     59        if (e.style) { e.style.MozUserSelect = ""; }
     60    },
     61    hasScroll: function(e, a) {
     62        var scroll = /top/.test(a||"top") ? 'scrollTop' : 'scrollLeft', has = false;
     63        if (e[scroll] > 0) return true; e[scroll] = 1;
     64        has = e[scroll] > 0 ? true : false; e[scroll] = 0;
     65        return has;
     66    }
     67};
     68
     69
     70/** jQuery core modifications and additions **/
     71
     72var _remove = $.fn.remove;
     73$.fn.remove = function() {
     74    $("*", this).add(this).trigger("remove");
     75    return _remove.apply(this, arguments );
     76};
     77
     78// $.widget is a factory to create jQuery plugins
     79// taking some boilerplate code out of the plugin code
     80// created by Scott González and Jörn Zaefferer
     81function getter(namespace, plugin, method) {
     82    var methods = $[namespace][plugin].getter || [];
     83    methods = (typeof methods == "string" ? methods.split(/,?\s+/) : methods);
     84    return ($.inArray(method, methods) != -1);
     85}
     86
     87var widgetPrototype = {
     88    init: function() {},
     89    destroy: function() {
     90        this.element.removeData(this.widgetName);
     91    },
     92   
     93    getData: function(key) {
     94        return this.options[key];
     95    },
     96    setData: function(key, value) {
     97        this.options[key] = value;
     98    },
     99   
     100    enable: function() {
     101        this.setData('disabled', false);
     102    },
     103    disable: function() {
     104        this.setData('disabled', true);
     105    }
     106};
     107
     108$.widget = function(name, prototype) {
     109    var namespace = name.split(".")[0];
     110    name = name.split(".")[1];
     111    // create plugin method
     112    $.fn[name] = function(options) {
     113        var isMethodCall = (typeof options == 'string'),
     114            args = Array.prototype.slice.call(arguments, 1);
     115       
     116        if (isMethodCall && getter(namespace, name, options)) {
     117            var instance = $.data(this[0], name);
     118            return (instance ? instance[options].apply(instance, args)
     119                : undefined);
     120        }
     121       
     122        return this.each(function() {
     123            var instance = $.data(this, name);
     124            if (!instance) {
     125                $.data(this, name, new $[namespace][name](this, options));
     126            } else if (isMethodCall) {
     127                instance[options].apply(instance, args);
     128            }
     129        });
     130    };
     131   
     132    // create widget constructor
     133    $[namespace][name] = function(element, options) {
     134        var self = this;
     135       
     136        this.widgetName = name;
     137       
     138        this.options = $.extend({}, $[namespace][name].defaults, options);
     139        this.element = $(element)
     140            .bind('setData.' + name, function(e, key, value) {
     141                return self.setData(key, value);
     142            })
     143            .bind('getData.' + name, function(e, key) {
     144                return self.getData(key);
     145            })
     146            .bind('remove', function() {
     147                return self.destroy();
     148            });
     149        this.init();
     150    };
     151   
     152    // add widget prototype
     153    $[namespace][name].prototype = $.extend({}, widgetPrototype, prototype);
     154};
     155
     156
     157/** Mouse Interaction Plugin **/
     158
     159$.ui.mouse = {
     160    mouseInit: function() {
     161        var self = this;
     162   
     163        this.element.bind('mousedown.'+this.widgetName, function(e) {
     164            return self.mouseDown(e);
     165        });
     166       
     167        // Prevent text selection in IE
     168        if ($.browser.msie) {
     169            this._mouseUnselectable = this.element.attr('unselectable');
     170            this.element.attr('unselectable', 'on');
     171        }
     172       
     173        this.started = false;
     174    },
     175   
     176    // TODO: make sure destroying one instance of mouse doesn't mess with
     177    // other instances of mouse
     178    mouseDestroy: function() {
     179        this.element.unbind('.'+this.widgetName);
     180       
     181        // Restore text selection in IE
     182        ($.browser.msie
     183            && this.element.attr('unselectable', this._mouseUnselectable));
     184    },
     185   
     186    mouseDown: function(e) {
     187        // we may have missed mouseup (out of window)
     188        (this._mouseStarted && this.mouseUp(e));
     189       
     190        this._mouseDownEvent = e;
     191       
     192        var self = this,
     193            btnIsLeft = (e.which == 1),
     194            elIsCancel = ($(e.target).is(this.options.cancel));
     195        if (!btnIsLeft || elIsCancel) {
     196            return true;
     197        }
     198       
     199        this._mouseDelayMet = !this.options.delay;
     200        if (!this._mouseDelayMet) {
     201            this._mouseDelayTimer = setTimeout(function() {
     202                self._mouseDelayMet = true;
     203            }, this.options.delay);
     204        }
     205       
     206        // these delegates are required to keep context
     207        this._mouseMoveDelegate = function(e) {
     208            return self.mouseMove(e);
     209        };
     210        this._mouseUpDelegate = function(e) {
     211            return self.mouseUp(e);
     212        };
     213        $(document)
     214            .bind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
     215            .bind('mouseup.'+this.widgetName, this._mouseUpDelegate);
     216       
     217        return false;
     218    },
     219   
     220    mouseMove: function(e) {
     221        // IE mouseup check - mouseup happened when mouse was out of window
     222        if ($.browser.msie && !e.button) {
     223            return this.mouseUp(e);
     224        }
     225       
     226        if (this._mouseStarted) {
     227            this.mouseDrag(e);
    206228            return false;
    207            
    208         },
    209         stop: function(e) {
    210            
    211             if(!this.initialized) {
    212                 return $(document).unbind('mouseup.mouse').unbind('mousemove.mouse');
    213             }
    214 
    215             (this.options.stop && this.options.stop.call(this.options.executor || this, e, this.element));
    216            
    217             $(document).unbind('mouseup.mouse').unbind('mousemove.mouse');
    218             return false;
    219            
    220         },
    221         drag: function(e) {
    222 
    223             var o = this.options;
    224             if ($.browser.msie && !e.button) {
    225                 return this.stop.call(this, e); // IE mouseup check
    226             }
    227            
    228             if(!this.initialized && (Math.abs(this._MP.left-e.pageX) >= o.distance || Math.abs(this._MP.top-e.pageY) >= o.distance)) {
    229                 (o.start && o.start.call(o.executor || this, e, this.element));
    230                 this.initialized = true;
    231             } else {
    232                 if(!this.initialized) { return false; }
    233             }
    234 
    235             (o.drag && o.drag.call(this.options.executor || this, e, this.element));
    236             return false;
    237            
    238         }
    239     });
    240    
     229        }
     230       
     231        if (this.mouseDistanceMet(e) && this.mouseDelayMet(e)) {
     232            this._mouseStarted =
     233                (this.mouseStart(this._mouseDownEvent, e) !== false);
     234            (this._mouseStarted || this.mouseUp(e));
     235        }
     236       
     237        return !this._mouseStarted;
     238    },
     239   
     240    mouseUp: function(e) {
     241        $(document)
     242            .unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
     243            .unbind('mouseup.'+this.widgetName, this._mouseUpDelegate);
     244       
     245        if (this._mouseStarted) {
     246            this._mouseStarted = false;
     247            this.mouseStop(e);
     248        }
     249       
     250        return false;
     251    },
     252   
     253    mouseDistanceMet: function(e) {
     254        return (Math.max(
     255                Math.abs(this._mouseDownEvent.pageX - e.pageX),
     256                Math.abs(this._mouseDownEvent.pageY - e.pageY)
     257            ) >= this.options.distance
     258        );
     259    },
     260   
     261    mouseDelayMet: function(e) {
     262        return this._mouseDelayMet;
     263    },
     264   
     265    // These are placeholder methods, to be overriden by extending plugin
     266    mouseStart: function(e) {},
     267    mouseDrag: function(e) {},
     268    mouseStop: function(e) {}
     269};
     270
     271$.ui.mouse.defaults = {
     272    cancel: null,
     273    distance: 0,
     274    delay: 0
     275};
     276
    241277})(jQuery);
  • trunk/wp-includes/js/jquery/ui.sortable.js

    r7973 r8032  
    1414 */
    1515;(function($) {
     16
     17function contains(a, b) {
     18    var safari2 = $.browser.safari && $.browser.version < 522;
     19    if (a.contains && !safari2) {
     20        return a.contains(b);
     21    }
     22    if (a.compareDocumentPosition)
     23        return !!(a.compareDocumentPosition(b) & 16);
     24    while (b = b.parentNode)
     25          if (b == a) return true;
     26    return false;
     27};
     28
     29$.widget("ui.sortable", $.extend($.ui.mouse, {
     30    init: function() {
     31
     32        var o = this.options;
     33        this.containerCache = {};
     34        this.element.addClass("ui-sortable");
    1635   
    17     function contains(a, b) {
    18         var safari2 = $.browser.safari && $.browser.version < 522;
    19         if (a.contains && !safari2) {
    20             return a.contains(b);
    21         }
    22         if (a.compareDocumentPosition)
    23             return !!(a.compareDocumentPosition(b) & 16);
    24         while (b = b.parentNode)
    25               if (b == a) return true;
    26         return false;
    27     };
    28    
    29     $.widget("ui.sortable", {
    30         init: function() {
    31 
    32             var o = this.options;
    33             this.containerCache = {};
    34             this.element.addClass("ui-sortable");
    35        
    36             //Get the items
    37             this.refresh();
    38    
    39             //Let's determine if the items are floating
    40             this.floating = this.items.length ? (/left|right/).test(this.items[0].item.css('float')) : false;
     36        //Get the items
     37        this.refresh();
     38
     39        //Let's determine if the items are floating
     40        this.floating = this.items.length ? (/left|right/).test(this.items[0].item.css('float')) : false;
     41       
     42        //Let's determine the parent's offset
     43        if(!(/(relative|absolute|fixed)/).test(this.element.css('position'))) this.element.css('position', 'relative');
     44        this.offset = this.element.offset();
     45
     46        //Initialize mouse events for interaction
     47        this.mouseInit();
     48       
     49    },
     50    plugins: {},
     51    ui: function(inst) {
     52        return {
     53            helper: (inst || this)["helper"],
     54            placeholder: (inst || this)["placeholder"] || $([]),
     55            position: (inst || this)["position"],
     56            absolutePosition: (inst || this)["positionAbs"],
     57            options: this.options,
     58            element: this.element,
     59            item: (inst || this)["currentItem"],
     60            sender: inst ? inst.element : null
     61        };     
     62    },
     63    propagate: function(n,e,inst, noPropagation) {
     64        $.ui.plugin.call(this, n, [e, this.ui(inst)]);
     65        if(!noPropagation) this.element.triggerHandler(n == "sort" ? n : "sort"+n, [e, this.ui(inst)], this.options[n]);
     66    },
     67    serialize: function(o) {
     68       
     69       
     70       
     71        var items = ($.isFunction(this.options.items) ? this.options.items.call(this.element) : $(this.options.items, this.element)).not('.ui-sortable-helper'); //Only the items of the sortable itself
     72        var str = []; o = o || {};
     73       
     74        items.each(function() {
     75            var res = ($(this).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
     76            if(res) str.push((o.key || res[1])+'[]='+(o.key ? res[1] : res[2]));
     77        });
     78       
     79        return str.join('&');
     80       
     81    },
     82    toArray: function(attr) {
     83        var items = ($.isFunction(this.options.items) ? this.options.items.call(this.element) : $(this.options.items, this.element)).not('.ui-sortable-helper'); //Only the items of the sortable itself
     84        var ret = [];
     85
     86        items.each(function() { ret.push($(this).attr(attr || 'id')); });
     87        return ret;
     88    },
     89    enable: function() {
     90        this.element.removeClass("ui-sortable-disabled");
     91        this.options.disabled = false;
     92    },
     93    disable: function() {
     94        this.element.addClass("ui-sortable-disabled");
     95        this.options.disabled = true;
     96    },
     97    /* Be careful with the following core functions */
     98    intersectsWith: function(item) {
     99       
     100        var x1 = this.positionAbs.left, x2 = x1 + this.helperProportions.width,
     101        y1 = this.positionAbs.top, y2 = y1 + this.helperProportions.height;
     102        var l = item.left, r = l + item.width,
     103        t = item.top, b = t + item.height;
     104
     105        if(this.options.tolerance == "pointer") {
     106            return (y1 + this.offset.click.top > t && y1 + this.offset.click.top < b && x1 + this.offset.click.left > l && x1 + this.offset.click.left < r);
     107        } else {
     108       
     109            return (l < x1 + (this.helperProportions.width / 2) // Right Half
     110                && x2 - (this.helperProportions.width / 2) < r // Left Half
     111                && t < y1 + (this.helperProportions.height / 2) // Bottom Half
     112                && y2 - (this.helperProportions.height / 2) < b ); // Top Half
     113       
     114        }
     115       
     116    },
     117    intersectsWithEdge: function(item) {   
     118        var x1 = this.positionAbs.left, x2 = x1 + this.helperProportions.width,
     119            y1 = this.positionAbs.top, y2 = y1 + this.helperProportions.height;
     120        var l = item.left, r = l + item.width,
     121            t = item.top, b = t + item.height;
     122
     123        if(this.options.tolerance == "pointer" || (this.options.tolerance == "guess" && this.currentItem[0]['offset'+(this.floating ? 'Width' : 'Height')] > item.item[0]['offset'+(this.floating ? 'Width' : 'Height')])) {
     124
     125            if(!(y1 + this.offset.click.top > t && y1 + this.offset.click.top < b && x1 + this.offset.click.left > l && x1 + this.offset.click.left < r)) return false;
    41126           
    42             //Let's determine the parent's offset
    43             if(!(/(relative|absolute|fixed)/).test(this.element.css('position'))) this.element.css('position', 'relative');
    44             this.offset = this.element.offset();
    45    
    46             //Initialize mouse events for interaction
    47             this.element.mouse({
    48                 executor: this,
    49                 delay: o.delay,
    50                 distance: o.distance || 1,
    51                 dragPrevention: o.prevention ? o.prevention.toLowerCase().split(',') : ['input','textarea','button','select','option'],
    52                 start: this.start,
    53                 stop: this.stop,
    54                 drag: this.drag,
    55                 condition: function(e) {
    56    
    57                     if(this.options.disabled || this.options.type == 'static') return false;
    58    
    59                     //Find out if the clicked node (or one of its parents) is a actual item in this.items
    60                     var currentItem = null, nodes = $(e.target).parents().each(function() {
    61                         if($.data(this, 'sortable-item')) {
    62                             currentItem = $(this);
    63                             return false;
    64                         }
    65                     });
    66                     if($.data(e.target, 'sortable-item')) currentItem = $(e.target);
    67                    
    68                     if(!currentItem) return false; 
    69                     if(this.options.handle) {
    70                         var validHandle = false;
    71                         $(this.options.handle, currentItem).each(function() { if(this == e.target) validHandle = true; });
    72                         if(!validHandle) return false;
    73                     }
    74                        
    75                     this.currentItem = currentItem;
    76                     return true;
    77    
    78                 }
    79             });
     127            if(this.floating) {
     128                if(x1 + this.offset.click.left > l && x1 + this.offset.click.left < l + item.width/2) return 2;
     129                if(x1 + this.offset.click.left > l+item.width/2 && x1 + this.offset.click.left < r) return 1;
     130            } else {
     131                if(y1 + this.offset.click.top > t && y1 + this.offset.click.top < t + item.height/2) return 2;
     132                if(y1 + this.offset.click.top > t+item.height/2 && y1 + this.offset.click.top < b) return 1;
     133            }
     134
     135        } else {
     136       
     137            if (!(l < x1 + (this.helperProportions.width / 2) // Right Half
     138                && x2 - (this.helperProportions.width / 2) < r // Left Half
     139                && t < y1 + (this.helperProportions.height / 2) // Bottom Half
     140                && y2 - (this.helperProportions.height / 2) < b )) return false; // Top Half
    80141           
    81         },
    82         plugins: {},
    83         ui: function(inst) {
    84             return {
    85                 helper: (inst || this)["helper"],
    86                 placeholder: (inst || this)["placeholder"] || $([]),
    87                 position: (inst || this)["position"].current,
    88                 absolutePosition: (inst || this)["position"].absolute,
    89                 instance: this,
    90                 options: this.options,
    91                 element: this.element,
    92                 item: (inst || this)["currentItem"],
    93                 sender: inst ? inst.element : null
    94             };     
    95         },
    96         propagate: function(n,e,inst) {
    97             $.ui.plugin.call(this, n, [e, this.ui(inst)]);
    98             this.element.triggerHandler(n == "sort" ? n : "sort"+n, [e, this.ui(inst)], this.options[n]);
    99         },
    100         serialize: function(o) {
    101            
    102             var items = $(this.options.items, this.element).not('.ui-sortable-helper'); //Only the items of the sortable itself
    103             var str = []; o = o || {};
    104            
    105             items.each(function() {
    106                 var res = ($(this).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
    107                 if(res) str.push((o.key || res[1])+'[]='+(o.key ? res[1] : res[2]));
    108             });
    109            
    110             return str.join('&');
    111            
    112         },
    113         toArray: function(attr) {
    114             var items = $(this.options.items, this.element).not('.ui-sortable-helper'); //Only the items of the sortable itself
    115             var ret = [];
    116 
    117             items.each(function() { ret.push($(this).attr(attr || 'id')); });
    118             return ret;
    119         },
    120         enable: function() {
    121             this.element.removeClass("ui-sortable-disabled");
    122             this.options.disabled = false;
    123         },
    124         disable: function() {
    125             this.element.addClass("ui-sortable-disabled");
    126             this.options.disabled = true;
    127         },
    128         /* Be careful with the following core functions */
    129         intersectsWith: function(item) {
    130            
    131             var x1 = this.position.absolute.left, x2 = x1 + this.helperProportions.width,
    132             y1 = this.position.absolute.top, y2 = y1 + this.helperProportions.height;
    133             var l = item.left, r = l + item.width,
    134             t = item.top, b = t + item.height;
    135 
    136             if(this.options.tolerance == "pointer") {
    137                 return (y1 + this.clickOffset.top > t && y1 + this.clickOffset.top < b && x1 + this.clickOffset.left > l && x1 + this.clickOffset.left < r);
     142            if(this.floating) {
     143                if(x2 > l && x1 < l) return 2; //Crosses left edge
     144                if(x1 < r && x2 > r) return 1; //Crosses right edge
    138145            } else {
    139            
    140                 return (l < x1 + (this.helperProportions.width / 2) // Right Half
    141                     && x2 - (this.helperProportions.width / 2) < r // Left Half
    142                     && t < y1 + (this.helperProportions.height / 2) // Bottom Half
    143                     && y2 - (this.helperProportions.height / 2) < b ); // Top Half
    144            
     146                if(y2 > t && y1 < t) return 1; //Crosses top edge
     147                if(y1 < b && y2 > b) return 2; //Crosses bottom edge
    145148            }
    146            
    147         },
    148         intersectsWithEdge: function(item) {   
    149             var x1 = this.position.absolute.left, x2 = x1 + this.helperProportions.width,
    150                 y1 = this.position.absolute.top, y2 = y1 + this.helperProportions.height;
    151             var l = item.left, r = l + item.width,
    152                 t = item.top, b = t + item.height;
    153 
    154             if(this.options.tolerance == "pointer") {
    155 
    156                 if(!(y1 + this.clickOffset.top > t && y1 + this.clickOffset.top < b && x1 + this.clickOffset.left > l && x1 + this.clickOffset.left < r)) return false;
    157                
    158                 if(this.floating) {
    159                     if(x1 + this.clickOffset.left > l && x1 + this.clickOffset.left < l + item.width/2) return 2;
    160                     if(x1 + this.clickOffset.left > l+item.width/2 && x1 + this.clickOffset.left < r) return 1;
    161                 } else {
    162                     if(y1 + this.clickOffset.top > t && y1 + this.clickOffset.top < t + item.height/2) return 2;
    163                     if(y1 + this.clickOffset.top > t+item.height/2 && y1 + this.clickOffset.top < b) return 1;
    164                 }
    165 
    166             } else {
    167            
    168                 if (!(l < x1 + (this.helperProportions.width / 2) // Right Half
    169                     && x2 - (this.helperProportions.width / 2) < r // Left Half
    170                     && t < y1 + (this.helperProportions.height / 2) // Bottom Half
    171                     && y2 - (this.helperProportions.height / 2) < b )) return false; // Top Half
    172                
    173                 if(this.floating) {
    174                     if(x2 > l && x1 < l) return 2; //Crosses left edge
    175                     if(x1 < r && x2 > r) return 1; //Crosses right edge
    176                 } else {
    177                     if(y2 > t && y1 < t) return 1; //Crosses top edge
    178                     if(y1 < b && y2 > b) return 2; //Crosses bottom edge
    179                 }
    180            
    181             }
    182            
    183             return false;
    184            
    185         },
    186         //This method checks approximately if the item is dragged in a container, but doesn't touch any items
    187         inEmptyZone: function(container) {
    188 
    189             if(!$(container.options.items, container.element).length) {
    190                 return container.options.dropOnEmpty ? true : false;
    191             };
    192 
    193             var last = $(container.options.items, container.element).not('.ui-sortable-helper'); last = $(last[last.length-1]);
    194             var top = last.offset()[this.floating ? 'left' : 'top'] + last[0][this.floating ? 'offsetWidth' : 'offsetHeight'];
    195             return (this.position.absolute[this.floating ? 'left' : 'top'] > top);
    196         },
    197         refresh: function() {
    198             this.refreshItems();
    199             this.refreshPositions();
    200         },
    201         refreshItems: function() {
    202            
    203             this.items = [];
    204             this.containers = [this];
    205             var items = this.items;
    206             var queries = [$(this.options.items, this.element)];
    207            
    208             if(this.options.connectWith) {
    209                 for (var i = this.options.connectWith.length - 1; i >= 0; i--){
    210                     var cur = $(this.options.connectWith[i]);
    211                     for (var j = cur.length - 1; j >= 0; j--){
    212                         var inst = $.data(cur[j], 'sortable');
    213                         if(inst && !inst.options.disabled) {
    214                             queries.push($(inst.options.items, inst.element));
    215                             this.containers.push(inst);
    216                         }
    217                     };
    218                 };
    219             }
    220 
    221             for (var i = queries.length - 1; i >= 0; i--){
    222                 queries[i].each(function() {
    223                     $.data(this, 'sortable-item', true); // Data for target checking (mouse manager)
    224                     items.push({
    225                         item: $(this),
    226                         width: 0, height: 0,
    227                         left: 0, top: 0
    228                     });
    229                 });
    230             };
    231 
    232         },
    233         refreshPositions: function(fast) {
    234             for (var i = this.items.length - 1; i >= 0; i--){
    235                 var t = this.items[i].item;
    236                 if(!fast) this.items[i].width = (this.options.toleranceElement ? $(this.options.toleranceElement, t) : t).outerWidth();
    237                 if(!fast) this.items[i].height = (this.options.toleranceElement ? $(this.options.toleranceElement, t) : t).outerHeight();
    238                 var p = (this.options.toleranceElement ? $(this.options.toleranceElement, t) : t).offset();
    239                 this.items[i].left = p.left;
    240                 this.items[i].top = p.top;
    241             };
    242             for (var i = this.containers.length - 1; i >= 0; i--){
    243                 var p =this.containers[i].element.offset();
    244                 this.containers[i].containerCache.left = p.left;
    245                 this.containers[i].containerCache.top = p.top;
    246                 this.containers[i].containerCache.width = this.containers[i].element.outerWidth();
    247                 this.containers[i].containerCache.height = this.containers[i].element.outerHeight();
    248             };
    249         },
    250         destroy: function() {
    251             this.element
    252                 .removeClass("ui-sortable ui-sortable-disabled")
    253                 .removeData("sortable")
    254                 .unbind(".sortable")
    255                 .mouse("destroy");
    256            
    257             for ( var i = this.items.length - 1; i >= 0; i-- )
    258                 this.items[i].item.removeData("sortable-item");
    259         },
    260         createPlaceholder: function(that) {
    261             (that || this).placeholderElement = this.options.placeholderElement ? $(this.options.placeholderElement, (that || this).currentItem) : (that || this).currentItem;
    262             (that || this).placeholder = $('<div></div>')
    263                 .addClass(this.options.placeholder)
    264                 .appendTo('body')
    265                 .css({ position: 'absolute' })
    266                 .css((that || this).placeholderElement.offset())
    267                 .css({ width: (that || this).placeholderElement.outerWidth(), height: (that || this).placeholderElement.outerHeight() })
    268                 ;
    269         },
    270         contactContainers: function(e) {
    271             for (var i = this.containers.length - 1; i >= 0; i--){
    272 
    273                 if(this.intersectsWith(this.containers[i].containerCache)) {
    274                     if(!this.containers[i].containerCache.over) {
    275                        
    276 
    277                         if(this.currentContainer != this.containers[i]) {
    278                            
    279                             //When entering a new container, we will find the item with the least distance and append our item near it
    280                             var dist = 10000; var itemWithLeastDistance = null; var base = this.position.absolute[this.containers[i].floating ? 'left' : 'top'];
    281                             for (var j = this.items.length - 1; j >= 0; j--) {
    282                                 if(!contains(this.containers[i].element[0], this.items[j].item[0])) continue;
    283                                 var cur = this.items[j][this.containers[i].floating ? 'left' : 'top'];
    284                                 if(Math.abs(cur - base) < dist) {
    285                                     dist = Math.abs(cur - base); itemWithLeastDistance = this.items[j];
    286                                 }
    287                             }
    288                            
    289                             //We also need to exchange the placeholder
    290                             if(this.placeholder) this.placeholder.remove();
    291                             if(this.containers[i].options.placeholder) {
    292                                 this.containers[i].createPlaceholder(this);
    293                             } else {
    294                                 this.placeholder = null; this.placeholderElement = null;
    295                             }
    296                            
    297                            
    298                             itemWithLeastDistance ? this.rearrange(e, itemWithLeastDistance) : this.rearrange(e, null, this.containers[i].element);
    299                             this.propagate("change", e); //Call plugins and callbacks
    300                             this.containers[i].propagate("change", e, this); //Call plugins and callbacks
    301                             this.currentContainer = this.containers[i];
    302 
    303                         }
    304                        
    305                         this.containers[i].propagate("over", e, this);
    306                         this.containers[i].containerCache.over = 1;
    307                     }
    308                 } else {
    309                     if(this.containers[i].containerCache.over) {
    310                         this.containers[i].propagate("out", e, this);
    311                         this.containers[i].containerCache.over = 0;
    312                     }
    313                 }
    314                
    315             };         
    316         },
    317         start: function(e,el) {
    318        
    319             var o = this.options;
    320             this.currentContainer = this;
    321             this.refresh();
    322 
    323             //Create and append the visible helper
    324             this.helper = typeof o.helper == 'function' ? $(o.helper.apply(this.element[0], [e, this.currentItem])) : this.currentItem.clone();
    325             if(!this.helper.parents('body').length) this.helper.appendTo(o.appendTo || this.currentItem[0].parentNode); //Add the helper to the DOM if that didn't happen already
    326             this.helper.css({ position: 'absolute', clear: 'both' }).addClass('ui-sortable-helper'); //Position it absolutely and add a helper class
    327            
    328             //Prepare variables for position generation
    329             $.extend(this, {
    330                 offsetParent: this.helper.offsetParent(),
    331                 offsets: {
    332                     absolute: this.currentItem.offset()
    333                 },
    334                 mouse: {
    335                     start: { top: e.pageY, left: e.pageX }
    336                 },
    337                 margins: {
    338                     top: parseInt(this.currentItem.css("marginTop")) || 0,
    339                     left: parseInt(this.currentItem.css("marginLeft")) || 0
    340                 }
    341             });
    342            
    343             //The relative click offset
    344             this.offsets.parent = this.offsetParent.offset();
    345             this.clickOffset = { left: e.pageX - this.offsets.absolute.left, top: e.pageY - this.offsets.absolute.top };
    346            
    347             this.originalPosition = {
    348                 left: this.offsets.absolute.left - this.offsets.parent.left - this.margins.left,
    349                 top: this.offsets.absolute.top - this.offsets.parent.top - this.margins.top
    350             }
    351            
    352             //Generate a flexible offset that will later be subtracted from e.pageX/Y
    353             //I hate margins - they need to be removed before positioning the element absolutely..
    354             this.offset = {
    355                 left: e.pageX - this.originalPosition.left,
    356                 top: e.pageY - this.originalPosition.top
    357             };
    358 
    359             //Save the first time position
    360             $.extend(this, {
    361                 position: {
    362                     current: { top: e.pageY - this.offset.top, left: e.pageX - this.offset.left },
    363                     absolute: { left: e.pageX - this.clickOffset.left, top: e.pageY - this.clickOffset.top },
    364                     dom: this.currentItem.prev()[0]
    365                 }
    366             });
    367 
    368             //If o.placeholder is used, create a new element at the given position with the class
    369             if(o.placeholder) this.createPlaceholder();
    370 
    371             this.propagate("start", e); //Call plugins and callbacks
    372             this.helperProportions = { width: this.helper.outerWidth(), height: this.helper.outerHeight() }; //Save and store the helper proportions
    373 
    374             //If we have something in cursorAt, we'll use it
    375             if(o.cursorAt) {
    376                 if(o.cursorAt.top != undefined || o.cursorAt.bottom != undefined) {
    377                     this.offset.top -= this.clickOffset.top - (o.cursorAt.top != undefined ? o.cursorAt.top : (this.helperProportions.height - o.cursorAt.bottom));
    378                     this.clickOffset.top = (o.cursorAt.top != undefined ? o.cursorAt.top : (this.helperProportions.height - o.cursorAt.bottom));
    379                 }
    380                 if(o.cursorAt.left != undefined || o.cursorAt.right != undefined) {
    381                     this.offset.left -= this.clickOffset.left - (o.cursorAt.left != undefined ? o.cursorAt.left : (this.helperProportions.width - o.cursorAt.right));
    382                     this.clickOffset.left = (o.cursorAt.left != undefined ? o.cursorAt.left : (this.helperProportions.width - o.cursorAt.right));
    383                 }
    384             }
    385 
    386             if(this.options.placeholder != 'clone') $(this.currentItem).css('visibility', 'hidden'); //Set the original element visibility to hidden to still fill out the white space
    387             for (var i = this.containers.length - 1; i >= 0; i--) { this.containers[i].propagate("activate", e, this); } //Post 'activate' events to possible containers
    388            
    389             //Prepare possible droppables
    390             if($.ui.ddmanager) $.ui.ddmanager.current = this;
    391             if ($.ui.ddmanager && !o.dropBehaviour) $.ui.ddmanager.prepareOffsets(this, e);
    392 
    393             this.dragging = true;
    394             return false;
    395            
    396         },
    397         stop: function(e) {
    398 
    399             this.propagate("stop", e); //Call plugins and trigger callbacks
    400             if(this.position.dom != this.currentItem.prev()[0]) this.propagate("update", e); //Trigger update callback if the DOM position has changed
    401             if(!contains(this.element[0], this.currentItem[0])) { //Node was moved out of the current element
    402                 this.propagate("remove", e);
    403                 for (var i = this.containers.length - 1; i >= 0; i--){
    404                     if(contains(this.containers[i].element[0], this.currentItem[0])) {
    405                         this.containers[i].propagate("update", e, this);
    406                         this.containers[i].propagate("receive", e, this);
     149       
     150        }
     151       
     152        return false;
     153       
     154    },
     155    refresh: function() {
     156        this.refreshItems();
     157        this.refreshPositions();
     158    },
     159    refreshItems: function() {
     160       
     161        this.items = [];
     162        this.containers = [this];
     163        var items = this.items;
     164        var queries = [$.isFunction(this.options.items) ? this.options.items.call(this.element) : $(this.options.items, this.element)];
     165       
     166        if(this.options.connectWith) {
     167            for (var i = this.options.connectWith.length - 1; i >= 0; i--){
     168                var cur = $(this.options.connectWith[i]);
     169                for (var j = cur.length - 1; j >= 0; j--){
     170                    var inst = $.data(cur[j], 'sortable');
     171                    if(inst && !inst.options.disabled) {
     172                        queries.push($.isFunction(inst.options.items) ? inst.options.items.call(inst.element) : $(inst.options.items, inst.element));
     173                        this.containers.push(inst);
    407174                    }
    408175                };
    409176            };
    410            
    411             //Post events to containers
    412             for (var i = this.containers.length - 1; i >= 0; i--){
    413                 this.containers[i].propagate("deactivate", e, this);
     177        }
     178
     179        for (var i = queries.length - 1; i >= 0; i--){
     180            queries[i].each(function() {
     181                $.data(this, 'sortable-item', true); // Data for target checking (mouse manager)
     182                items.push({
     183                    item: $(this),
     184                    width: 0, height: 0,
     185                    left: 0, top: 0
     186                });
     187            });
     188        };
     189
     190    },
     191    refreshPositions: function(fast) {
     192        for (var i = this.items.length - 1; i >= 0; i--){
     193            var t = this.items[i].item;
     194            if(!fast) this.items[i].width = (this.options.toleranceElement ? $(this.options.toleranceElement, t) : t).outerWidth();
     195            if(!fast) this.items[i].height = (this.options.toleranceElement ? $(this.options.toleranceElement, t) : t).outerHeight();
     196            var p = (this.options.toleranceElement ? $(this.options.toleranceElement, t) : t).offset();
     197            this.items[i].left = p.left;
     198            this.items[i].top = p.top;
     199        };
     200        for (var i = this.containers.length - 1; i >= 0; i--){
     201            var p =this.containers[i].element.offset();
     202            this.containers[i].containerCache.left = p.left;
     203            this.containers[i].containerCache.top = p.top;
     204            this.containers[i].containerCache.width = this.containers[i].element.outerWidth();
     205            this.containers[i].containerCache.height = this.containers[i].element.outerHeight();
     206        };
     207    },
     208    destroy: function() {
     209        this.element
     210            .removeClass("ui-sortable ui-sortable-disabled")
     211            .removeData("sortable")
     212            .unbind(".sortable");
     213        this.mouseDestroy();
     214       
     215        for ( var i = this.items.length - 1; i >= 0; i-- )
     216            this.items[i].item.removeData("sortable-item");
     217    },
     218    createPlaceholder: function(that) {
     219       
     220        var self = that || this, o = self.options;
     221
     222        if(o.placeholder.constructor == String) {
     223            var className = o.placeholder;
     224            o.placeholder = {
     225                element: function() {
     226                    return $('<div></div>').addClass(className)[0];
     227                },
     228                update: function(i, p) {
     229                    p.css(i.offset()).css({ width: i.outerWidth(), height: i.outerHeight() });
     230                }
     231            };
     232        }
     233       
     234        self.placeholder = $(o.placeholder.element.call(self.element, self.currentItem)).appendTo('body').css({ position: 'absolute' });
     235        o.placeholder.update.call(self.element, self.currentItem, self.placeholder);
     236    },
     237    contactContainers: function(e) {
     238        for (var i = this.containers.length - 1; i >= 0; i--){
     239
     240            if(this.intersectsWith(this.containers[i].containerCache)) {
     241                if(!this.containers[i].containerCache.over) {
     242                   
     243
     244                    if(this.currentContainer != this.containers[i]) {
     245                       
     246                        //When entering a new container, we will find the item with the least distance and append our item near it
     247                        var dist = 10000; var itemWithLeastDistance = null; var base = this.positionAbs[this.containers[i].floating ? 'left' : 'top'];
     248                        for (var j = this.items.length - 1; j >= 0; j--) {
     249                            if(!contains(this.containers[i].element[0], this.items[j].item[0])) continue;
     250                            var cur = this.items[j][this.containers[i].floating ? 'left' : 'top'];
     251                            if(Math.abs(cur - base) < dist) {
     252                                dist = Math.abs(cur - base); itemWithLeastDistance = this.items[j];
     253                            }
     254                        }
     255                       
     256                        if(!itemWithLeastDistance && !this.options.dropOnEmpty) //Check if dropOnEmpty is enabled
     257                            continue;
     258                       
     259                        //We also need to exchange the placeholder
     260                        if(this.placeholder) this.placeholder.remove();
     261                        if(this.containers[i].options.placeholder) {
     262                            this.containers[i].createPlaceholder(this);
     263                        } else {
     264                            this.placeholder = null;;
     265                        }
     266                       
     267                       
     268                        itemWithLeastDistance ? this.rearrange(e, itemWithLeastDistance) : this.rearrange(e, null, this.containers[i].element);
     269                        this.propagate("change", e); //Call plugins and callbacks
     270                        this.containers[i].propagate("change", e, this); //Call plugins and callbacks
     271                        this.currentContainer = this.containers[i];
     272
     273                    }
     274                   
     275                    this.containers[i].propagate("over", e, this);
     276                    this.containers[i].containerCache.over = 1;
     277                }
     278            } else {
    414279                if(this.containers[i].containerCache.over) {
    415280                    this.containers[i].propagate("out", e, this);
     
    418283            }
    419284           
    420             //If we are using droppables, inform the manager about the drop
    421             if ($.ui.ddmanager && !this.options.dropBehaviour) $.ui.ddmanager.drop(this, e);
     285        };         
     286    },
     287    mouseStart: function(e, overrideHandle, noActivation) {
     288   
     289        var o = this.options;
     290        this.currentContainer = this;
     291       
     292        if(this.options.disabled || this.options.type == 'static') return false;
     293
     294        //Find out if the clicked node (or one of its parents) is a actual item in this.items
     295        var currentItem = null, nodes = $(e.target).parents().each(function() {
     296            if($.data(this, 'sortable-item')) {
     297                currentItem = $(this);
     298                return false;
     299            }
     300        });
     301        if($.data(e.target, 'sortable-item')) currentItem = $(e.target);
     302   
     303        if(!currentItem) return false;
     304        if(this.options.handle && !overrideHandle) {
     305            var validHandle = false;
     306            $(this.options.handle, currentItem).each(function() { if(this == e.target) validHandle = true; });
     307            if(!validHandle) return false;
     308        }
    422309           
    423             this.dragging = false;
    424             if(this.cancelHelperRemoval) return false;
    425             $(this.currentItem).css('visibility', '');
    426             if(this.placeholder) this.placeholder.remove();
    427             this.helper.remove();
    428 
    429             return false;
     310        this.currentItem = currentItem;
     311
     312        this.refresh();
     313
     314        //Create and append the visible helper         
     315        this.helper = typeof o.helper == 'function' ? $(o.helper.apply(this.element[0], [e, this.currentItem])) : this.currentItem.clone();
     316        if(!this.helper.parents('body').length) this.helper.appendTo((o.appendTo != 'parent' ? o.appendTo : this.currentItem[0].parentNode)); //Add the helper to the DOM if that didn't happen already
     317        this.helper.css({ position: 'absolute', clear: 'both' }).addClass('ui-sortable-helper'); //Position it absolutely and add a helper class
     318
     319        /*
     320         * - Position generation -
     321         * This block generates everything position related - it's the core of draggables.
     322         */
     323
     324        this.margins = {                                                                                //Cache the margins
     325            left: (parseInt(this.currentItem.css("marginLeft"),10) || 0),
     326            top: (parseInt(this.currentItem.css("marginTop"),10) || 0)
     327        };     
     328   
     329        this.offset = this.currentItem.offset();                                                        //The element's absolute position on the page
     330        this.offset = {                                                                                 //Substract the margins from the element's absolute offset
     331            top: this.offset.top - this.margins.top,
     332            left: this.offset.left - this.margins.left
     333        };
     334       
     335        this.offset.click = {                                                                           //Where the click happened, relative to the element
     336            left: e.pageX - this.offset.left,
     337            top: e.pageY - this.offset.top
     338        };
     339       
     340        this.offsetParent = this.helper.offsetParent(); var po = this.offsetParent.offset();            //Get the offsetParent and cache its position
     341
     342        this.offset.parent = {                                                                          //Store its position plus border
     343            top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
     344            left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
     345        };
     346   
     347        this.originalPosition = this.generatePosition(e);                                               //Generate the original position
     348        this.helperProportions = { width: this.helper.outerWidth(), height: this.helper.outerHeight() };//Cache the helper size
     349       
     350        if(o.cursorAt) {
     351            if(o.cursorAt.left != undefined) this.offset.click.left = o.cursorAt.left;
     352            if(o.cursorAt.right != undefined) this.offset.click.left = this.helperProportions.width - o.cursorAt.right;
     353            if(o.cursorAt.top != undefined) this.offset.click.top = o.cursorAt.top;
     354            if(o.cursorAt.bottom != undefined) this.offset.click.top = this.helperProportions.height - o.cursorAt.bottom;
     355        }
     356
     357        this.domPosition = this.currentItem.prev()[0];                                                  //Cache the former DOM position
     358
     359        /*
     360         * - Position constraining -
     361         * Here we prepare position constraining like grid and containment.
     362         */
     363       
     364        if(o.containment) {
     365            if(o.containment == 'parent') o.containment = this.helper[0].parentNode;
     366            if(o.containment == 'document') this.containment = [0,0,$(document).width(), ($(document).height() || document.body.parentNode.scrollHeight)];
     367            if(!(/^(document|window|parent)$/).test(o.containment)) {
     368                var ce = $(o.containment)[0];
     369                var co = $(o.containment).offset();
     370               
     371                this.containment = [
     372                    co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) - this.offset.parent.left,
     373                    co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) - this.offset.parent.top,
     374                    co.left+Math.max(ce.scrollWidth,ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - this.offset.parent.left - this.helperProportions.width - this.margins.left - (parseInt(this.currentItem.css("marginRight"),10) || 0),
     375                    co.top+Math.max(ce.scrollHeight,ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - this.offset.parent.top - this.helperProportions.height - this.margins.top - (parseInt(this.currentItem.css("marginBottom"),10) || 0)
     376                ];
     377            }
     378        }
     379       
     380        //If o.placeholder is used, create a new element at the given position with the class
     381        if(o.placeholder) this.createPlaceholder();
     382       
     383        //Call plugins and callbacks
     384        this.propagate("start", e);
     385        this.helperProportions = { width: this.helper.outerWidth(), height: this.helper.outerHeight() };//Recache the helper size
     386
     387        if(this.options.placeholder != 'clone') this.currentItem.css('visibility', 'hidden'); //Set the original element visibility to hidden to still fill out the white space
     388       
     389        if(!noActivation) {
     390             for (var i = this.containers.length - 1; i >= 0; i--) { this.containers[i].propagate("activate", e, this); } //Post 'activate' events to possible containers
     391        }
     392       
     393        //Prepare possible droppables
     394        if($.ui.ddmanager) $.ui.ddmanager.current = this;
     395        if ($.ui.ddmanager && !o.dropBehaviour) $.ui.ddmanager.prepareOffsets(this, e);
     396
     397        this.dragging = true;
     398
     399        this.mouseDrag(e); //Execute the drag once - this causes the helper not to be visible before getting its correct position
     400        return true;
     401
     402
     403    },
     404    convertPositionTo: function(d, pos) {
     405        if(!pos) pos = this.position;
     406        var mod = d == "absolute" ? 1 : -1;
     407        return {
     408            top: (
     409                pos.top                                                                 // the calculated relative position
     410                + this.offset.parent.top * mod                                          // The offsetParent's offset without borders (offset + border)
     411                - (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollTop) * mod    // The offsetParent's scroll position
     412                + this.margins.top * mod                                                //Add the margin (you don't want the margin counting in intersection methods)
     413            ),
     414            left: (
     415                pos.left                                                                // the calculated relative position
     416                + this.offset.parent.left * mod                                         // The offsetParent's offset without borders (offset + border)
     417                - (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollLeft) * mod   // The offsetParent's scroll position
     418                + this.margins.left * mod                                               //Add the margin (you don't want the margin counting in intersection methods)
     419            )
     420        };
     421    },
     422    generatePosition: function(e) {
     423       
     424        var o = this.options;
     425        var position = {
     426            top: (
     427                e.pageY                                                                 // The absolute mouse position
     428                - this.offset.click.top                                                 // Click offset (relative to the element)
     429                - this.offset.parent.top                                                // The offsetParent's offset without borders (offset + border)
     430                + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollTop)  // The offsetParent's scroll position, not if the element is fixed
     431            ),
     432            left: (
     433                e.pageX                                                                 // The absolute mouse position
     434                - this.offset.click.left                                                // Click offset (relative to the element)
     435                - this.offset.parent.left                                               // The offsetParent's offset without borders (offset + border)
     436                + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollLeft) // The offsetParent's scroll position, not if the element is fixed
     437            )
     438        };
     439       
     440        if(!this.originalPosition) return position;                                     //If we are not dragging yet, we won't check for options
     441       
     442        /*
     443         * - Position constraining -
     444         * Constrain the position to a mix of grid, containment.
     445         */
     446        if(this.containment) {
     447            if(position.left < this.containment[0]) position.left = this.containment[0];
     448            if(position.top < this.containment[1]) position.top = this.containment[1];
     449            if(position.left > this.containment[2]) position.left = this.containment[2];
     450            if(position.top > this.containment[3]) position.top = this.containment[3];
     451        }
     452       
     453        if(o.grid) {
     454            var top = this.originalPosition.top + Math.round((position.top - this.originalPosition.top) / o.grid[1]) * o.grid[1];
     455            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;
    430456           
    431         },
    432         drag: function(e) {
    433 
    434             //Compute the helpers position
    435             this.position.current = { top: e.pageY - this.offset.top, left: e.pageX - this.offset.left };
    436             this.position.absolute = { left: e.pageX - this.clickOffset.left, top: e.pageY - this.clickOffset.top };
    437 
    438             //Rearrange
    439             for (var i = this.items.length - 1; i >= 0; i--) {
    440                 var intersection = this.intersectsWithEdge(this.items[i]);
    441                 if(!intersection) continue;
     457            var left = this.originalPosition.left + Math.round((position.left - this.originalPosition.left) / o.grid[0]) * o.grid[0];
     458            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;
     459        }
     460       
     461        return position;
     462    },
     463    mouseDrag: function(e) {
     464
     465
     466        //Compute the helpers position
     467        this.position = this.generatePosition(e);
     468        this.positionAbs = this.convertPositionTo("absolute");
     469
     470        //Rearrange
     471        for (var i = this.items.length - 1; i >= 0; i--) {
     472            var intersection = this.intersectsWithEdge(this.items[i]);
     473            if(!intersection) continue;
     474           
     475            if(this.items[i].item[0] != this.currentItem[0] //cannot intersect with itself
     476                &&  this.currentItem[intersection == 1 ? "next" : "prev"]()[0] != this.items[i].item[0] //no useless actions that have been done before
     477                &&  !contains(this.currentItem[0], this.items[i].item[0]) //no action if the item moved is the parent of the item checked
     478                && (this.options.type == 'semi-dynamic' ? !contains(this.element[0], this.items[i].item[0]) : true)
     479            ) {
    442480               
    443                 if(this.items[i].item[0] != this.currentItem[0] //cannot intersect with itself
    444                     &&  this.currentItem[intersection == 1 ? "next" : "prev"]()[0] != this.items[i].item[0] //no useless actions that have been done before
    445                     &&  !contains(this.currentItem[0], this.items[i].item[0]) //no action if the item moved is the parent of the item checked
    446                     && (this.options.type == 'semi-dynamic' ? !contains(this.element[0], this.items[i].item[0]) : true)
    447                 ) {
    448                    
    449                     this.direction = intersection == 1 ? "down" : "up";
    450                     this.rearrange(e, this.items[i]);
    451                     this.propagate("change", e); //Call plugins and callbacks
    452                     break;
     481                this.direction = intersection == 1 ? "down" : "up";
     482                this.rearrange(e, this.items[i]);
     483                this.propagate("change", e); //Call plugins and callbacks
     484                break;
     485            }
     486        }
     487       
     488        //Post events to containers
     489        this.contactContainers(e);
     490       
     491         //Call plugins and callbacks
     492        this.propagate("sort", e);
     493
     494        if(!this.options.axis || this.options.axis == "x") this.helper[0].style.left = this.position.left+'px';
     495        if(!this.options.axis || this.options.axis == "y") this.helper[0].style.top = this.position.top+'px';
     496       
     497        //Interconnect with droppables
     498        if($.ui.ddmanager) $.ui.ddmanager.drag(this, e);
     499
     500        return false;
     501       
     502    },
     503    mouseStop: function(e, noPropagation) {
     504
     505        //If we are using droppables, inform the manager about the drop
     506        if ($.ui.ddmanager && !this.options.dropBehaviour)
     507            $.ui.ddmanager.drop(this, e);
     508           
     509        if(this.options.revert) {
     510            var self = this;
     511            var cur = self.currentItem.offset();
     512
     513            //Also animate the placeholder if we have one
     514            if(self.placeholder) self.placeholder.animate({ opacity: 'hide' }, (parseInt(this.options.revert, 10) || 500)-50);
     515
     516            $(this.helper).animate({
     517                left: cur.left - this.offset.parent.left - self.margins.left + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollLeft),
     518                top: cur.top - this.offset.parent.top - self.margins.top + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollTop)
     519            }, parseInt(this.options.revert, 10) || 500, function() {
     520                self.propagate("stop", e, null, noPropagation);
     521                self.clear(e);
     522            });
     523        } else {
     524            this.propagate("stop", e, null, noPropagation);
     525            this.clear(e, noPropagation);
     526        }
     527
     528        return false;
     529       
     530    },
     531    clear: function(e, noPropagation) {
     532       
     533        if(this.domPosition != this.currentItem.prev()[0]) this.propagate("update", e, null, noPropagation); //Trigger update callback if the DOM position has changed
     534        if(!contains(this.element[0], this.currentItem[0])) { //Node was moved out of the current element
     535            this.propagate("remove", e, null, noPropagation);
     536            for (var i = this.containers.length - 1; i >= 0; i--){
     537                if(contains(this.containers[i].element[0], this.currentItem[0])) {
     538                    this.containers[i].propagate("update", e, this, noPropagation);
     539                    this.containers[i].propagate("receive", e, this, noPropagation);
    453540                }
     541            };
     542        };
     543       
     544        //Post events to containers
     545        for (var i = this.containers.length - 1; i >= 0; i--){
     546            this.containers[i].propagate("deactivate", e, this, noPropagation);
     547            if(this.containers[i].containerCache.over) {
     548                this.containers[i].propagate("out", e, this);
     549                this.containers[i].containerCache.over = 0;
    454550            }
    455            
    456             //Post events to containers
    457             this.contactContainers(e);
    458            
    459             //Interconnect with droppables
    460             if($.ui.ddmanager) $.ui.ddmanager.drag(this, e);
    461 
    462             this.propagate("sort", e); //Call plugins and callbacks
    463             this.helper.css({ left: this.position.current.left+'px', top: this.position.current.top+'px' }); // Stick the helper to the cursor
    464             return false;
    465            
    466         },
    467         rearrange: function(e, i, a) {
    468             a ? a.append(this.currentItem) : i.item[this.direction == 'down' ? 'before' : 'after'](this.currentItem);
    469             this.refreshPositions(true); //Precompute after each DOM insertion, NOT on mousemove
    470             if(this.placeholderElement) this.placeholder.css(this.placeholderElement.offset());
    471             if(this.placeholderElement && this.placeholderElement.is(":visible")) this.placeholder.css({ width: this.placeholderElement.outerWidth(), height: this.placeholderElement.outerHeight() });
    472         }
    473     });
    474    
    475     $.extend($.ui.sortable, {
    476         getter: "serialize toArray",
    477         defaults: {
    478             items: '> *',
    479             zIndex: 1000
    480         }
    481     });
    482 
    483    
     551        }
     552       
     553        this.dragging = false;
     554        if(this.cancelHelperRemoval) return false;
     555        $(this.currentItem).css('visibility', '');
     556        if(this.placeholder) this.placeholder.remove();
     557        this.helper.remove();
     558       
     559        return true;
     560       
     561    },
     562    rearrange: function(e, i, a) {
     563        a ? a.append(this.currentItem) : i.item[this.direction == 'down' ? 'before' : 'after'](this.currentItem);
     564        this.refreshPositions(true); //Precompute after each DOM insertion, NOT on mousemove
     565        if(this.options.placeholder) this.options.placeholder.update.call(this.element, this.currentItem, this.placeholder);
     566    }
     567}));
     568
     569$.extend($.ui.sortable, {
     570    getter: "serialize toArray",
     571    defaults: {
     572        tolerance: "guess",
     573        distance: 0,
     574        delay: 0,
     575        cancel: ":input,button",
     576        items: '> *',
     577        zIndex: 1000,
     578        dropOnEmpty: true,
     579        appendTo: "parent"
     580    }
     581});
     582
    484583/*
    485584 * Sortable Extensions
    486585 */
    487586
    488     $.ui.plugin.add("sortable", "cursor", {
    489         start: function(e, ui) {
    490             var t = $('body');
    491             if (t.css("cursor")) ui.options._cursor = t.css("cursor");
    492             t.css("cursor", ui.options.cursor);
    493         },
    494         stop: function(e, ui) {
    495             if (ui.options._cursor) $('body').css("cursor", ui.options._cursor);
    496         }
    497     });
    498 
    499     $.ui.plugin.add("sortable", "zIndex", {
    500         start: function(e, ui) {
    501             var t = ui.helper;
    502             if(t.css("zIndex")) ui.options._zIndex = t.css("zIndex");
    503             t.css('zIndex', ui.options.zIndex);
    504         },
    505         stop: function(e, ui) {
    506             if(ui.options._zIndex) $(ui.helper).css('zIndex', ui.options._zIndex);
    507         }
    508     });
    509 
    510     $.ui.plugin.add("sortable", "opacity", {
    511         start: function(e, ui) {
    512             var t = ui.helper;
    513             if(t.css("opacity")) ui.options._opacity = t.css("opacity");
    514             t.css('opacity', ui.options.opacity);
    515         },
    516         stop: function(e, ui) {
    517             if(ui.options._opacity) $(ui.helper).css('opacity', ui.options._opacity);
    518         }
    519     });
    520 
    521 
    522     $.ui.plugin.add("sortable", "revert", {
    523         stop: function(e, ui) {
    524             var self = ui.instance;
    525             self.cancelHelperRemoval = true;
    526             var cur = self.currentItem.offset();
    527             var op = self.helper.offsetParent().offset();
    528             if(ui.instance.options.zIndex) ui.helper.css('zIndex', ui.instance.options.zIndex); //Do the zIndex again because it already was resetted by the plugin above on stop
    529 
    530             //Also animate the placeholder if we have one
    531             if(ui.instance.placeholder) ui.instance.placeholder.animate({ opacity: 'hide' }, parseInt(ui.options.revert, 10) || 500);
    532            
    533            
    534             ui.helper.animate({
    535                 left: cur.left - op.left - self.margins.left,
    536                 top: cur.top - op.top - self.margins.top
    537             }, parseInt(ui.options.revert, 10) || 500, function() {
    538                 self.currentItem.css('visibility', 'visible');
    539                 window.setTimeout(function() {
    540                     if(self.placeholder) self.placeholder.remove();
    541                     self.helper.remove();
    542                     if(ui.options._zIndex) ui.helper.css('zIndex', ui.options._zIndex);
    543                 }, 50);
    544             });
    545         }
    546     });
    547 
     587$.ui.plugin.add("sortable", "cursor", {
     588    start: function(e, ui) {
     589        var t = $('body');
     590        if (t.css("cursor")) ui.options._cursor = t.css("cursor");
     591        t.css("cursor", ui.options.cursor);
     592    },
     593    stop: function(e, ui) {
     594        if (ui.options._cursor) $('body').css("cursor", ui.options._cursor);
     595    }
     596});
     597
     598$.ui.plugin.add("sortable", "zIndex", {
     599    start: function(e, ui) {
     600        var t = ui.helper;
     601        if(t.css("zIndex")) ui.options._zIndex = t.css("zIndex");
     602        t.css('zIndex', ui.options.zIndex);
     603    },
     604    stop: function(e, ui) {
     605        if(ui.options._zIndex) $(ui.helper).css('zIndex', ui.options._zIndex);
     606    }
     607});
     608
     609$.ui.plugin.add("sortable", "opacity", {
     610    start: function(e, ui) {
     611        var t = ui.helper;
     612        if(t.css("opacity")) ui.options._opacity = t.css("opacity");
     613        t.css('opacity', ui.options.opacity);
     614    },
     615    stop: function(e, ui) {
     616        if(ui.options._opacity) $(ui.helper).css('opacity', ui.options._opacity);
     617    }
     618});
     619
     620$.ui.plugin.add("sortable", "scroll", {
     621    start: function(e, ui) {
     622        var o = ui.options;
     623        var i = $(this).data("sortable");
     624        o.scrollSensitivity = o.scrollSensitivity || 20;
     625        o.scrollSpeed       = o.scrollSpeed || 20;
    548626   
    549     $.ui.plugin.add("sortable", "containment", {
    550         start: function(e, ui) {
    551 
    552             var o = ui.options;
    553             if((o.containment.left != undefined || o.containment.constructor == Array) && !o._containment) return;
    554             if(!o._containment) o._containment = o.containment;
    555 
    556             if(o._containment == 'parent') o._containment = this[0].parentNode;
    557             if(o._containment == 'sortable') o._containment = this[0];
    558             if(o._containment == 'document') {
    559                 o.containment = [
    560                     0,
    561                     0,
    562                     $(document).width(),
    563                     ($(document).height() || document.body.parentNode.scrollHeight)
    564                 ];
    565             } else { //I'm a node, so compute top/left/right/bottom
    566 
    567                 var ce = $(o._containment);
    568                 var co = ce.offset();
    569 
    570                 o.containment = [
    571                     co.left,
    572                     co.top,
    573                     co.left+(ce.outerWidth() || ce[0].scrollWidth),
    574                     co.top+(ce.outerHeight() || ce[0].scrollHeight)
    575                 ];
    576             }
    577 
    578         },
    579         sort: function(e, ui) {
    580 
    581             var o = ui.options;
    582             var h = ui.helper;
    583             var c = o.containment;
    584             var self = ui.instance;
    585             var borderLeft = (parseInt(self.offsetParent.css("borderLeftWidth"), 10) || 0);
    586             var borderRight = (parseInt(self.offsetParent.css("borderRightWidth"), 10) || 0);
    587             var borderTop = (parseInt(self.offsetParent.css("borderTopWidth"), 10) || 0);
    588             var borderBottom = (parseInt(self.offsetParent.css("borderBottomWidth"), 10) || 0);
    589            
    590             if(c.constructor == Array) {
    591                 if((self.position.absolute.left < c[0])) self.position.current.left = c[0] - self.offsets.parent.left - self.margins.left;
    592                 if((self.position.absolute.top < c[1])) self.position.current.top = c[1] - self.offsets.parent.top - self.margins.top;
    593                 if(self.position.absolute.left - c[2] + self.helperProportions.width >= 0) self.position.current.left = c[2] - self.offsets.parent.left - self.helperProportions.width - self.margins.left - borderLeft - borderRight;
    594                 if(self.position.absolute.top - c[3] + self.helperProportions.height >= 0) self.position.current.top = c[3] - self.offsets.parent.top - self.helperProportions.height - self.margins.top - borderTop - borderBottom;
    595             } else {
    596                 if((ui.position.left < c.left)) self.position.current.left = c.left;
    597                 if((ui.position.top < c.top)) self.position.current.top = c.top;
    598                 if(ui.position.left - self.offsetParent.innerWidth() + self.helperProportions.width + c.right + borderLeft + borderRight >= 0) self.position.current.left = self.offsetParent.innerWidth() - self.helperProportions.width - c.right - borderLeft - borderRight;
    599                 if(ui.position.top - self.offsetParent.innerHeight() + self.helperProportions.height + c.bottom + borderTop + borderBottom >= 0) self.position.current.top = self.offsetParent.innerHeight() - self.helperProportions.height - c.bottom - borderTop - borderBottom;
    600             }
    601 
    602         }
    603     });
    604 
    605     $.ui.plugin.add("sortable", "axis", {
    606         sort: function(e, ui) {
    607             var o = ui.options;
    608             if(o.constraint) o.axis = o.constraint; //Legacy check
    609             o.axis == 'x' ? ui.instance.position.current.top = ui.instance.originalPosition.top : ui.instance.position.current.left = ui.instance.originalPosition.left;
    610         }
    611     });
    612 
    613     $.ui.plugin.add("sortable", "scroll", {
    614         start: function(e, ui) {
    615             var o = ui.options;
    616             o.scrollSensitivity = o.scrollSensitivity || 20;
    617             o.scrollSpeed       = o.scrollSpeed || 20;
    618 
    619             ui.instance.overflowY = function(el) {
    620                 do { if((/auto|scroll/).test(el.css('overflow')) || (/auto|scroll/).test(el.css('overflow-y'))) return el; el = el.parent(); } while (el[0].parentNode);
    621                 return $(document);
    622             }(this);
    623             ui.instance.overflowX = function(el) {
    624                 do { if((/auto|scroll/).test(el.css('overflow')) || (/auto|scroll/).test(el.css('overflow-x'))) return el; el = el.parent(); } while (el[0].parentNode);
    625                 return $(document);
    626             }(this);
    627            
    628             if(ui.instance.overflowY[0] != document && ui.instance.overflowY[0].tagName != 'HTML') ui.instance.overflowYstart = ui.instance.overflowY[0].scrollTop;
    629             if(ui.instance.overflowX[0] != document && ui.instance.overflowX[0].tagName != 'HTML') ui.instance.overflowXstart = ui.instance.overflowX[0].scrollLeft;
    630            
    631         },
    632         sort: function(e, ui) {
    633            
    634             var o = ui.options;
    635             var i = ui.instance;
    636 
    637             if(i.overflowY[0] != document && i.overflowY[0].tagName != 'HTML') {
    638                 if(i.overflowY[0].offsetHeight - (ui.position.top - i.overflowY[0].scrollTop + i.clickOffset.top) < o.scrollSensitivity)
    639                     i.overflowY[0].scrollTop = i.overflowY[0].scrollTop + o.scrollSpeed;
    640                 if((ui.position.top - i.overflowY[0].scrollTop + i.clickOffset.top) < o.scrollSensitivity)
    641                     i.overflowY[0].scrollTop = i.overflowY[0].scrollTop - o.scrollSpeed;               
    642             } else {
    643                 //$(document.body).append('<p>'+(e.pageY - $(document).scrollTop())+'</p>');
    644                 if(e.pageY - $(document).scrollTop() < o.scrollSensitivity)
    645                     $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
    646                 if($(window).height() - (e.pageY - $(document).scrollTop()) < o.scrollSensitivity)
    647                     $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
    648             }
    649            
    650             if(i.overflowX[0] != document && i.overflowX[0].tagName != 'HTML') {
    651                 if(i.overflowX[0].offsetWidth - (ui.position.left - i.overflowX[0].scrollLeft + i.clickOffset.left) < o.scrollSensitivity)
    652                     i.overflowX[0].scrollLeft = i.overflowX[0].scrollLeft + o.scrollSpeed;
    653                 if((ui.position.top - i.overflowX[0].scrollLeft + i.clickOffset.left) < o.scrollSensitivity)
    654                     i.overflowX[0].scrollLeft = i.overflowX[0].scrollLeft - o.scrollSpeed;             
    655             } else {
    656                 if(e.pageX - $(document).scrollLeft() < o.scrollSensitivity)
    657                     $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
    658                 if($(window).width() - (e.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
    659                     $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
    660             }
    661            
    662             //ui.instance.recallOffset(e);
    663             i.offset = {
    664                 left: i.mouse.start.left - i.originalPosition.left + (i.overflowXstart !== undefined ? i.overflowXstart - i.overflowX[0].scrollLeft : 0),
    665                 top: i.mouse.start.top - i.originalPosition.top + (i.overflowYstart !== undefined ? i.overflowYstart - i.overflowX[0].scrollTop : 0)
    666             };
    667 
    668         }
    669     });
     627        i.overflowY = function(el) {
     628            do { if(/auto|scroll/.test(el.css('overflow')) || (/auto|scroll/).test(el.css('overflow-y'))) return el; el = el.parent(); } while (el[0].parentNode);
     629            return $(document);
     630        }(i.currentItem);
     631        i.overflowX = function(el) {
     632            do { if(/auto|scroll/.test(el.css('overflow')) || (/auto|scroll/).test(el.css('overflow-x'))) return el; el = el.parent(); } while (el[0].parentNode);
     633            return $(document);
     634        }(i.currentItem);
     635       
     636        if(i.overflowY[0] != document && i.overflowY[0].tagName != 'HTML') i.overflowYOffset = i.overflowY.offset();
     637        if(i.overflowX[0] != document && i.overflowX[0].tagName != 'HTML') i.overflowXOffset = i.overflowX.offset();
     638       
     639    },
     640    sort: function(e, ui) {
     641       
     642        var o = ui.options;
     643        var i = $(this).data("sortable");
     644       
     645        if(i.overflowY[0] != document && i.overflowY[0].tagName != 'HTML') {
     646            if((i.overflowYOffset.top + i.overflowY[0].offsetHeight) - e.pageY < o.scrollSensitivity)
     647                i.overflowY[0].scrollTop = i.overflowY[0].scrollTop + o.scrollSpeed;
     648            if(e.pageY - i.overflowYOffset.top < o.scrollSensitivity)
     649                i.overflowY[0].scrollTop = i.overflowY[0].scrollTop - o.scrollSpeed;
     650        } else {
     651            if(e.pageY - $(document).scrollTop() < o.scrollSensitivity)
     652                $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
     653            if($(window).height() - (e.pageY - $(document).scrollTop()) < o.scrollSensitivity)
     654                $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
     655        }
     656       
     657        if(i.overflowX[0] != document && i.overflowX[0].tagName != 'HTML') {
     658            if((i.overflowXOffset.left + i.overflowX[0].offsetWidth) - e.pageX < o.scrollSensitivity)
     659                i.overflowX[0].scrollLeft = i.overflowX[0].scrollLeft + o.scrollSpeed;
     660            if(e.pageX - i.overflowXOffset.left < o.scrollSensitivity)
     661                i.overflowX[0].scrollLeft = i.overflowX[0].scrollLeft - o.scrollSpeed;
     662        } else {
     663            if(e.pageX - $(document).scrollLeft() < o.scrollSensitivity)
     664                $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
     665            if($(window).width() - (e.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
     666                $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
     667        }
     668       
     669    }
     670});
    670671
    671672})(jQuery);
  • trunk/wp-includes/js/jquery/ui.tabs.js

    r7973 r8032  
    1414 */
    1515;(function($) {
    16    
    17     $.widget("ui.tabs", {
    18         init: function() {
    19             this.options.event += '.tabs'; // namespace event
     16
     17$.widget("ui.tabs", {
     18    init: function() {
     19        this.options.event += '.tabs'; // namespace event
     20       
     21        // create tabs
     22        this.tabify(true);
     23    },
     24    setData: function(key, value) {
     25        if ((/^selected/).test(key))
     26            this.select(value);
     27        else {
     28            this.options[key] = value;
     29            this.tabify();
     30        }
     31    },
     32    length: function() {
     33        return this.$tabs.length;
     34    },
     35    tabId: function(a) {
     36        return a.title && a.title.replace(/\s/g, '_').replace(/[^A-Za-z0-9\-_:\.]/g, '')
     37            || this.options.idPrefix + $.data(a);
     38    },
     39    ui: function(tab, panel) {
     40        return {
     41            instance: this,
     42            options: this.options,
     43            tab: tab,
     44            panel: panel
     45        };
     46    },
     47    tabify: function(init) {
     48
     49        this.$lis = $('li:has(a[href])', this.element);
     50        this.$tabs = this.$lis.map(function() { return $('a', this)[0]; });
     51        this.$panels = $([]);
     52
     53        var self = this, o = this.options;
     54
     55        this.$tabs.each(function(i, a) {
     56            // inline tab
     57            if (a.hash && a.hash.replace('#', '')) // Safari 2 reports '#' for an empty hash
     58                self.$panels = self.$panels.add(a.hash);
     59            // remote tab
     60            else if ($(a).attr('href') != '#') { // prevent loading the page itself if href is just "#"
     61                $.data(a, 'href.tabs', a.href); // required for restore on destroy
     62                $.data(a, 'load.tabs', a.href); // mutable
     63                var id = self.tabId(a);
     64                a.href = '#' + id;
     65                var $panel = $('#' + id);
     66                if (!$panel.length) {
     67                    $panel = $(o.panelTemplate).attr('id', id).addClass(o.panelClass)
     68                        .insertAfter( self.$panels[i - 1] || self.element );
     69                    $panel.data('destroy.tabs', true);
     70                }
     71                self.$panels = self.$panels.add( $panel );
     72            }
     73            // invalid tab href
     74            else
     75                o.disabled.push(i + 1);
     76        });
     77
     78        if (init) {
     79
     80            // attach necessary classes for styling if not present
     81            this.element.addClass(o.navClass);
     82            this.$panels.each(function() {
     83                var $this = $(this);
     84                $this.addClass(o.panelClass);
     85            });
     86
     87            // Selected tab
     88            // use "selected" option or try to retrieve:
     89            // 1. from fragment identifier in url
     90            // 2. from cookie
     91            // 3. from selected class attribute on <li>
     92            if (o.selected === undefined) {
     93                if (location.hash) {
     94                    this.$tabs.each(function(i, a) {
     95                        if (a.hash == location.hash) {
     96                            o.selected = i;
     97                            // prevent page scroll to fragment
     98                            if ($.browser.msie || $.browser.opera) { // && !o.remote
     99                                var $toShow = $(location.hash), toShowId = $toShow.attr('id');
     100                                $toShow.attr('id', '');
     101                                setTimeout(function() {
     102                                    $toShow.attr('id', toShowId); // restore id
     103                                }, 500);
     104                            }
     105                            scrollTo(0, 0);
     106                            return false; // break
     107                        }
     108                    });
     109                }
     110                else if (o.cookie) {
     111                    var index = parseInt($.cookie('ui-tabs' + $.data(self.element)),10);
     112                    if (index && self.$tabs[index])
     113                        o.selected = index;
     114                }
     115                else if (self.$lis.filter('.' + o.selectedClass).length)
     116                    o.selected = self.$lis.index( self.$lis.filter('.' + o.selectedClass)[0] );
     117            }
     118            o.selected = o.selected === null || o.selected !== undefined ? o.selected : 0; // first tab selected by default
     119
     120            // Take disabling tabs via class attribute from HTML
     121            // into account and update option properly.
     122            // A selected tab cannot become disabled.
     123            o.disabled = $.unique(o.disabled.concat(
     124                $.map(this.$lis.filter('.' + o.disabledClass),
     125                    function(n, i) { return self.$lis.index(n); } )
     126            )).sort();
     127            if ($.inArray(o.selected, o.disabled) != -1)
     128                o.disabled.splice($.inArray(o.selected, o.disabled), 1);
    20129           
    21             // create tabs
    22             this.tabify(true);
    23         },
    24         setData: function(key, value) {
    25             if ((/^selected/).test(key))
    26                 this.select(value);
    27             else {
    28                 this.options[key] = value;
    29                 this.tabify();
    30             }
    31         },
    32         length: function() {
    33             return this.$tabs.length;
    34         },
    35         tabId: function(a) {
    36             return a.title && a.title.replace(/\s/g, '_').replace(/[^A-Za-z0-9\-_:\.]/g, '')
    37                 || this.options.idPrefix + $.data(a);
    38         },
    39         ui: function(tab, panel) {
    40             return {
    41                 instance: this,
    42                 options: this.options,
    43                 tab: tab,
    44                 panel: panel
    45             };
    46         },
    47         tabify: function(init) {
    48 
    49             this.$lis = $('li:has(a[href])', this.element);
    50             this.$tabs = this.$lis.map(function() { return $('a', this)[0]; });
    51             this.$panels = $([]);
    52 
    53             var self = this, o = this.options;
    54 
    55             this.$tabs.each(function(i, a) {
    56                 // inline tab
    57                 if (a.hash && a.hash.replace('#', '')) // Safari 2 reports '#' for an empty hash
    58                     self.$panels = self.$panels.add(a.hash);
    59                 // remote tab
    60                 else if ($(a).attr('href') != '#') { // prevent loading the page itself if href is just "#"
    61                     $.data(a, 'href.tabs', a.href); // required for restore on destroy
    62                     $.data(a, 'load.tabs', a.href); // mutable
    63                     var id = self.tabId(a);
    64                     a.href = '#' + id;
    65                     var $panel = $('#' + id);
    66                     if (!$panel.length) {
    67                         $panel = $(o.panelTemplate).attr('id', id).addClass(o.panelClass)
    68                             .insertAfter( self.$panels[i - 1] || self.element );
    69                         $panel.data('destroy.tabs', true);
    70                     }
    71                     self.$panels = self.$panels.add( $panel );
    72                 }
    73                 // invalid tab href
     130            // highlight selected tab
     131            this.$panels.addClass(o.hideClass);
     132            this.$lis.removeClass(o.selectedClass);
     133            if (o.selected !== null) {
     134                this.$panels.eq(o.selected).show().removeClass(o.hideClass); // use show and remove class to show in any case no matter how it has been hidden before
     135                this.$lis.eq(o.selected).addClass(o.selectedClass);
     136               
     137                // seems to be expected behavior that the show callback is fired
     138                var onShow = function() {
     139                    $(self.element).triggerHandler('tabsshow',
     140                        [self.ui(self.$tabs[o.selected], self.$panels[o.selected])], o.show);
     141                };
     142
     143                // load if remote tab
     144                if ($.data(this.$tabs[o.selected], 'load.tabs'))
     145                    this.load(o.selected, onShow);
     146                // just trigger show event
    74147                else
    75                     o.disabled.push(i + 1);
     148                    onShow();
     149               
     150            }
     151           
     152            // clean up to avoid memory leaks in certain versions of IE 6
     153            $(window).bind('unload', function() {
     154                self.$tabs.unbind('.tabs');
     155                self.$lis = self.$tabs = self.$panels = null;
    76156            });
    77157
    78             if (init) {
    79 
    80                 // attach necessary classes for styling if not present
    81                 this.element.addClass(o.navClass);
    82                 this.$panels.each(function() {
    83                     var $this = $(this);
    84                     $this.addClass(o.panelClass);
    85                 });
    86 
    87                 // Selected tab
    88                 // use "selected" option or try to retrieve:
    89                 // 1. from fragment identifier in url
    90                 // 2. from cookie
    91                 // 3. from selected class attribute on <li>
    92                 if (o.selected === undefined) {
    93                     if (location.hash) {
    94                         this.$tabs.each(function(i, a) {
    95                             if (a.hash == location.hash) {
    96                                 o.selected = i;
    97                                 // prevent page scroll to fragment
    98                                 if ($.browser.msie || $.browser.opera) { // && !o.remote
    99                                     var $toShow = $(location.hash), toShowId = $toShow.attr('id');
    100                                     $toShow.attr('id', '');
    101                                     setTimeout(function() {
    102                                         $toShow.attr('id', toShowId); // restore id
    103                                     }, 500);
    104                                 }
    105                                 scrollTo(0, 0);
    106                                 return false; // break
    107                             }
    108                         });
    109                     }
    110                     else if (o.cookie) {
    111                         var index = parseInt($.cookie('ui-tabs' + $.data(self.element)),10);
    112                         if (index && self.$tabs[index])
    113                             o.selected = index;
    114                     }
    115                     else if (self.$lis.filter('.' + o.selectedClass).length)
    116                         o.selected = self.$lis.index( self.$lis.filter('.' + o.selectedClass)[0] );
    117                 }
    118                 o.selected = o.selected === null || o.selected !== undefined ? o.selected : 0; // first tab selected by default
    119 
    120                 // Take disabling tabs via class attribute from HTML
    121                 // into account and update option properly.
    122                 // A selected tab cannot become disabled.
    123                 o.disabled = $.unique(o.disabled.concat(
    124                     $.map(this.$lis.filter('.' + o.disabledClass),
    125                         function(n, i) { return self.$lis.index(n); } )
    126                 )).sort();
    127                 if ($.inArray(o.selected, o.disabled) != -1)
    128                     o.disabled.splice($.inArray(o.selected, o.disabled), 1);
    129                
    130                 // highlight selected tab
    131                 this.$panels.addClass(o.hideClass);
    132                 this.$lis.removeClass(o.selectedClass);
    133                 if (o.selected !== null) {
    134                     this.$panels.eq(o.selected).show().removeClass(o.hideClass); // use show and remove class to show in any case no matter how it has been hidden before
    135                     this.$lis.eq(o.selected).addClass(o.selectedClass);
    136                    
    137                     // seems to be expected behavior that the show callback is fired
    138                     var onShow = function() {
    139                         $(self.element).triggerHandler('tabsshow',
    140                             [self.ui(self.$tabs[o.selected], self.$panels[o.selected])], o.show);
    141                     };
    142 
    143                     // load if remote tab
    144                     if ($.data(this.$tabs[o.selected], 'load.tabs'))
    145                         this.load(o.selected, onShow);
    146                     // just trigger show event
    147                     else
    148                         onShow();
    149                    
    150                 }
    151                
    152                 // clean up to avoid memory leaks in certain versions of IE 6
    153                 $(window).bind('unload', function() {
    154                     self.$tabs.unbind('.tabs');
    155                     self.$lis = self.$tabs = self.$panels = null;
    156                 });
    157 
    158             }
    159 
    160             // disable tabs
    161             for (var i = 0, li; li = this.$lis[i]; i++)
    162                 $(li)[$.inArray(i, o.disabled) != -1 && !$(li).hasClass(o.selectedClass) ? 'addClass' : 'removeClass'](o.disabledClass);
    163 
    164             // reset cache if switching from cached to not cached
    165             if (o.cache === false)
    166                 this.$tabs.removeData('cache.tabs');
    167            
    168             // set up animations
    169             var hideFx, showFx, baseFx = { 'min-width': 0, duration: 1 }, baseDuration = 'normal';
    170             if (o.fx && o.fx.constructor == Array)
    171                 hideFx = o.fx[0] || baseFx, showFx = o.fx[1] || baseFx;
    172             else
    173                 hideFx = showFx = o.fx || baseFx;
    174 
    175             // reset some styles to maintain print style sheets etc.
    176             var resetCSS = { display: '', overflow: '', height: '' };
    177             if (!$.browser.msie) // not in IE to prevent ClearType font issue
    178                 resetCSS.opacity = '';
    179 
    180             // Hide a tab, animation prevents browser scrolling to fragment,
    181             // $show is optional.
    182             function hideTab(clicked, $hide, $show) {
    183                 $hide.animate(hideFx, hideFx.duration || baseDuration, function() { //
    184                     $hide.addClass(o.hideClass).css(resetCSS); // maintain flexible height and accessibility in print etc.
    185                     if ($.browser.msie && hideFx.opacity)
    186                         $hide[0].style.filter = '';
    187                     if ($show)
    188                         showTab(clicked, $show, $hide);
    189                 });
    190             }
    191 
    192             // Show a tab, animation prevents browser scrolling to fragment,
    193             // $hide is optional.
    194             function showTab(clicked, $show, $hide) {
    195                 if (showFx === baseFx)
    196                     $show.css('display', 'block'); // prevent occasionally occuring flicker in Firefox cause by gap between showing and hiding the tab panels
    197                 $show.animate(showFx, showFx.duration || baseDuration, function() {
    198                     $show.removeClass(o.hideClass).css(resetCSS); // maintain flexible height and accessibility in print etc.
    199                     if ($.browser.msie && showFx.opacity)
    200                         $show[0].style.filter = '';
    201 
    202                     // callback
    203                     $(self.element).triggerHandler('tabsshow',
    204                         [self.ui(clicked, $show[0])], o.show);
    205 
    206                 });
    207             }
    208 
    209             // switch a tab
    210             function switchTab(clicked, $li, $hide, $show) {
    211                 /*if (o.bookmarkable && trueClick) { // add to history only if true click occured, not a triggered click
    212                     $.ajaxHistory.update(clicked.hash);
    213                 }*/
    214                 $li.addClass(o.selectedClass)
    215                     .siblings().removeClass(o.selectedClass);
    216                 hideTab(clicked, $hide, $show);
    217             }
    218 
    219             // attach tab event handler, unbind to avoid duplicates from former tabifying...
    220             this.$tabs.unbind('.tabs').bind(o.event, function() {
    221 
    222                 //var trueClick = e.clientX; // add to history only if true click occured, not a triggered click
    223                 var $li = $(this).parents('li:eq(0)'),
    224                     $hide = self.$panels.filter(':visible'),
    225                     $show = $(this.hash);
    226 
    227                 // If tab is already selected and not unselectable or tab disabled or
    228                 // or is already loading or click callback returns false stop here.
    229                 // Check if click handler returns false last so that it is not executed
    230                 // for a disabled or loading tab!
    231                 if (($li.hasClass(o.selectedClass) && !o.unselect)
    232                     || $li.hasClass(o.disabledClass)
    233                     || $(this).hasClass(o.loadingClass)
    234                     || $(self.element).triggerHandler('tabsselect', [self.ui(this, $show[0])], o.select) === false
    235                     ) {
     158        }
     159
     160        // disable tabs
     161        for (var i = 0, li; li = this.$lis[i]; i++)
     162            $(li)[$.inArray(i, o.disabled) != -1 && !$(li).hasClass(o.selectedClass) ? 'addClass' : 'removeClass'](o.disabledClass);
     163
     164        // reset cache if switching from cached to not cached
     165        if (o.cache === false)
     166            this.$tabs.removeData('cache.tabs');
     167       
     168        // set up animations
     169        var hideFx, showFx, baseFx = { 'min-width': 0, duration: 1 }, baseDuration = 'normal';
     170        if (o.fx && o.fx.constructor == Array)
     171            hideFx = o.fx[0] || baseFx, showFx = o.fx[1] || baseFx;
     172        else
     173            hideFx = showFx = o.fx || baseFx;
     174
     175        // reset some styles to maintain print style sheets etc.
     176        var resetCSS = { display: '', overflow: '', height: '' };
     177        if (!$.browser.msie) // not in IE to prevent ClearType font issue
     178            resetCSS.opacity = '';
     179
     180        // Hide a tab, animation prevents browser scrolling to fragment,
     181        // $show is optional.
     182        function hideTab(clicked, $hide, $show) {
     183            $hide.animate(hideFx, hideFx.duration || baseDuration, function() { //
     184                $hide.addClass(o.hideClass).css(resetCSS); // maintain flexible height and accessibility in print etc.
     185                if ($.browser.msie && hideFx.opacity)
     186                    $hide[0].style.filter = '';
     187                if ($show)
     188                    showTab(clicked, $show, $hide);
     189            });
     190        }
     191
     192        // Show a tab, animation prevents browser scrolling to fragment,
     193        // $hide is optional.
     194        function showTab(clicked, $show, $hide) {
     195            if (showFx === baseFx)
     196                $show.css('display', 'block'); // prevent occasionally occuring flicker in Firefox cause by gap between showing and hiding the tab panels
     197            $show.animate(showFx, showFx.duration || baseDuration, function() {
     198                $show.removeClass(o.hideClass).css(resetCSS); // maintain flexible height and accessibility in print etc.
     199                if ($.browser.msie && showFx.opacity)
     200                    $show[0].style.filter = '';
     201
     202                // callback
     203                $(self.element).triggerHandler('tabsshow',
     204                    [self.ui(clicked, $show[0])], o.show);
     205
     206            });
     207        }
     208
     209        // switch a tab
     210        function switchTab(clicked, $li, $hide, $show) {
     211            /*if (o.bookmarkable && trueClick) { // add to history only if true click occured, not a triggered click
     212                $.ajaxHistory.update(clicked.hash);
     213            }*/
     214            $li.addClass(o.selectedClass)
     215                .siblings().removeClass(o.selectedClass);
     216            hideTab(clicked, $hide, $show);
     217        }
     218
     219        // attach tab event handler, unbind to avoid duplicates from former tabifying...
     220        this.$tabs.unbind('.tabs').bind(o.event, function() {
     221
     222            //var trueClick = e.clientX; // add to history only if true click occured, not a triggered click
     223            var $li = $(this).parents('li:eq(0)'),
     224                $hide = self.$panels.filter(':visible'),
     225                $show = $(this.hash);
     226
     227            // If tab is already selected and not unselectable or tab disabled or
     228            // or is already loading or click callback returns false stop here.
     229            // Check if click handler returns false last so that it is not executed
     230            // for a disabled or loading tab!
     231            if (($li.hasClass(o.selectedClass) && !o.unselect)
     232                || $li.hasClass(o.disabledClass)
     233                || $(this).hasClass(o.loadingClass)
     234                || $(self.element).triggerHandler('tabsselect', [self.ui(this, $show[0])], o.select) === false
     235                ) {
     236                this.blur();
     237                return false;
     238            }
     239
     240            self.options.selected = self.$tabs.index(this);
     241
     242            // if tab may be closed
     243            if (o.unselect) {
     244                if ($li.hasClass(o.selectedClass)) {
     245                    self.options.selected = null;
     246                    $li.removeClass(o.selectedClass);
     247                    self.$panels.stop();
     248                    hideTab(this, $hide);
     249                    this.blur();
     250                    return false;
     251                } else if (!$hide.length) {
     252                    self.$panels.stop();
     253                    var a = this;
     254                    self.load(self.$tabs.index(this), function() {
     255                        $li.addClass(o.selectedClass).addClass(o.unselectClass);
     256                        showTab(a, $show);
     257                    });
    236258                    this.blur();
    237259                    return false;
    238260                }
    239 
    240                 self.options.selected = self.$tabs.index(this);
    241 
    242                 // if tab may be closed
    243                 if (o.unselect) {
    244                     if ($li.hasClass(o.selectedClass)) {
    245                         self.options.selected = null;
    246                         $li.removeClass(o.selectedClass);
    247                         self.$panels.stop();
    248                         hideTab(this, $hide);
    249                         this.blur();
    250                         return false;
    251                     } else if (!$hide.length) {
    252                         self.$panels.stop();
    253                         var a = this;
    254                         self.load(self.$tabs.index(this), function() {
    255                             $li.addClass(o.selectedClass).addClass(o.unselectClass);
    256                             showTab(a, $show);
     261            }
     262
     263            if (o.cookie)
     264                $.cookie('ui-tabs' + $.data(self.element), self.options.selected, o.cookie);
     265
     266            // stop possibly running animations
     267            self.$panels.stop();
     268
     269            // show new tab
     270            if ($show.length) {
     271
     272                // prevent scrollbar scrolling to 0 and than back in IE7, happens only if bookmarking/history is enabled
     273                /*if ($.browser.msie && o.bookmarkable) {
     274                    var showId = this.hash.replace('#', '');
     275                    $show.attr('id', '');
     276                    setTimeout(function() {
     277                        $show.attr('id', showId); // restore id
     278                    }, 0);
     279                }*/
     280
     281                var a = this;
     282                self.load(self.$tabs.index(this), $hide.length ?
     283                    function() {
     284                        switchTab(a, $li, $hide, $show);
     285                    } :
     286                    function() {
     287                        $li.addClass(o.selectedClass);
     288                        showTab(a, $show);
     289                    }
     290                );
     291
     292                // Set scrollbar to saved position - need to use timeout with 0 to prevent browser scroll to target of hash
     293                /*var scrollX = window.pageXOffset || document.documentElement && document.documentElement.scrollLeft || document.body.scrollLeft || 0;
     294                var scrollY = window.pageYOffset || document.documentElement && document.documentElement.scrollTop || document.body.scrollTop || 0;
     295                setTimeout(function() {
     296                    scrollTo(scrollX, scrollY);
     297                }, 0);*/
     298
     299            } else
     300                throw 'jQuery UI Tabs: Mismatching fragment identifier.';
     301
     302            // Prevent IE from keeping other link focussed when using the back button
     303            // and remove dotted border from clicked link. This is controlled in modern
     304            // browsers via CSS, also blur removes focus from address bar in Firefox
     305            // which can become a usability and annoying problem with tabsRotate.
     306            if ($.browser.msie)
     307                this.blur();
     308
     309            //return o.bookmarkable && !!trueClick; // convert trueClick == undefined to Boolean required in IE
     310            return false;
     311
     312        });
     313
     314        // disable click if event is configured to something else
     315        if (!(/^click/).test(o.event))
     316            this.$tabs.bind('click.tabs', function() { return false; });
     317
     318    },
     319    add: function(url, label, index) {
     320        if (index == undefined)
     321            index = this.$tabs.length; // append by default
     322
     323        var o = this.options;
     324        var $li = $(o.tabTemplate.replace(/#\{href\}/g, url).replace(/#\{label\}/g, label));
     325        $li.data('destroy.tabs', true);
     326
     327        var id = url.indexOf('#') == 0 ? url.replace('#', '') : this.tabId( $('a:first-child', $li)[0] );
     328
     329        // try to find an existing element before creating a new one
     330        var $panel = $('#' + id);
     331        if (!$panel.length) {
     332            $panel = $(o.panelTemplate).attr('id', id)
     333                .addClass(o.hideClass)
     334                .data('destroy.tabs', true);
     335        }
     336        $panel.addClass(o.panelClass);
     337        if (index >= this.$lis.length) {
     338            $li.appendTo(this.element);
     339            $panel.appendTo(this.element[0].parentNode);
     340        } else {
     341            $li.insertBefore(this.$lis[index]);
     342            $panel.insertBefore(this.$panels[index]);
     343        }
     344       
     345        o.disabled = $.map(o.disabled,
     346            function(n, i) { return n >= index ? ++n : n });
     347           
     348        this.tabify();
     349
     350        if (this.$tabs.length == 1) {
     351            $li.addClass(o.selectedClass);
     352            $panel.removeClass(o.hideClass);
     353            var href = $.data(this.$tabs[0], 'load.tabs');
     354            if (href)
     355                this.load(index, href);
     356        }
     357
     358        // callback
     359        this.element.triggerHandler('tabsadd',
     360            [this.ui(this.$tabs[index], this.$panels[index])], o.add
     361        );
     362    },
     363    remove: function(index) {
     364        var o = this.options, $li = this.$lis.eq(index).remove(),
     365            $panel = this.$panels.eq(index).remove();
     366
     367        // If selected tab was removed focus tab to the right or
     368        // in case the last tab was removed the tab to the left.
     369        if ($li.hasClass(o.selectedClass) && this.$tabs.length > 1)
     370            this.select(index + (index + 1 < this.$tabs.length ? 1 : -1));
     371
     372        o.disabled = $.map($.grep(o.disabled, function(n, i) { return n != index; }),
     373            function(n, i) { return n >= index ? --n : n });
     374
     375        this.tabify();
     376
     377        // callback
     378        this.element.triggerHandler('tabsremove',
     379            [this.ui($li.find('a')[0], $panel[0])], o.remove
     380        );
     381    },
     382    enable: function(index) {
     383        var o = this.options;
     384        if ($.inArray(index, o.disabled) == -1)
     385            return;
     386           
     387        var $li = this.$lis.eq(index).removeClass(o.disabledClass);
     388        if ($.browser.safari) { // fix disappearing tab (that used opacity indicating disabling) after enabling in Safari 2...
     389            $li.css('display', 'inline-block');
     390            setTimeout(function() {
     391                $li.css('display', 'block');
     392            }, 0);
     393        }
     394
     395        o.disabled = $.grep(o.disabled, function(n, i) { return n != index; });
     396
     397        // callback
     398        this.element.triggerHandler('tabsenable',
     399            [this.ui(this.$tabs[index], this.$panels[index])], o.enable
     400        );
     401
     402    },
     403    disable: function(index) {
     404        var self = this, o = this.options;
     405        if (index != o.selected) { // cannot disable already selected tab
     406            this.$lis.eq(index).addClass(o.disabledClass);
     407
     408            o.disabled.push(index);
     409            o.disabled.sort();
     410
     411            // callback
     412            this.element.triggerHandler('tabsdisable',
     413                [this.ui(this.$tabs[index], this.$panels[index])], o.disable
     414            );
     415        }
     416    },
     417    select: function(index) {
     418        if (typeof index == 'string')
     419            index = this.$tabs.index( this.$tabs.filter('[href$=' + index + ']')[0] );
     420        this.$tabs.eq(index).trigger(this.options.event);
     421    },
     422    load: function(index, callback) { // callback is for internal usage only
     423       
     424        var self = this, o = this.options, $a = this.$tabs.eq(index), a = $a[0],
     425                bypassCache = callback == undefined || callback === false, url = $a.data('load.tabs');
     426
     427        callback = callback || function() {};
     428       
     429        // no remote or from cache - just finish with callback
     430        if (!url || !bypassCache && $.data(a, 'cache.tabs')) {
     431            callback();
     432            return;
     433        }
     434
     435        // load remote from here on
     436       
     437        var inner = function(parent) {
     438            var $parent = $(parent), $inner = $parent.find('*:last');
     439            return $inner.length && $inner || $parent;
     440        };
     441        var cleanup = function() {
     442            self.$tabs.filter('.' + o.loadingClass).removeClass(o.loadingClass)
     443                        .each(function() {
     444                            if (o.spinner)
     445                                inner(this).parent().html(inner(this).data('label.tabs'));
    257446                        });
    258                         this.blur();
    259                         return false;
    260                     }
    261                 }
    262 
    263                 if (o.cookie)
    264                     $.cookie('ui-tabs' + $.data(self.element), self.options.selected, o.cookie);
    265 
    266                 // stop possibly running animations
    267                 self.$panels.stop();
    268 
    269                 // show new tab
    270                 if ($show.length) {
    271 
    272                     // prevent scrollbar scrolling to 0 and than back in IE7, happens only if bookmarking/history is enabled
    273                     /*if ($.browser.msie && o.bookmarkable) {
    274                         var showId = this.hash.replace('#', '');
    275                         $show.attr('id', '');
    276                         setTimeout(function() {
    277                             $show.attr('id', showId); // restore id
    278                         }, 0);
    279                     }*/
    280 
    281                     var a = this;
    282                     self.load(self.$tabs.index(this), $hide.length ?
    283                         function() {
    284                             switchTab(a, $li, $hide, $show);
    285                         } :
    286                         function() {
    287                             $li.addClass(o.selectedClass);
    288                             showTab(a, $show);
    289                         }
    290                     );
    291 
    292                     // Set scrollbar to saved position - need to use timeout with 0 to prevent browser scroll to target of hash
    293                     /*var scrollX = window.pageXOffset || document.documentElement && document.documentElement.scrollLeft || document.body.scrollLeft || 0;
    294                     var scrollY = window.pageYOffset || document.documentElement && document.documentElement.scrollTop || document.body.scrollTop || 0;
    295                     setTimeout(function() {
    296                         scrollTo(scrollX, scrollY);
    297                     }, 0);*/
    298 
    299                 } else
    300                     throw 'jQuery UI Tabs: Mismatching fragment identifier.';
    301 
    302                 // Prevent IE from keeping other link focussed when using the back button
    303                 // and remove dotted border from clicked link. This is controlled in modern
    304                 // browsers via CSS, also blur removes focus from address bar in Firefox
    305                 // which can become a usability and annoying problem with tabsRotate.
    306                 if ($.browser.msie)
    307                     this.blur();
    308 
    309                 //return o.bookmarkable && !!trueClick; // convert trueClick == undefined to Boolean required in IE
    310                 return false;
    311 
     447            self.xhr = null;
     448        };
     449       
     450        if (o.spinner) {
     451            var label = inner(a).html();
     452            inner(a).wrapInner('<em></em>')
     453                .find('em').data('label.tabs', label).html(o.spinner);
     454        }
     455
     456        var ajaxOptions = $.extend({}, o.ajaxOptions, {
     457            url: url,
     458            success: function(r, s) {
     459                $(a.hash).html(r);
     460                cleanup();
     461               
     462                if (o.cache)
     463                    $.data(a, 'cache.tabs', true); // if loaded once do not load them again
     464
     465                // callbacks
     466                $(self.element).triggerHandler('tabsload',
     467                    [self.ui(self.$tabs[index], self.$panels[index])], o.load
     468                );
     469                o.ajaxOptions.success && o.ajaxOptions.success(r, s);
     470               
     471                // This callback is required because the switch has to take
     472                // place after loading has completed. Call last in order to
     473                // fire load before show callback...
     474                callback();
     475            }
     476        });
     477        if (this.xhr) {
     478            // terminate pending requests from other tabs and restore tab label
     479            this.xhr.abort();
     480            cleanup();
     481        }
     482        $a.addClass(o.loadingClass);
     483        setTimeout(function() { // timeout is again required in IE, "wait" for id being restored
     484            self.xhr = $.ajax(ajaxOptions);
     485        }, 0);
     486
     487    },
     488    url: function(index, url) {
     489        this.$tabs.eq(index).removeData('cache.tabs').data('load.tabs', url);
     490    },
     491    destroy: function() {
     492        var o = this.options;
     493        this.element.unbind('.tabs')
     494            .removeClass(o.navClass).removeData('tabs');
     495        this.$tabs.each(function() {
     496            var href = $.data(this, 'href.tabs');
     497            if (href)
     498                this.href = href;
     499            var $this = $(this).unbind('.tabs');
     500            $.each(['href', 'load', 'cache'], function(i, prefix) {
     501                $this.removeData(prefix + '.tabs');
    312502            });
    313 
    314             // disable click if event is configured to something else
    315             if (!(/^click/).test(o.event))
    316                 this.$tabs.bind('click.tabs', function() { return false; });
    317 
    318         },
    319         add: function(url, label, index) {
    320             if (index == undefined)
    321                 index = this.$tabs.length; // append by default
    322 
    323             var o = this.options;
    324             var $li = $(o.tabTemplate.replace(/#\{href\}/, url).replace(/#\{label\}/, label));
    325             $li.data('destroy.tabs', true);
    326 
    327             var id = url.indexOf('#') == 0 ? url.replace('#', '') : this.tabId( $('a:first-child', $li)[0] );
    328 
    329             // try to find an existing element before creating a new one
    330             var $panel = $('#' + id);
    331             if (!$panel.length) {
    332                 $panel = $(o.panelTemplate).attr('id', id)
    333                     .addClass(o.hideClass)
    334                     .data('destroy.tabs', true);
    335             }
    336             $panel.addClass(o.panelClass);
    337             if (index >= this.$lis.length) {
    338                 $li.appendTo(this.element);
    339                 $panel.appendTo(this.element[0].parentNode);
    340             } else {
    341                 $li.insertBefore(this.$lis[index]);
    342                 $panel.insertBefore(this.$panels[index]);
    343             }
    344            
    345             o.disabled = $.map(o.disabled,
    346                 function(n, i) { return n >= index ? ++n : n });
    347                
    348             this.tabify();
    349 
    350             if (this.$tabs.length == 1) {
    351                 $li.addClass(o.selectedClass);
    352                 $panel.removeClass(o.hideClass);
    353                 var href = $.data(this.$tabs[0], 'load.tabs');
    354                 if (href)
    355                     this.load(index, href);
    356             }
    357 
    358             // callback
    359             this.element.triggerHandler('tabsadd',
    360                 [this.ui(this.$tabs[index], this.$panels[index])], o.add
    361             );
    362         },
    363         remove: function(index) {
    364             var o = this.options, $li = this.$lis.eq(index).remove(),
    365                 $panel = this.$panels.eq(index).remove();
    366 
    367             // If selected tab was removed focus tab to the right or
    368             // in case the last tab was removed the tab to the left.
    369             if ($li.hasClass(o.selectedClass) && this.$tabs.length > 1)
    370                 this.select(index + (index + 1 < this.$tabs.length ? 1 : -1));
    371 
    372             o.disabled = $.map($.grep(o.disabled, function(n, i) { return n != index; }),
    373                 function(n, i) { return n >= index ? --n : n });
    374 
    375             this.tabify();
    376 
    377             // callback
    378             this.element.triggerHandler('tabsremove',
    379                 [this.ui($li.find('a')[0], $panel[0])], o.remove
    380             );
    381         },
    382         enable: function(index) {
    383             var o = this.options;
    384             if ($.inArray(index, o.disabled) == -1)
    385                 return;
    386                
    387             var $li = this.$lis.eq(index).removeClass(o.disabledClass);
    388             if ($.browser.safari) { // fix disappearing tab (that used opacity indicating disabling) after enabling in Safari 2...
    389                 $li.css('display', 'inline-block');
    390                 setTimeout(function() {
    391                     $li.css('display', 'block');
    392                 }, 0);
    393             }
    394 
    395             o.disabled = $.grep(o.disabled, function(n, i) { return n != index; });
    396 
    397             // callback
    398             this.element.triggerHandler('tabsenable',
    399                 [this.ui(this.$tabs[index], this.$panels[index])], o.enable
    400             );
    401 
    402         },
    403         disable: function(index) {
    404             var self = this, o = this.options;
    405             if (index != o.selected) { // cannot disable already selected tab
    406                 this.$lis.eq(index).addClass(o.disabledClass);
    407 
    408                 o.disabled.push(index);
    409                 o.disabled.sort();
    410 
    411                 // callback
    412                 this.element.triggerHandler('tabsdisable',
    413                     [this.ui(this.$tabs[index], this.$panels[index])], o.disable
    414                 );
    415             }
    416         },
    417         select: function(index) {
    418             if (typeof index == 'string')
    419                 index = this.$tabs.index( this.$tabs.filter('[href$=' + index + ']')[0] );
    420             this.$tabs.eq(index).trigger(this.options.event);
    421         },
    422         load: function(index, callback) { // callback is for internal usage only
    423            
    424             var self = this, o = this.options, $a = this.$tabs.eq(index), a = $a[0],
    425                     bypassCache = callback == undefined || callback === false, url = $a.data('load.tabs');
    426 
    427             callback = callback || function() {};
    428            
    429             // no remote or from cache - just finish with callback
    430             if (!url || !bypassCache && $.data(a, 'cache.tabs')) {
    431                 callback();
    432                 return;
    433             }
    434 
    435             // load remote from here on
    436            
    437             var inner = function(parent) {
    438                 var $parent = $(parent), $inner = $parent.find('*:last');
    439                 return $inner.length && $inner || $parent;
    440             };
    441             var cleanup = function() {
    442                 self.$tabs.filter('.' + o.loadingClass).removeClass(o.loadingClass)
    443                             .each(function() {
    444                                 if (o.spinner)
    445                                     inner(this).parent().html(inner(this).data('label.tabs'));
    446                             });
    447                 self.xhr = null;
    448             };
    449            
    450             if (o.spinner) {
    451                 var label = inner(a).html();
    452                 inner(a).wrapInner('<em></em>')
    453                     .find('em').data('label.tabs', label).html(o.spinner);
    454             }
    455 
    456             var ajaxOptions = $.extend({}, o.ajaxOptions, {
    457                 url: url,
    458                 success: function(r, s) {
    459                     $(a.hash).html(r);
    460                     cleanup();
    461                    
    462                     if (o.cache)
    463                         $.data(a, 'cache.tabs', true); // if loaded once do not load them again
    464 
    465                     // callbacks
    466                     $(self.element).triggerHandler('tabsload',
    467                         [self.ui(self.$tabs[index], self.$panels[index])], o.load
    468                     );
    469                     o.ajaxOptions.success && o.ajaxOptions.success(r, s);
    470                    
    471                     // This callback is required because the switch has to take
    472                     // place after loading has completed. Call last in order to
    473                     // fire load before show callback...
    474                     callback();
    475                 }
    476             });
    477             if (this.xhr) {
    478                 // terminate pending requests from other tabs and restore tab label
    479                 this.xhr.abort();
    480                 cleanup();
    481             }
    482             $a.addClass(o.loadingClass);
    483             setTimeout(function() { // timeout is again required in IE, "wait" for id being restored
    484                 self.xhr = $.ajax(ajaxOptions);
    485             }, 0);
    486 
    487         },
    488         url: function(index, url) {
    489             this.$tabs.eq(index).removeData('cache.tabs').data('load.tabs', url);
    490         },
    491         destroy: function() {
    492             var o = this.options;
    493             this.element.unbind('.tabs')
    494                 .removeClass(o.navClass).removeData('tabs');
    495             this.$tabs.each(function() {
    496                 var href = $.data(this, 'href.tabs');
    497                 if (href)
    498                     this.href = href;
    499                 var $this = $(this).unbind('.tabs');
    500                 $.each(['href', 'load', 'cache'], function(i, prefix) {
    501                     $this.removeData(prefix + '.tabs');
    502                 });
    503             });
    504             this.$lis.add(this.$panels).each(function() {
    505                 if ($.data(this, 'destroy.tabs'))
    506                     $(this).remove();
    507                 else
    508                     $(this).removeClass([o.selectedClass, o.unselectClass,
    509                         o.disabledClass, o.panelClass, o.hideClass].join(' '));
    510             });
    511         }
    512     });
    513    
    514     $.ui.tabs.defaults = {
    515         // basic setup
    516         unselect: false,
    517         event: 'click',
    518         disabled: [],
    519         cookie: null, // e.g. { expires: 7, path: '/', domain: 'jquery.com', secure: true }
    520         // TODO history: false,
    521 
    522         // Ajax
    523         spinner: 'Loading&#8230;',
    524         cache: false,
    525         idPrefix: 'ui-tabs-',
    526         ajaxOptions: {},
    527 
    528         // animations
    529         fx: null, // e.g. { height: 'toggle', opacity: 'toggle', duration: 200 }
    530 
    531         // templates
    532         tabTemplate: '<li><a href="#{href}"><span>#{label}</span></a></li>',
    533         panelTemplate: '<div></div>',
    534 
    535         // CSS classes
    536         navClass: 'ui-tabs-nav',
    537         selectedClass: 'ui-tabs-selected',
    538         unselectClass: 'ui-tabs-unselect',
    539         disabledClass: 'ui-tabs-disabled',
    540         panelClass: 'ui-tabs-panel',
    541         hideClass: 'ui-tabs-hide',
    542         loadingClass: 'ui-tabs-loading'
    543     };
    544    
    545     $.ui.tabs.getter = "length";
     503        });
     504        this.$lis.add(this.$panels).each(function() {
     505            if ($.data(this, 'destroy.tabs'))
     506                $(this).remove();
     507            else
     508                $(this).removeClass([o.selectedClass, o.unselectClass,
     509                    o.disabledClass, o.panelClass, o.hideClass].join(' '));
     510        });
     511    }
     512});
     513
     514$.ui.tabs.defaults = {
     515    // basic setup
     516    unselect: false,
     517    event: 'click',
     518    disabled: [],
     519    cookie: null, // e.g. { expires: 7, path: '/', domain: 'jquery.com', secure: true }
     520    // TODO history: false,
     521
     522    // Ajax
     523    spinner: 'Loading&#8230;',
     524    cache: false,
     525    idPrefix: 'ui-tabs-',
     526    ajaxOptions: {},
     527
     528    // animations
     529    fx: null, // e.g. { height: 'toggle', opacity: 'toggle', duration: 200 }
     530
     531    // templates
     532    tabTemplate: '<li><a href="#{href}"><span>#{label}</span></a></li>',
     533    panelTemplate: '<div></div>',
     534
     535    // CSS classes
     536    navClass: 'ui-tabs-nav',
     537    selectedClass: 'ui-tabs-selected',
     538    unselectClass: 'ui-tabs-unselect',
     539    disabledClass: 'ui-tabs-disabled',
     540    panelClass: 'ui-tabs-panel',
     541    hideClass: 'ui-tabs-hide',
     542    loadingClass: 'ui-tabs-loading'
     543};
     544
     545$.ui.tabs.getter = "length";
    546546
    547547/*
     
    549549 */
    550550
    551     /*
    552     * Rotate
    553     */
    554     $.extend($.ui.tabs.prototype, {
    555         rotation: null,
    556         rotate: function(ms, continuing) {
    557            
    558             continuing = continuing || false;
    559            
    560             var self = this, t = this.options.selected;
    561            
    562             function start() {
    563                 self.rotation = setInterval(function() {
    564                     t = ++t < self.$tabs.length ? t : 0;
    565                     self.select(t);
    566                 }, ms);
    567             }
    568            
    569             function stop(e) {
    570                 if (!e || e.clientX) { // only in case of a true click
    571                     clearInterval(self.rotation);
    572                 }
    573             }
    574            
    575             // start interval
    576             if (ms) {
    577                 start();
    578                 if (!continuing)
    579                     this.$tabs.bind(this.options.event, stop);
    580                 else
    581                     this.$tabs.bind(this.options.event, function() {
    582                         stop();
    583                         t = self.options.selected;
    584                         start();
    585                     });
    586             }
    587             // stop interval
    588             else {
    589                 stop();
    590                 this.$tabs.unbind(this.options.event, stop);
    591             }
    592         }
    593     });
     551/*
     552 * Rotate
     553 */
     554$.extend($.ui.tabs.prototype, {
     555    rotation: null,
     556    rotate: function(ms, continuing) {
     557       
     558        continuing = continuing || false;
     559       
     560        var self = this, t = this.options.selected;
     561       
     562        function start() {
     563            self.rotation = setInterval(function() {
     564                t = ++t < self.$tabs.length ? t : 0;
     565                self.select(t);
     566            }, ms);
     567        }
     568       
     569        function stop(e) {
     570            if (!e || e.clientX) { // only in case of a true click
     571                clearInterval(self.rotation);
     572            }
     573        }
     574       
     575        // start interval
     576        if (ms) {
     577            start();
     578            if (!continuing)
     579                this.$tabs.bind(this.options.event, stop);
     580            else
     581                this.$tabs.bind(this.options.event, function() {
     582                    stop();
     583                    t = self.options.selected;
     584                    start();
     585                });
     586        }
     587        // stop interval
     588        else {
     589            stop();
     590            this.$tabs.unbind(this.options.event, stop);
     591        }
     592    }
     593});
    594594
    595595})(jQuery);
Note: See TracChangeset for help on using the changeset viewer.