Changeset 8032

Show
Ignore:
Timestamp:
06/02/08 21:01:42 (3 months 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

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • 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�z and J�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&nbs