=== modified file 'misc/tableselect.js'
--- misc/tableselect.js	2008-12-06 09:01:58 +0000
+++ misc/tableselect.js	2009-01-26 04:52:36 +0000
@@ -14,28 +14,66 @@ Drupal.tableSelect = function() {
 
   // Keep track of the table, which checkbox is checked and alias the settings.
   var table = this, checkboxes, lastChecked;
-  var strings = { 'selectAll': Drupal.t('Select all rows in this table'), 'selectNone': Drupal.t('Deselect all rows in this table') };
-  var updateSelectAll = function(state) {
-    $('th.select-all input:checkbox', table).each(function() {
-      $(this).attr('title', state ? strings.selectNone : strings.selectAll);
-      this.checked = state;
+  var strings = { 'selectAll': Drupal.t('Select all rows in this table'), 'selectAllLabel' : Drupal.t('All'), 'selectNone': Drupal.t('Deselect all rows in this table'), 'selectNoneLabel' : Drupal.t('None'), 'selectInvertLabel' : Drupal.t('Invert') };
+  
+  // Create select operations link
+  select_all_link = $(document.createElement('a'))
+    .addClass('select-operation select-all')
+    .attr('href', '#')
+    .html(strings.selectAllLabel)
+    .click(function(){
+      Drupal.selectOperation('all', checkboxes);
+      return false;
+    });
+  select_none_link = $(document.createElement('a'))
+    .addClass('select-operation select-none')
+    .attr('href', '#')
+    .html(strings.selectNoneLabel)
+    .click(function(){
+      Drupal.selectOperation('none', checkboxes);
+      return false;
+    });
+  select_invert_link = $(document.createElement('a'))
+    .addClass('select-operation select-invert')
+    .attr('href', '#')
+    .html(strings.selectInvertLabel)
+    .click(function(){
+      Drupal.selectOperation('invert', checkboxes);
+      return false;
     });
-  };
-
-  // Find all <th> with class select-all, and insert the check all checkbox.
-  $('th.select-all', table).prepend($('<input type="checkbox" class="form-checkbox" />').attr('title', strings.selectAll)).click(function(event) {
-    if ($(event.target).is('input:checkbox')) {
-      // Loop through all checkboxes and set their state to the select all checkbox' state.
-      checkboxes.each(function() {
-        this.checked = event.target.checked;
-        // Either add or remove the selected class based on the state of the check all checkbox.
-        $(this).parents('tr:first')[ this.checked ? 'addClass' : 'removeClass' ]('selected');
-      });
-      // Update the title and the state of the check all box.
-      updateSelectAll(event.target.checked);
-    }
-  });
 
+  // Append select operations link to the top and bottom of table
+  $(table).before('<div class="select-operations"></span>').after('<div class="select-operations"></span>');
+  $('.select-operations')
+    .append('Select: ')
+    .append(select_all_link)
+    .append(', ')
+    .append(select_none_link)
+    .append(', ')
+    .append(select_invert_link)
+  
+    
+  operations = Drupal.settings.operations;
+  for (i = 0; i < operations.length; i++) {
+    filter = operations[i].filter;
+    link = $(document.createElement('a'))
+      .addClass('select-operation')
+      .addClass(operations[i].class)
+      .attr('href', '#')
+      .html(operations[i].label)
+      .click(function(filter){
+        // Returns function to prevent referencing the same filter variable on click
+          return function() {
+            Drupal.selectOperation('all', checkboxes, filter);
+            return false;
+          }
+        }(filter)
+      );
+    $('.select-operations')
+      .append(', ')
+      .append(link)
+  }
+  
   // For each of the checkboxes within the table.
   checkboxes = $('td input:checkbox', table).click(function(e) {
     // Either add or remove the selected class based on the state of the check all checkbox.
@@ -49,15 +87,42 @@ Drupal.tableSelect = function() {
       Drupal.tableSelectRange($(e.target).parents('tr')[0], $(lastChecked).parents('tr')[0], e.target.checked);
     }
 
-    // If all checkboxes are checked, make sure the select-all one is checked too, otherwise keep unchecked.
-    updateSelectAll((checkboxes.length == $(checkboxes).filter(':checked').length));
-
     // Keep track of the last checked checkbox.
     lastChecked = e.target;
   });
   $(this).addClass('tableSelect-processed');
 };
 
+Drupal.selectOperation = function(operation, checkboxes, filter) {
+  var checked;
+  // If filter is defined, unchecked all checkboxes and filter accordingly
+  if (filter) {
+    checkboxes.each(function() {
+      this.checked = false;
+      $(this).parents('tr:first').removeClass('selected');
+    });
+    
+    checkboxes = $(checkboxes).filter(filter);
+  }
+  // Loop through each checkbox and set the checked status according to the operation
+  checkboxes.each(function() {
+    switch (operation) {
+      case 'all':
+        checked = this.checked = true;
+        break;
+      case 'none':
+        checked = this.checked = false;
+        break;
+      case 'invert':
+        checked = this.checked = !this.checked;
+        break;
+    }
+
+    // Either add or remove the selected class based on the checked status
+    $(this).parents('tr:first')[ checked ? 'addClass' : 'removeClass' ]('selected');
+  });
+}
+
 Drupal.tableSelectRange = function(from, to, state) {
   // We determine the looping mode based on the the order of from and to.
   var mode = from.rowIndex > to.rowIndex ? 'previousSibling' : 'nextSibling';

=== modified file 'modules/node/node.admin.inc'
--- modules/node/node.admin.inc	2009-01-19 11:00:08 +0000
+++ modules/node/node.admin.inc	2009-01-26 04:47:53 +0000
@@ -92,11 +92,13 @@ function node_node_operations() {
       'label' => t('Publish'),
       'callback' => 'node_mass_update',
       'callback arguments' => array('updates' => array('status' => 1)),
+      'select operation' => array('label' => t('Published'), 'class'=> "select-published", 'filter' => '.published'),
     ),
     'unpublish' => array(
       'label' => t('Unpublish'),
       'callback' => 'node_mass_update',
       'callback arguments' => array('updates' => array('status' => 0)),
+      'select operation' => array('label' => t('Unpublished'), 'class' => 'select-unpublished', 'filter' => '.unpublished'),
     ),
     'promote' => array(
       'label' => t('Promote to front page'),
@@ -493,8 +495,15 @@ function node_admin_nodes() {
     '#suffix' => '</div>',
   );
   $options = array();
+  $select_operations = array();
   foreach (module_invoke_all('node_operations') as $operation => $array) {
     $options[$operation] = $array['label'];
+    if (isset($array['select operation'])) {
+      $select_operations[] = $array['select operation'];
+    }
+  }
+  if (count($select_operations)) {
+    drupal_add_js(array('operations' => $select_operations), 'setting');
   }
   $form['options']['operation'] = array(
     '#type' => 'select',
@@ -590,6 +599,16 @@ function theme_node_admin_nodes($form) {
   if ($has_posts) {
     $rows = array();
     foreach (element_children($form['title']) as $key) {
+      $class = '';
+      switch ($form['status'][$key]['#markup']) {
+        case 'published':
+          $class = 'published';
+          break;
+        case 'not published':
+          $class = 'unpublished';
+          break;
+      }
+      $form['nodes'][$key]['#attributes'] = array('class' => $class);
       $row = array();
       $row[] = drupal_render($form['nodes'][$key]);
       $row[] = drupal_render($form['title'][$key]);

