Ticket #1749: ticketdeleteplugin_0.11_working.patch

File ticketdeleteplugin_0.11_working.patch, 62.1 kB (added by daniel, 5 months ago)

Patch to implement the button with a ITemplateStreamFilter and make the plugin work fully

  • ticketdelete/web_ui.py

    old new  
    11# Ticket deleting plugins 
    22 
    3 from trac import __version__ as TRAC_VERSION 
     3from genshi.builder import tag 
     4from genshi.filters import Transformer 
     5from genshi.filters.transform import StreamBuffer 
     6 
    47from trac import ticket 
    58from trac.admin.api import IAdminPanelProvider 
    69from trac.core import * 
    710from trac.ticket.model import Ticket 
    8 from trac.web.api import IRequestFilter 
     11from trac.web.api import ITemplateStreamFilter 
    912from trac.web.chrome import ITemplateProvider, add_script, add_stylesheet 
    1013from trac.util import sorted 
    1114from trac.util.datefmt import to_datetime, utc, to_timestamp 
     
    2124class TicketDeletePlugin(Component): 
    2225    """A small ticket deletion plugin.""" 
    2326     
    24     implements(ITemplateProvider, IAdminPanelProvider, IRequestFilter) 
     27    implements(ITemplateProvider, IAdminPanelProvider, ITemplateStreamFilter) 
    2528 
    26     # IRequestFilter methods 
    27     def pre_process_request(self, req, handler): 
    28         return handler 
     29    # ITemplateStreamFilter methods      
     30    def filter_stream(self, req, method, filename, stream, data): 
     31        if filename == 'ticket.html' and req.authname != 'anonymous': 
     32            ticket = data.get('ticket') 
     33            if 'TICKET_ADMIN' in req.perm(ticket.resource): 
     34                self.log.debug("TicketDeletePlugin adding 'Delete' links for ticket %s" % ticket.id) 
     35                add_stylesheet(req, 'ticketdelete/ticketdelete.css') 
     36                buffer = StreamBuffer() 
     37                 
     38                def insert_delete_link(): 
     39                    try: 
     40                        cnum = list(buffer)[0][1][1][0][1] 
     41                        if cnum == "description": 
     42                            return tag.a("Delete", href=("../admin/ticket/delete/%s" % ticket.id)) 
     43                        else: 
     44                            return tag.a("Delete", href=("../admin/ticket/comments/%s?cnum=%s" % (ticket.id, cnum))) 
     45                    except: 
     46                        return "" 
    2947 
    30     def post_process_request(self, req, template, content_type): 
    31         if template == 'ticket.html' and 'TICKET_ADMIN' in req.perm: 
    32             add_script(req, 'ticketdelete/ticketdelete.js') 
    33             add_stylesheet(req, 'ticketdelete/ticketdelete.css') 
    34         return template, content_type 
    35   
     48                filter = Transformer("//div[@class='inlinebuttons']/input[@name='replyto']/@value") 
     49                return stream | filter.copy(buffer).end() \ 
     50                              .select("//div[@class='inlinebuttons']") \ 
     51                              .append(insert_delete_link) 
     52        return stream 
     53 
     54 
    3655    # IAdminPanelProvider methods 
    3756    def get_admin_panels(self, req): 
    3857        if 'TICKET_ADMIN' in req.perm: 
     
    4463         
    4564        data = {} 
    4665 
    47         data['href'] = req.href('admin', cat, page) 
     66        data['href'] = req.args.has_key('referrer') and req.args['referrer'] or req.get_header('Referer') or req.href('admin', cat, page) 
    4867        data['page'] = page 
    4968        data['redir'] = 1 
    5069        data['changes'] = {} 
    5170        data['id'] = 0 
     71         
     72        deleted = False 
    5273 
    5374        if req.method == 'POST': 
    5475            if page == 'delete': 
    5576                if 'ticketid' in req.args and 'ticketid2' in req.args: 
    5677                    if req.args.get('ticketid') == req.args.get('ticketid2'): 
    57                         t = self._validate(req, req.args.get('ticketid')
     78                        t = self._validate(req, req.args.get('ticketid'), data
    5879                        if t: 
    5980                            self._delete_ticket(t.id) 
    6081                            data['message'] = "Ticket #%s has been deleted." % t.id 
     82                            deleted = True 
    6183                             
    6284                    else: 
    6385                        data['message'] = "The two IDs did not match. Please try again." 
     
    6587                if 'ticketid' in req.args: 
    6688                    req.redirect(req.href.admin(cat, page, req.args.get('ticketid'))) 
    6789                else: 
    68                     t = self._validate(req, path_info
     90                    t = self._validate(req, path_info, data
    6991                    if t: 
    70                         data['href'] = req.href('admin', cat, page, path_info) 
     92                        data['href'] = req.args.has_key('referrer') and req.args['referrer'] or req.href('admin', cat, page, path_info) 
    7193                         
    7294                        deletions = None 
    7395                        if "multidelete" in req.args: 
     
    86108                                    data['redir'] = 0 
    87109                     
    88110                 
    89         if path_info
    90             t = self._validate(req, path_info
     111        if path_info and not deleted
     112            t = self._validate(req, path_info, data
    91113            if t: 
    92114                if page == 'comments': 
    93115                    try: 
    94                         selected = int(req.args.get('cnum')) - 1 
     116                        selected = int(req.args.get('cnum')) 
    95117                    except (TypeError, ValueError): 
    96118                        selected = None 
    97119 
    98120                    ticket_data = {} 
    99121                    for time, author, field, oldvalue, newvalue, perm in t.get_changelog(): 
    100                         c_data = ticket_data.setdefault(to_timestamp(time), {}) 
     122                        ts = to_timestamp(time) 
     123                        c_data = ticket_data.setdefault(ts, {}) 
    101124                        c_data.setdefault('fields', {})[field] = {'old': oldvalue, 'new': newvalue} 
    102125                        c_data['author'] = author 
    103126                        # FIXME: The datetime handling is not working - enable 
    104127                        # for traceback 
    105128                        c_data['prettytime'] = strftime('%a, %d %b %Y %H:%M:%S',time.timetuple()) 
     129                        c_data['ts'] = ts 
    106130                     
    107131                    # Check the boxes next to change number `selected` 
    108132                    time_list = list(sorted(ticket_data.iterkeys())) 
    109                     if selected is not None and selected < len(time_list): 
    110                         ticket_data[time_list[selected]]['checked'] = True 
    111                     data['changes'] = ticket_data 
     133                    #selected isn't necessarily the same as the index because of temporary changes 
     134                    changes = [ticket_data[time_list[i]] for i in range(0, len(time_list))] 
     135                    if selected is not None: 
     136                        for change in changes: 
     137                            if change['fields'].has_key('comment') and change['fields']['comment']['old'] == str(selected): 
     138                                change['checked'] = True 
     139                                break 
     140                     
     141                    data['changes'] = changes 
    112142                elif page == 'delete': 
    113143                    data['id'] = t.id 
    114144  
     
    139169        return [('ticketdelete', resource_filename(__name__, 'htdocs'))] 
    140170 
    141171    # Internal methods 
    142     def _get_trac_version(self): 
    143         md = re.match('(\d+)\.(\d+)',TRAC_VERSION) 
    144         if md: 
    145             return (int(md.group(1)),int(md.group(2))) 
    146         else: 
    147             return (0,0) 
    148172 
    149     def _validate(self, req, arg): 
     173    def _validate(self, req, arg, data): 
    150174        """Validate that arg is a string containing a valid ticket ID.""" 
    151175        try: 
    152176            id = int(arg) 
     
    161185     
    162186    def _delete_ticket(self, id): 
    163187        """Delete the given ticket ID.""" 
    164         major, minor = self._get_trac_version() 
    165         if major > 0 or minor >= 10: 
    166             ticket = Ticket(self.env,id) 
    167             ticket.delete() 
    168         else: 
    169             db = self.env.get_db_cnx() 
    170             cursor = db.cursor() 
    171             cursor.execute("DELETE FROM ticket WHERE id=%s", (id,)) 
    172             cursor.execute("DELETE FROM ticket_change WHERE ticket=%s", (id,)) 
    173             cursor.execute("DELETE FROM attachment WHERE type='ticket' and id=%s", (id,)) 
    174             cursor.execute("DELETE FROM ticket_custom WHERE ticket=%s", (id,)) 
    175             db.commit() 
     188        ticket = Ticket(self.env,id) 
     189        ticket.delete() 
    176190             
    177191    def _delete_change(self, id, ts, field=None): 
    178192        """Delete the change on the given ticket at the given timestamp.""" 
     
    196210                self._delete_change(id, ts, field) 
    197211             
    198212        db.commit() 
     213 
  • ticketdelete/htdocs/ticketdelete.js

    old new  
    1 $(document).ready(function() { 
    2     var ticket = /\/ticket\/(\d+)/.exec(document.location)[1]; 
    3     var delete_link = '<a href="../admin/ticket/delete/'+ticket+'">Delete</a>'; 
    4     var ticket_buttons = $('#ticket .inlinebuttons')[0]; 
    5     if (ticket_buttons) { 
    6         $(ticket_buttons).append(delete_link); 
    7     } else { 
    8         $('#ticket table.properties').after('<div class="description"><h3><span class="inlinebuttons">'+delete_link+'</span>&nbsp;</h3></div>'); 
    9     } 
    10     $('#changelog h3').each(function() { 
    11         var comment = $('input[@name=replyto]', this)[0]; 
    12         if (comment) { 
    13             comment = comment.value; 
    14             $('.inlinebuttons', this).append('<a href="../admin/ticket/comments/'+ticket+'?cnum='+comment+'">Delete</a>'); 
    15         } 
    16     }); 
    17 }); 
  • ticketdelete/htdocs/jquery.js

    old new  
    1 /* 
    2  * jQuery - New Wave Javascript 
    3  * 
    4  * Copyright (c) 2006 John Resig (jquery.com) 
    5  * Dual licensed under the MIT (MIT-LICENSE.txt)  
    6  * and GPL (GPL-LICENSE.txt) licenses. 
    7  * 
    8  * $Date: 2006-08-31 13:26:31 -0400 (Thu, 31 Aug 2006) $ 
    9  * $Rev: 249 $ 
    10  */ 
    11  
    12 // Global undefined variable 
    13 window.undefined = window.undefined; 
    14 function jQuery(a,c) { 
    15  
    16         // Shortcut for document ready (because $(document).each() is silly) 
    17         if ( a && a.constructor == Function && jQuery.fn.ready ) 
    18                 return jQuery(document).ready(a); 
    19  
    20         // Make sure that a selection was provided 
    21         a = a || jQuery.context || document; 
    22  
    23         // Watch for when a jQuery object is passed as the selector 
    24         if ( a.jquery ) 
    25                 return $( jQuery.merge( a, [] ) ); 
    26  
    27         // Watch for when a jQuery object is passed at the context 
    28         if ( c && c.jquery ) 
    29                 return $( c ).find(a); 
    30          
    31         // If the context is global, return a new object 
    32         if ( window == this ) 
    33                 return new jQuery(a,c); 
    34  
    35         // Handle HTML strings 
    36         var m = /^[^<]*(<.+>)[^>]*$/.exec(a); 
    37         if ( m ) a = jQuery.clean( [ m[1] ] ); 
    38  
    39         // Watch for when an array is passed in 
    40         this.get( a.constructor == Array || a.length && !a.nodeType && a[0] != undefined && a[0].nodeType ? 
    41                 // Assume that it is an array of DOM Elements 
    42                 jQuery.merge( a, [] ) : 
    43  
    44                 // Find the matching elements and save them for later 
    45                 jQuery.find( a, c ) ); 
    46  
    47   // See if an extra function was provided 
    48         var fn = arguments[ arguments.length - 1 ]; 
    49          
    50         // If so, execute it in context 
    51         if ( fn && fn.constructor == Function ) 
    52                 this.each(fn); 
    53 } 
    54  
    55 // Map over the $ in case of overwrite 
    56 if ( typeof $ != "undefined" ) 
    57         jQuery._$ = $; 
    58  
    59 // Map the jQuery namespace to the '$' one 
    60 var $ = jQuery; 
    61  
    62 jQuery.fn = jQuery.prototype = { 
    63         jquery: "$Rev: 249 $", 
    64  
    65         size: function() { 
    66                 return this.length; 
    67         }, 
    68  
    69         get: function( num ) { 
    70                 // Watch for when an array (of elements) is passed in 
    71                 if ( num && num.constructor == Array ) { 
    72  
    73                         // Use a tricky hack to make the jQuery object 
    74                         // look and feel like an array 
    75                         this.length = 0; 
    76                         [].push.apply( this, num ); 
    77                          
    78                         return this; 
    79                 } else 
    80                         return num == undefined ? 
    81  
    82                                 // Return a 'clean' array 
    83                                 jQuery.map( this, function(a){ return a } ) : 
    84  
    85                                 // Return just the object 
    86                                 this[num]; 
    87         }, 
    88         each: function( fn, args ) { 
    89                 return jQuery.each( this, fn, args ); 
    90         }, 
    91  
    92         index: function( obj ) { 
    93                 var pos = -1; 
    94                 this.each(function(i){ 
    95                         if ( this == obj ) pos = i; 
    96                 }); 
    97                 return pos; 
    98         }, 
    99  
    100         attr: function( key, value, type ) { 
    101                 // Check to see if we're setting style values 
    102                 return key.constructor != String || value != undefined ? 
    103                         this.each(function(){ 
    104                                 // See if we're setting a hash of styles 
    105                                 if ( value == undefined ) 
    106                                         // Set all the styles 
    107                                         for ( var prop in key ) 
    108                                                 jQuery.attr( 
    109                                                         type ? this.style : this, 
    110                                                         prop, key[prop] 
    111                                                 ); 
    112                                  
    113                                 // See if we're setting a single key/value style 
    114                                 else 
    115                                         jQuery.attr( 
    116                                                 type ? this.style : this, 
    117                                                 key, value 
    118                                         ); 
    119                         }) : 
    120                          
    121                         // Look for the case where we're accessing a style value 
    122                         jQuery[ type || "attr" ]( this[0], key ); 
    123         }, 
    124  
    125         css: function( key, value ) { 
    126                 return this.attr( key, value, "curCSS" ); 
    127         }, 
    128         text: function(e) { 
    129                 e = e || this; 
    130                 var t = ""; 
    131                 for ( var j = 0; j < e.length; j++ ) { 
    132                         var r = e[j].childNodes; 
    133                         for ( var i = 0; i < r.length; i++ ) 
    134                                 if ( r[i].nodeType != 8 ) 
    135                                         t += r[i].nodeType != 1 ? 
    136                                                 r[i].nodeValue : jQuery.fn.text([ r[i] ]); 
    137                 } 
    138                 return t; 
    139         }, 
    140         wrap: function() { 
    141                 // The elements to wrap the target around 
    142                 var a = jQuery.clean(arguments); 
    143                  
    144                 // Wrap each of the matched elements individually 
    145                 return this.each(function(){ 
    146                         // Clone the structure that we're using to wrap 
    147                         var b = a[0].cloneNode(true); 
    148                          
    149                         // Insert it before the element to be wrapped 
    150                         this.parentNode.insertBefore( b, this ); 
    151                          
    152                         // Find he deepest point in the wrap structure 
    153                         while ( b.firstChild ) 
    154                                 b = b.firstChild; 
    155                          
    156                         // Move the matched element to within the wrap structure 
    157                         b.appendChild( this ); 
    158                 }); 
    159         }, 
    160         append: function() { 
    161                 return this.domManip(arguments, true, 1, function(a){ 
    162                         this.appendChild( a ); 
    163                 }); 
    164         }, 
    165         prepend: function() { 
    166                 return this.domManip(arguments, true, -1, function(a){ 
    167                         this.insertBefore( a, this.firstChild ); 
    168                 }); 
    169         }, 
    170         before: function() { 
    171                 return this.domManip(arguments, false, 1, function(a){ 
    172                         this.parentNode.insertBefore( a, this ); 
    173                 }); 
    174         }, 
    175         after: function() { 
    176                 return this.domManip(arguments, false, -1, function(a){ 
    177                         this.parentNode.insertBefore( a, this.nextSibling ); 
    178                 }); 
    179         }, 
    180         end: function() { 
    181                 return this.get( this.stack.pop() ); 
    182         }, 
    183         find: function(t) { 
    184                 return this.pushStack( jQuery.map( this, function(a){ 
    185                         return jQuery.find(t,a); 
    186                 }), arguments ); 
    187         }, 
    188  
    189         clone: function(deep) { 
    190                 return this.pushStack( jQuery.map( this, function(a){ 
    191                         return a.cloneNode( deep != undefined ? deep : true ); 
    192                 }), arguments ); 
    193         }, 
    194  
    195         filter: function(t) { 
    196                 return this.pushStack( 
    197                         t.constructor == Array && 
    198                         jQuery.map(this,function(a){ 
    199                                 for ( var i = 0; i < t.length; i++ ) 
    200                                         if ( jQuery.filter(t[i],[a]).r.length ) 
    201                                                 return a; 
    202                         }) || 
    203  
    204                         t.constructor == Boolean && 
    205                         ( t ? this.get() : [] ) || 
    206  
    207                         t.constructor == Function && 
    208                         jQuery.grep( this, t ) || 
    209  
    210                         jQuery.filter(t,this).r, arguments ); 
    211         }, 
    212  
    213         not: function(t) { 
    214                 return this.pushStack( t.constructor == String ? 
    215                         jQuery.filter(t,this,false).r : 
    216                         jQuery.grep(this,function(a){ return a != t; }), arguments ); 
    217         }, 
    218  
    219         add: function(t) { 
    220                 return this.pushStack( jQuery.merge( this, t.constructor == String ? 
    221                         jQuery.find(t) : t.constructor == Array ? t : [t] ), arguments ); 
    222         }, 
    223         is: function(expr) { 
    224                 return expr ? jQuery.filter(expr,this).r.length > 0 : this.length > 0; 
    225         }, 
    226         domManip: function(args, table, dir, fn){ 
    227                 var clone = this.size() > 1; 
    228                 var a = jQuery.clean(args); 
    229                  
    230                 return this.each(function(){ 
    231                         var obj = this; 
    232                          
    233                         if ( table && this.nodeName == "TABLE" && a[0].nodeName != "THEAD" ) { 
    234                                 var tbody = this.getElementsByTagName("tbody"); 
    235  
    236                                 if ( !tbody.length ) { 
    237                                         obj = document.createElement("tbody"); 
    238                                         this.appendChild( obj ); 
    239                                 } else 
    240                                         obj = tbody[0]; 
    241                         } 
    242  
    243                         for ( var i = ( dir < 0 ? a.length - 1 : 0 ); 
    244                                 i != ( dir < 0 ? dir : a.length ); i += dir ) { 
    245                                         fn.apply( obj, [ clone ? a[i].cloneNode(true) : a[i] ] ); 
    246                         } 
    247                 }); 
    248         }, 
    249         pushStack: function(a,args) { 
    250                 var fn = args && args[args.length-1]; 
    251  
    252                 if ( !fn || fn.constructor != Function ) { 
    253                         if ( !this.stack ) this.stack = []; 
    254                         this.stack.push( this.get() ); 
    255                         this.get( a ); 
    256                 } else { 
    257                         var old = this.get(); 
    258                         this.get( a ); 
    259                         if ( fn.constructor == Function ) 
    260                                 return this.each( fn ); 
    261                         this.get( old ); 
    262                 } 
    263  
    264                 return this; 
    265         } 
    266 }; 
    267  
    268 jQuery.extend = jQuery.fn.extend = function(obj,prop) { 
    269         if ( !prop ) { prop = obj; obj = this; } 
    270         for ( var i in prop ) obj[i] = prop[i]; 
    271         return obj; 
    272 }; 
    273  
    274 jQuery.extend({ 
    275         init: function(){ 
    276                 jQuery.initDone = true; 
    277                  
    278                 jQuery.each( jQuery.macros.axis, function(i,n){ 
    279                         jQuery.fn[ i ] = function(a) { 
    280                                 var ret = jQuery.map(this,n); 
    281                                 if ( a && a.constructor == String ) 
    282                                         ret = jQuery.filter(a,ret).r; 
    283                                 return this.pushStack( ret, arguments ); 
    284                         }; 
    285                 }); 
    286                  
    287                 jQuery.each( jQuery.macros.to, function(i,n){ 
    288                         jQuery.fn[ i ] = function(){ 
    289                                 var a = arguments; 
    290                                 return this.each(function(){ 
    291                                         for ( var j = 0; j < a.length; j++ ) 
    292                                                 $(a[j])[n]( this ); 
    293                                 }); 
    294                         }; 
    295                 }); 
    296                  
    297                 jQuery.each( jQuery.macros.each, function(i,n){ 
    298                         jQuery.fn[ i ] = function() { 
    299                                 return this.each( n, arguments ); 
    300                         }; 
    301                 }); 
    302  
    303                 jQuery.each( jQuery.macros.filter, function(i,n){ 
    304                         jQuery.fn[ n ] = function(num,fn) { 
    305                                 return this.filter( ":" + n + "(" + num + ")", fn ); 
    306                         }; 
    307                 }); 
    308                  
    309                 jQuery.each( jQuery.macros.attr, function(i,n){ 
    310                         n = n || i; 
    311                         jQuery.fn[ i ] = function(h) { 
    312                                 return h == undefined ? 
    313                                         this.length ? this[0][n] : null : 
    314                                         this.attr( n, h ); 
    315                         }; 
    316                 }); 
    317          
    318                 jQuery.each( jQuery.macros.css, function(i,n){ 
    319                         jQuery.fn[ n ] = function(h) { 
    320                                 return h == undefined ? 
    321                                         ( this.length ? jQuery.css( this[0], n ) : null ) : 
    322                                         this.css( n, h ); 
    323                         }; 
    324                 }); 
    325          
    326         }, 
    327         each: function( obj, fn, args ) { 
    328                 if ( obj.length == undefined ) 
    329                         for ( var i in obj ) 
    330                                 fn.apply( obj[i], args || [i, obj[i]] ); 
    331                 else 
    332                         for ( var i = 0; i < obj.length; i++ ) 
    333                                 fn.apply( obj[i], args || [i, obj[i]] ); 
    334                 return obj; 
    335         }, 
    336          
    337         className: { 
    338                 add: function(o,c){ 
    339                         if (jQuery.className.has(o,c)) return; 
    340                         o.className += ( o.className ? " " : "" ) + c; 
    341                 }, 
    342                 remove: function(o,c){ 
    343                         o.className = !c ? "" : 
    344                                 o.className.replace( 
    345                                         new RegExp("(^|\\s*\\b[^-])"+c+"($|\\b(?=[^-]))", "g"), ""); 
    346                 }, 
    347                 has: function(e,a) { 
    348                         if ( e.className != undefined ) 
    349                                 e = e.className; 
    350                         return new RegExp("(^|\\s)" + a + "(\\s|$)").test(e); 
    351                 } 
    352         }, 
    353         swap: function(e,o,f) { 
    354                 for ( var i in o ) { 
    355                         e.style["old"+i] = e.style[i]; 
    356                         e.style[i] = o[i]; 
    357                 } 
    358                 f.apply( e, [] ); 
    359                 for ( var i in o ) 
    360                         e.style[i] = e.style["old"+i]; 
    361         }, 
    362          
    363         css: function(e,p) { 
    364                 if ( p == "height" || p == "width" ) { 
    365                         var old = {}, oHeight, oWidth, d = ["Top","Bottom","Right","Left"]; 
    366          
    367                         for ( var i in d ) { 
    368                                 old["padding" + d[i]] = 0; 
    369                                 old["border" + d[i] + "Width"] = 0; 
    370                         } 
    371          
    372                         jQuery.swap( e, old, function() { 
    373                                 if (jQuery.css(e,"display") != "none") { 
    374                                         oHeight = e.offsetHeight; 
    375                                         oWidth = e.offsetWidth; 
    376                                 } else { 
    377                                         e = $(e.cloneNode(true)).css({ 
    378                                                 visibility: "hidden", position: "absolute", display: "block" 
    379                                         }).prependTo("body")[0]; 
    380  
    381                                         oHeight = e.clientHeight; 
    382                                         oWidth = e.clientWidth; 
    383                                          
    384                                         e.parentNode.removeChild(e); 
    385                                 } 
    386                         }); 
    387          
    388                         return p == "height" ? oHeight : oWidth; 
    389                 } else if ( p == "opacity" && jQuery.browser.msie ) 
    390                         return parseFloat( jQuery.curCSS(e,"filter").replace(/[^0-9.]/,"") ) || 1; 
    391  
    392                 return jQuery.curCSS( e, p ); 
    393         }, 
    394  
    395         curCSS: function(elem, prop, force) { 
    396                 var ret; 
    397          
    398                 if (!force && elem.style[prop]) { 
    399  
    400                         ret = elem.style[prop]; 
    401  
    402                 } else if (elem.currentStyle) { 
    403  
    404                         var newProp = prop.replace(/\-(\w)/g,function(m,c){return c.toUpperCase()});  
    405                         ret = elem.currentStyle[prop] || elem.currentStyle[newProp]; 
    406  
    407                 } else if (document.defaultView && document.defaultView.getComputedStyle) { 
    408  
    409                         prop = prop.replace(/([A-Z])/g,"-$1").toLowerCase(); 
    410                         var cur = document.defaultView.getComputedStyle(elem, null); 
    411  
    412                         if ( cur ) 
    413                                 ret = cur.getPropertyValue(prop); 
    414                         else if ( prop == 'display' ) 
    415                                 ret = 'none'; 
    416                         else 
    417                                 jQuery.swap(elem, { display: 'block' }, function() { 
    418                                         ret = document.defaultView.getComputedStyle(this,null).getPropertyValue(prop); 
    419                                 }); 
    420  
    421                 } 
    422                  
    423                 return ret; 
    424         }, 
    425          
    426         clean: function(a) { 
    427                 var r = []; 
    428                 for ( var i = 0; i < a.length; i++ ) { 
    429                         if ( a[i].constructor == String ) { 
    430  
    431                                 var table = ""; 
    432          
    433                                 if ( !a[i].indexOf("<thead") || !a[i].indexOf("<tbody") ) { 
    434                                         table = "thead"; 
    435                                         a[i] = "<table>" + a[i] + "</table>"; 
    436                                 } else if ( !a[i].indexOf("<tr") ) { 
    437                                         table = "tr"; 
    438                                         a[i] = "<table>" + a[i] + "</table>"; 
    439                                 } else if ( !a[i].indexOf("<td") || !a[i].indexOf("<th") ) { 
    440                                         table = "td"; 
    441                                         a[i] = "<table><tbody><tr>" + a[i] + "</tr></tbody></table>"; 
    442                                 } 
    443          
    444                                 var div = document.createElement("div"); 
    445                                 div.innerHTML = a[i]; 
    446          
    447                                 if ( table ) { 
    448                                         div = div.firstChild; 
    449                                         if ( table != "thead" ) div = div.firstChild; 
    450                                         if ( table == "td" ) div = div.firstChild; 
    451                                 } 
    452          
    453                                 for ( var j = 0; j < div.childNodes.length; j++ ) 
    454                                         r.push( div.childNodes[j] ); 
    455                                 } else if ( a[i].jquery || a[i].length && !a[i].nodeType ) 
    456                                         for ( var k = 0; k < a[i].length; k++ ) 
    457                                                 r.push( a[i][k] ); 
    458                                 else if ( a[i] !== null ) 
    459                                         r.push( a[i].nodeType ? a[i] : document.createTextNode(a[i].toString()) ); 
    460                 } 
    461                 return r; 
    462         }, 
    463          
    464         expr: { 
    465                 "": "m[2]== '*'||a.nodeName.toUpperCase()==m[2].toUpperCase()", 
    466                 "#": "a.getAttribute('id')&&a.getAttribute('id')==m[2]", 
    467                 ":": { 
    468                         // Position Checks 
    469                         lt: "i<m[3]-0", 
    470                         gt: "i>m[3]-0", 
    471                         nth: "m[3]-0==i", 
    472                         eq: "m[3]-0==i", 
    473                         first: "i==0", 
    474                         last: "i==r.length-1", 
    475                         even: "i%2==0", 
    476                         odd: "i%2", 
    477                          
    478                         // Child Checks 
    479                         "nth-child": "jQuery.sibling(a,m[3]).cur", 
    480                         "first-child": "jQuery.sibling(a,0).cur", 
    481                         "last-child": "jQuery.sibling(a,0).last", 
    482                         "only-child": "jQuery.sibling(a).length==1", 
    483                          
    484                         // Parent Checks 
    485                         parent: "a.childNodes.length", 
    486                         empty: "!a.childNodes.length", 
    487                          
    488                         // Text Check 
    489                         contains: "(a.innerText||a.innerHTML).indexOf(m[3])>=0", 
    490                          
    491                         // Visibility 
    492                         visible: "a.type!='hidden'&&jQuery.css(a,'display')!='none'&&jQuery.css(a,'visibility')!='hidden'", 
    493                         hidden: "a.type=='hidden'||jQuery.css(a,'display')=='none'||jQuery.css(a,'visibility')=='hidden'", 
    494                          
    495                         // Form elements 
    496                         enabled: "!a.disabled", 
    497                         disabled: "a.disabled", 
    498                         checked: "a.checked", 
    499                         selected: "a.selected" 
    500                 }, 
    501                 ".": "jQuery.className.has(a,m[2])", 
    502                 "@": { 
    503                         "=": "z==m[4]", 
    504                         "!=": "z!=m[4]", 
    505                         "^=": "!z.indexOf(m[4])", 
    506                         "$=": "z.substr(z.length - m[4].length,m[4].length)==m[4]", 
    507                         "*=": "z.indexOf(m[4])>=0", 
    508                         "": "z" 
    509                 }, 
    510                 "[": "jQuery.find(m[2],a).length" 
    511         }, 
    512          
    513         token: [ 
    514                 "\\.\\.|/\\.\\.", "a.parentNode", 
    515                 ">|/", "jQuery.sibling(a.firstChild)", 
    516                 "\\+", "jQuery.sibling(a).next", 
    517                 "~", function(a){ 
    518                         var r = []; 
    519                         var s = jQuery.sibling(a); 
    520                         if ( s.n > 0 ) 
    521                                 for ( var i = s.n; i < s.length; i++ ) 
    522                                         r.push( s[i] ); 
    523                         return r; 
    524                 } 
    525         ], 
    526         find: function( t, context ) { 
    527                 // Make sure that the context is a DOM Element 
    528                 if ( context && context.nodeType == undefined ) 
    529                         context = null; 
    530          
    531                 // Set the correct context (if none is provided) 
    532                 context = context || jQuery.context || document; 
    533          
    534                 if ( t.constructor != String ) return [t]; 
    535          
    536                 if ( !t.indexOf("//") ) { 
    537                         context = context.documentElement; 
    538                         t = t.substr(2,t.length); 
    539                 } else if ( !t.indexOf("/") ) { 
    540                         context = context.documentElement; 
    541                         t = t.substr(1,t.length); 
    542                         // FIX Assume the root element is right :( 
    543                         if ( t.indexOf("/") >= 1 ) 
    544                                 t = t.substr(t.indexOf("/"),t.length); 
    545                 } 
    546          
    547                 var ret = [context]; 
    548                 var done = []; 
    549                 var last = null; 
    550          
    551                 while ( t.length > 0 && last != t ) { 
    552                         var r = []; 
    553                         last = t; 
    554          
    555                         t = jQuery.trim(t).replace( /^\/\//i, "" ); 
    556                          
    557                         var foundToken = false; 
    558                          
    559                         for ( var i = 0; i < jQuery.token.length; i += 2 ) { 
    560                                 if ( foundToken ) continue; 
    561  
    562                                 var re = new RegExp("^(" + jQuery.token[i] + ")"); 
    563                                 var m = re.exec(t); 
    564                                  
    565                                 if ( m ) { 
    566                                         r = ret = jQuery.map( ret, jQuery.token[i+1] ); 
    567                                         t = jQuery.trim( t.replace( re, "" ) ); 
    568                                         foundToken = true; 
    569                                 } 
    570                         } 
    571                          
    572                         if ( !foundToken ) { 
    573                                 if ( !t.indexOf(",") || !t.indexOf("|") ) { 
    574                                         if ( ret[0] == context ) ret.shift(); 
    575                                         done = jQuery.merge( done, ret ); 
    576                                         r = ret = [context]; 
    577                                         t = " " + t.substr(1,t.length); 
    578                                 } else { 
    579                                         var re2 = /^([#.]?)([a-z0-9\\*_-]*)/i; 
    580                                         var m = re2.exec(t); 
    581         &nb