Index: better_exposed_filters_exposed_form_plugin.inc
===================================================================
--- better_exposed_filters_exposed_form_plugin.inc	(revision 8111)
+++ better_exposed_filters_exposed_form_plugin.inc	(working copy)
@@ -23,6 +23,60 @@
   function options_form(&$form, &$form_state) {
     parent::options_form($form, $form_state);
 
+    $bef_options = array();
+
+    /*
+     * Add options for exposed sorts
+     */
+    $exposed = FALSE;
+    foreach ($this->display->handler->get_handlers('sort') as $label => $sort) {
+      if ($sort->options['exposed']) {
+        $exposed = TRUE;
+        break;
+      }
+    }
+    if ($exposed) {
+      $bef_options['sort']['bef_format'] = array(
+        '#type' => 'select',
+        '#title' => t('Display exposed sort options as'),
+        '#default_value' => $this->options['bef']['sort']['bef_format'],
+        '#options' => array(
+          'default' => t('Default select list'),
+          'bef' => t('Radio Buttons'),
+          'bef_links' => t('Links'),
+        ),
+        '#description' => t('Select a format for the exposed sort options.'),
+      );
+      $bef_options['sort']['allow_no_sort'] = array(
+        '#type' => 'checkbox',
+        '#title' => t('Display a "Do not sort" option'),
+        '#default_value' => $this->options['bef']['sort']['allow_no_sort'],
+        '#description' => t('Adds a "do not sort" option to the existing Asc/Desc options'),
+      );
+      $bef_options['sort']['no_sort_label'] = array(
+        '#type' => 'textfield',
+        '#title' => t('"Do not sort" label'),
+        '#default_value' => $this->options['bef']['sort']['no_sort_label'],
+        '#description' => t('Text to use if the above option is checked'),
+      );
+    }
+
+    /*
+     * Add options for exposed pager
+     */
+    if ($this->display->display_options['pager'] && $this->display->display_options['pager']['options']['expose']['items_per_page']) {
+      $bef_options['pager']['bef_format'] = array(
+        '#type' => 'select',
+        '#title' => t('Display exposed pager options as'),
+        '#default_value' => $this->options['bef']['pager']['bef_format'],
+        '#options' => array(
+          'default' => t('Default select list'),
+          'bef' => t('Radio Buttons'),
+          'bef_links' => t('Links'),
+        ),
+        '#description' => t('Select a format for the exposed pager options.'),
+      );
+    }
     /*
      * Changes to the options form need to be mirrored in hook_form_alter in 
      * better_exposed_filters.module to maintain Views 2.x support 
@@ -103,6 +157,99 @@
     parent::exposed_form_alter($form, $form_state);
     
     /*
+     * Handle exposed sort elements
+     */
+    if (isset($this->options['bef']['sort'])) {
+      // Add "Do not sort" option if selected
+      if ($this->options['bef']['sort']['allow_no_sort']) {
+        array_unshift($form['sort_order']['#options'], $this->options['bef']['sort']['no_sort_label']);
+
+        // JavaScript support is needed to remove the sort_by and sort_order form elements on form submission
+        drupal_add_js(drupal_get_path('module', 'better_exposed_filters') .'/better_exposed_filters.js');
+      }
+
+      switch($this->options['bef']['sort']['bef_format']) {
+        case 'bef':
+          if (1 == count($form['sort_by']['#options'])) {
+            // Don't show radio buttons when there's just one option.
+            // NOTE: Views' exposed forms code does some manipulations after form_alter is called, so we can't
+            //       add the markup and hidden form items as proper FAPI elements as they won't get positioned
+            //       correctly. Thus we 'display: none' the select element and add the display text using #suffix.
+            // TODO: should this be theme-able?  Should we move all select-as-radios to theme functions?
+    //        if (empty($form['sort_by']['#suffix'])) {
+    //          $form['sort_by']['#suffix'] = '';
+    //        }
+    //        if (empty($form['sort_by']['#prefix'])) {
+    //          $form['sort_by']['#prefix'] = '';
+    //        }
+            $form['sort_by']['#prefix'] .= '<div class="bef-sortby bef-single-sortby-option">';
+            $form['sort_by']['#suffix'] .= array_pop(array_values($form['sort_by']['#options'])) .'</div>';
+            $form['sort_by']['#attributes'] = array('style' => 'display: none;');
+          }
+          else {
+            $form['sort_by']['#type'] = 'radios';
+            $form['sort_by']['#process'] = array('expand_radios', 'views_process_dependency');
+            $form['sort_by']['#prefix'] = '<div class="bef-sortby bef-select-as-radios">';
+            $form['sort_by']['#suffix'] = '</div>';
+          }
+
+          $form['sort_order']['#type'] = 'radios';
+          $form['sort_order']['#process'] = array('expand_radios', 'views_process_dependency');
+          $form['sort_order']['#prefix'] = '<div class="bef-sortorder bef-select-as-radios">';
+          $form['sort_order']['#suffix'] = '</div>';
+        break;
+        case 'bef_links':
+          if (count($form['sort_by']['#options']) > 1) {
+            $form['sort_by']['#theme'] = 'select_as_links_sort';
+            $form['sort_order']['#sort_order'] = $form['sort_order']['#default_value'] ? $form['sort_order']['#default_value'] : key($form['sort_order']['#options']);
+            $form['sort_by']['#sort_by'] = $form['sort_by']['#default_value'] ? $form['sort_by']['#default_value'] : key($form['sort_by']['#options']);
+            $form['sort_order']['#attributes'] = array('style' => 'display: none;');
+          }
+        break;
+      }
+
+    }
+
+    /*
+     * Handle exposed pager elements
+     */
+    if (isset($this->options['bef']['pager'])) {
+      switch($this->options['bef']['pager']['bef_format']) {
+        case 'bef':
+          if (1 == count($form['items_per_page']['#options'])) {
+            // Don't show radio buttons when there's just one option.
+            // NOTE: Views' exposed forms code does some manipulations after form_alter is called, so we can't
+            //       add the markup and hidden form items as proper FAPI elements as they won't get positioned
+            //       correctly. Thus we 'display: none' the select element and add the display text using #suffix.
+            // TODO: should this be theme-able?  Should we move all select-as-radios to theme functions?
+    //        if (empty($form['items_per_page']['#suffix'])) {
+    //          $form['items_per_page']['#suffix'] = '';
+    //        }
+    //        if (empty($form['items_per_page']['#prefix'])) {
+    //          $form['items_per_page']['#prefix'] = '';
+    //        }
+            $form['items_per_page']['#prefix'] .= '<div class="bef-items-per-page bef-single-sortby-option">';
+            $form['items_per_page']['#suffix'] .= array_pop(array_values($form['items_per_page']['#options'])) .'</div>';
+            $form['items_per_page']['#attributes'] = array('style' => 'display: none;');
+          }
+          else {
+            $form['items_per_page']['#type'] = 'radios';
+            $form['items_per_page']['#process'] = array('expand_radios', 'views_process_dependency');
+            $form['items_per_page']['#prefix'] = '<div class="bef-sortby bef-select-as-radios">';
+            $form['items_per_page']['#suffix'] = '</div>';
+          }
+
+        break;
+        case 'bef_links':
+          if (count($form['items_per_page']['#options']) > 1) {
+            $form['items_per_page']['#theme'] = 'select_as_links_pager';
+            $form['items_per_page']['#items_per_page'] = max($form['items_per_page']['#default_value'], key($form['items_per_page']['#options']));
+          }
+        break;
+      }
+    }
+
+    /*
      * Changes to the display of BEF filters need to be mirrored in hook_form_alter in 
      * better_exposed_filters.module to maintain Views 2.x support 
      */
@@ -133,7 +280,7 @@
             // radio buttons.
             $form[$field_id]['#type'] = 'radios';
             $form[$field_id]['#process'] = array('expand_radios', 'views_process_dependency');
-            $form[$field_id]['#prefix'] = '<div class="bef-select-as-radios">';
+            $form[$field_id]['#prefix'] = '<div class="bef-filter bef-select-as-radios">';
             $form[$field_id]['#suffix'] = '</div>';
 
             if (isset($form[$field_id]['#options']['All'])) {
Index: better_exposed_filters.module
===================================================================
--- better_exposed_filters.module	(revision 8111)
+++ better_exposed_filters.module	(working copy)
@@ -186,6 +186,8 @@
   return array(
     'select_as_checkboxes' => array('function' => 'theme_select_as_checkboxes'),
     'select_as_hidden'     => array('function' => 'theme_select_as_hidden'),
+    'select_as_links_sort'  => array('function' => 'theme_select_as_links_sort'),
+    'select_as_links_pager' => array('function' => 'theme_select_as_links_pager'),
   );
 }
 
@@ -272,6 +274,147 @@
 }
 
 /**
+ * Themes a select element as a series of links (for sort)
+ *
+ * @see theme_select(), http://api.drupal.org/api/function/theme_select/6, http://drupal.org/node/894312
+ * @param object $element - An associative array containing the properties of the element.
+ *                          Properties used: title, value, options, description, extra, multiple, required
+ * @return HTML string representing the form element.
+ */
+function theme_select_as_links_sort($element) {
+  $output = '';
+  $selected_options = (array)$element['#post'][$element['#name']];   // the selected keys from #options
+  if (empty($_REQUEST['sort_by'])) {
+    $selected_options[] = $element['#sort_by'];
+  }
+  $properties = array(
+    'title' => $element['#title'],
+    'description' => $element['#description'],
+    'required' => FALSE,
+  );
+
+  foreach ($element['#options'] as $option => $elem) {
+    // Check for Taxonomy-based filters
+    if (is_object($elem)) {
+      list($option, $elem) = each(array_slice($elem->option, 0, 1, TRUE));
+    }
+
+    /*
+     * Check for optgroups.  Put subelements in the $element_set array and add a group heading.
+     * Otherwise, just add the element to the set
+     */
+    $element_set = array();
+    if (is_array($elem)) {
+      $element_set = $elem;
+    }
+    else {
+      $element_set[$option] = $elem;
+    }
+
+    $form_state = array('storage' => NULL, 'submitted' => FALSE);
+    $form_build_id = $element['#post']['form_build_id'];
+    $form = form_get_cache($form_build_id, $form_state);
+
+    $is_link = TRUE;
+    $links = array();
+    foreach ($element_set as $key => $value) {
+
+      // Custom ID for each hidden field based on the <select>'s original ID
+      $id = form_clean_id($element['#id'] . '-' . $key);
+      if (array_search($key, $selected_options) === FALSE) {
+        define('BEF_SORT_REVERSE', FALSE); // TODO: this option needs some work and testing
+        $is_link = TRUE;
+        if (BEF_SORT_REVERSE) {
+          $is_link = TRUE;
+          if (!$sort_order = better_exposed_filters_get_uri_value('sort_order')) {
+            $sort_order = $element['#sort_order'];
+          }
+          switch ($sort_order) {
+            case 'ASC':
+              $sort_order = 'DESC';
+            break;
+            case 'DESC':
+              $sort_order = 'ASC';
+            break;
+            default:
+              $sort_order = 'ASC';
+          }
+        }
+        $link = $is_link ? better_exposed_filters_replace_uri_value($element['#name'], $key, $value) : $value;
+        $sort_order = NULL;
+
+        // Very similar to theme_hidden (http://api.drupal.org/api/function/theme_hidden/6)
+        $output .= theme('form_element', array_merge($properties, array('#id' => $id)), $link);
+      } else {
+        $is_link = FALSE;
+        $link = $is_link ? better_exposed_filters_replace_uri_value('sort_order', NULL, $value, $is_link) : $value;
+
+        $output .= theme('form_element', array_merge($properties, array('#id' => $id)), $link);
+      }
+    }
+  }
+  return $output;
+}
+
+/**
+ * Themes a select element as a series of links (for pager)
+ *
+ * @see theme_select(), http://api.drupal.org/api/function/theme_select/6, http://drupal.org/node/894312
+ * @param object $element - An associative array containing the properties of the element.
+ *                          Properties used: title, value, options, description, extra, multiple, required
+ * @return HTML string representing the form element.
+ */
+function theme_select_as_links_pager($element) {
+  $output = '';
+  $selected_options = (array)$element['#post'][$element['#name']];   // the selected keys from #options
+  if (empty($_REQUEST['items_per_page'])) {
+    $selected_options[] = $element['#items_per_page'];
+  }
+  $properties = array(
+    'title' => $element['#title'],
+    'description' => $element['#description'],
+    'required' => FALSE,
+  );
+
+  foreach ($element['#options'] as $option => $elem) {
+    // Check for Taxonomy-based filters
+    if (is_object($elem)) {
+      list($option, $elem) = each(array_slice($elem->option, 0, 1, TRUE));
+    }
+
+    /*
+     * Check for optgroups.  Put subelements in the $element_set array and add a group heading.
+     * Otherwise, just add the element to the set
+     */
+    $element_set = array();
+    if (is_array($elem)) {
+      $element_set = $elem;
+    }
+    else {
+      $element_set[$option] = $elem;
+    }
+
+    $links = array();
+    foreach ($element_set as $key => $value) {
+
+      // Custom ID for each hidden field based on the <select>'s original ID
+      $id = form_clean_id($element['#id'] . '-' . $key);
+      if (array_search($key, $selected_options) === FALSE) {
+
+        $link = better_exposed_filters_replace_uri_value($element['#name'], $key, $value);
+
+        // Very similar to theme_hidden (http://api.drupal.org/api/function/theme_hidden/6)
+        $output .= theme('form_element', array_merge($properties, array('#id' => $id)), $link);
+      } else {
+        // $link = better_exposed_filters_replace_uri_value($element['#name'], $sort_order, $value);
+        $output .= theme('form_element', array_merge($properties, array('#id' => $id)), $value);
+      }
+    }
+  }
+  return $output;
+}
+
+/**
  * Themes a select element as a series of hidden fields
  * 
  * @see theme_select(), http://api.drupal.org/api/function/theme_select/6
@@ -344,3 +487,76 @@
   );
 }
 
+
+/**
+ * Replace URI value of selected argument
+ *
+ * @param string $arg name of the argument in URI
+ * @param string $value value of the argument in URI
+ * @return array
+ *    Return base URL, URL-encoded query string
+ *
+ * TODO: Any easier method to change the value of URI argument?
+ */
+function better_exposed_filters_replace_uri_value($arg, $arg_value, $name = NULL, $link = TRUE) {
+  $urllist = parse_url(request_uri());
+  if (strpos($urllist['query'], $arg . '=') !== FALSE) {
+    $keyvalue_list = explode("&",($urllist["query"])); // Explode into key/value array
+    $keyvalue_result = array(); // Store resulting key/value pairs
+    foreach($keyvalue_list as $key => $value) {
+      $keyvalue=explode("=", $value); // Explode each individual key/value into an array
+    
+      if(count($keyvalue) == 2) { // Make sure we have a "key=value" array
+        $keyvalue[1]=urldecode($keyvalue[1]); // Encode the value portion
+
+        if ($keyvalue[0] == $arg) {
+          $keyvalue[1] = $arg_value; // change value of our argument
+        }
+        $keyvalue_result[$keyvalue[0]] = $keyvalue[1]; // Add our key and encoded value into the result
+      } // end: if
+    } // end: foreach
+    // $urllist["query"] = implode("&", $keyvalue_result); // Repopulate our query key with encoded results
+    $urllist["query"] = $keyvalue_result; // assign array of query key/value-pairs without any URL-encoding
+  } else {
+    $urllist['query'] .= !empty($urllist['query']) ? '&' : '';
+    $urllist['query'] .= $arg . '=' . $arg_value;
+  }     
+    $url=(isset($urllist["scheme"])?$urllist["scheme"]."://":"").
+         (isset($urllist["user"])?$urllist["user"].":":"").
+         (isset($urllist["pass"])?$urllist["pass"]."@":"").
+         (isset($urllist["host"])?$urllist["host"]:"").
+         (isset($urllist["port"])?":".$urllist["port"]:"").
+         (isset($urllist["path"])?$urllist["path"]:"").
+         (isset($urllist["query"])?"?".$urllist["query"]:"").
+         (isset($urllist["fragment"])?"#".$urllist["fragment"]:"");
+  return $link ? l($name ? $name : $arg_value, substr($urllist["path"], 1), $urllist) : substr(url($urllist["path"], $urllist), 1);
+}
+
+/**
+ * Get URI value of selected argument
+ *
+ * @param string $arg name of the argument in URI
+ * @return string
+ *    Return value of the argument
+ *
+ * TODO: Any easier method to get the value of URI argument?
+ */
+function better_exposed_filters_get_uri_value($arg) {
+  $res = NULL;
+  $urllist = parse_url(request_uri());
+  if (strpos($urllist['query'], $arg . '=') !== FALSE) {
+    $keyvalue_list = explode("&",($urllist["query"])); // Explode into key/value array
+    foreach($keyvalue_list as $key => $value) {
+      $keyvalue=explode("=", $value); // Explode each individual key/value into an array
+    
+      if(count($keyvalue) == 2) { // Make sure we have a "key=value" array
+
+        if ($keyvalue[0] == $arg) {
+          $res = $keyvalue[1];
+          break;
+        }
+      } // end: if
+    } // end: foreach
+  }
+  return $res;
+}
