? .DS_Store
? diff.patch
? engines
? patch.patch
? rewrite_00.patch
? css/.DS_Store
? js/.DS_Store
Index: extesea.api.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/extesea/Attic/extesea.api.php,v
retrieving revision 1.1.2.1
diff -u -p -r1.1.2.1 extesea.api.php
--- extesea.api.php	17 Apr 2009 10:52:02 -0000	1.1.2.1
+++ extesea.api.php	25 Apr 2010 17:13:45 -0000
@@ -8,28 +8,40 @@
 /**
  * Declare predefined search engines.
  *
- * @return
+ * @return array
  *   A hash-flagged array of engines and their properties.
  */
-function hook_extesea_engines_info() {
-  // $lastfm (name), $type (token) and $all, $label and $tag (field values) are
-  // strings for this specific search engine. Dollar signs should be omitted.
+function hook_extesea_engine_info() {
   return array(
-    t('Media') => array(
-      '$lastfm' => array(
-        'title' => t('Last.fm'),
-        'url' => 'http://last.fm/search?m=[type]&q=[extesea]',
-        'fields' => array(
-          '$type' => array(
-            'title' => t('Type'),
-            'values' => array(
-              '$all' => t('Music'),
-              '$label' => t('Label'),
-              '$tag' => t('Tag'),
-            ),
+    'lastfm' => array(
+      '#title' => t('Last.fm'),
+      '#category' => 'media',
+      '#url' => 'http://last.fm/search?m=[type]&q=[extesea]',
+      '#fields' => array(
+        'type' => array(
+          '#title' => t('Type'),
+          '#values' => array(
+            'all' => t('Music'),
+            'label' => t('Label'),
+            'tag' => t('Tag'),
           ),
         ),
       ),
     ),
   );
+}
+
+/**
+ * Declare search engine categories.
+ *
+ * @return array
+ *   Keys are system names and values are human-readable names.
+ */
+function hook_extesea_category_info() {
+  return array(
+    'dictionaries' => t('Dictionaries and encyclopediae'),
+    'search' => t('General search sites'),
+    'media' => t('Media'),
+    'programming' => t('Programming'),
+  );
 }
\ No newline at end of file
Index: extesea.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/extesea/Attic/extesea.install,v
retrieving revision 1.1.2.7
diff -u -p -r1.1.2.7 extesea.install
--- extesea.install	4 Jan 2009 12:04:47 -0000	1.1.2.7
+++ extesea.install	25 Apr 2010 17:13:45 -0000
@@ -10,23 +10,23 @@
  */
 function extesea_schema() {
   return array(
-    'extesea_engines' => array(
-      'description' => t('Custom engines.'),
+    'extesea_engine' => array(
+      'description' => 'Custom engines.',
       'fields' => array(
         'name' => array(
-          'description' => t('The engine name.'),
+          'description' => 'The engine name.',
           'type' => 'varchar',
           'length' => 255,
           'not null' => TRUE,
         ),
         'title' => array(
-          'description' => t('The human readable engine name.'),
+          'description' => 'The human readable engine name.',
           'type' => 'varchar',
           'length' => 255,
           'not null' => TRUE,
         ),
         'url' => array(
-          'description' => t('The submission URL.'),
+          'description' => 'The submission URL.',
           'type' => 'varchar',
           'length' => 255,
           'not null' => TRUE,
@@ -34,45 +34,54 @@ function extesea_schema() {
       ),
       'primary key' => array('name'),
     ),
-    'extesea_blocks' => array(
-      'description' => t('Search blocks.'),
+    'extesea_form' => array(
+      'description' => 'Search forms.',
       'fields' => array(
-        'delta' => array(
-          'description' => t("The block's delta."),
+        'fid' => array(
+          'description' => 'The primary identifier for a form.',
           'type' => 'serial',
+          'unsigned' => TRUE,
           'not null' => TRUE,
         ),
         'title' => array(
-          'description' => t('The block title.'),
+          'description' => 'The block title.',
           'type' => 'varchar',
           'length' => 255,
           'not null' => TRUE,
         ),
+        'description' => array(
+          'description' => 'A description of this {extesea_form}.',
+          'type' => 'text',
+          'size' => 'big',
+          'default' => '',
+        ),
       ),
-      'primary key' => array('delta'),
+      'primary key' => array('fid'),
     ),
-    'extesea_block_engines' => array(
-      'description' => t('Engine data for blocks.'),
+    'extesea_form_engine' => array(
+      'description' => 'Engine data for blocks.',
       'fields' => array(
-        'delta' => array(
-          'description' => t("The block's delta"),
+        'fid' => array(
+          'description' => 'The {extesea_form}.fid this record affects.',
           'type' => 'int',
           'unsigned' => TRUE,
           'not null' => TRUE,
+          'default' => 0,
         ),
         'name' => array(
-          'description' => t('The engine name.'),
+          'description' => 'The search engine name.',
           'type' => 'varchar',
           'length' => 255,
           'not null' => TRUE,
         ),
-        'field_values' => array(
-          'description' => t('Field data.'),
+        'fields' => array(
+          'description' => 'Field data.',
           'type' => 'text',
           'size' => 'medium',
           'not null' => TRUE,
         ),
       ),
+      'primary key' => array('fid', 'name'),
     ),
   );
 }
@@ -89,4 +98,11 @@ function extesea_install() {
  */
 function extesea_uninstall() {
   drupal_uninstall_schema('extesea');
+}
+
+/**
+ * Implementation of hook_enable().
+ */
+function extesea_enable() {
+  drupal_set_message(t('External Search has been enabled. <a href="@form_add">Add a search form</a> to get started.', array('@form_add' => url('admin/build/extesea/form-add'))));
 }
\ No newline at end of file
Index: extesea.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/extesea/Attic/extesea.module,v
retrieving revision 1.1.2.35
diff -u -p -r1.1.2.35 extesea.module
--- extesea.module	17 Apr 2009 19:03:57 -0000	1.1.2.35
+++ extesea.module	25 Apr 2010 17:13:46 -0000
@@ -20,67 +20,107 @@ function extesea_menu() {
     'title' => 'External search',
     'description' => 'Create search forms that submit to external search engines.',
     'access arguments' => array('administer external search'),
-    'page callback' => 'extesea_admin_blocks',
+    'page callback' => 'extesea_form_list',
     'file' => 'extesea.admin.inc',
     'file path' => drupal_get_path('module', 'extesea') . '/includes',
   );
-  $items['admin/build/extesea/blocks'] = array(
-    'title' => 'Forms',
-    'description' => 'Administer available search forms.',
+  $items['admin/build/extesea/form'] = array(
+    'title' => 'List forms',
+    'description' => 'List search forms.',
     'access arguments' => array('administer external search'),
-    'page callback' => 'extesea_admin_blocks',
+    'page callback' => 'extesea_form_list',
     'file' => 'extesea.admin.inc',
     'file path' => drupal_get_path('module', 'extesea') . '/includes',
     'type' => MENU_DEFAULT_LOCAL_TASK,
+    'weight' => 0,
   );
-  $items['admin/build/extesea/engines'] = array(
-    'title' => 'Engines',
-    'description' => 'Administer custom engines.',
+  $items['admin/build/extesea/form-add'] = array(
+    'title' => 'Add form',
+    'description' => 'Add a search form.',
     'access arguments' => array('administer external search'),
-    'page callback' => 'extesea_admin_engines',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('extesea_form_form'),
+    'file' => 'extesea.admin.inc',
+    'file path' => drupal_get_path('module', 'extesea') . '/includes',
+    'type' => MENU_LOCAL_TASK,
+    'weight' => 1,
+  );
+  $items['admin/build/extesea/form-edit/%extesea_form'] = array(
+    'access arguments' => array('administer external search'),
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('extesea_form_form', 4),
+    'file' => 'extesea.admin.inc',
+    'file path' => drupal_get_path('module', 'extesea') . '/includes',
+    'type' => MENU_CALLBACK,
+  );
+  $items['admin/build/extesea/form-delete/%extesea_form'] = array(
+    'access arguments' => array('administer external search'),
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('extesea_form_delete', 4),
+    'file' => 'extesea.admin.inc',
+    'file path' => drupal_get_path('module', 'extesea') . '/includes',
+    'type' => MENU_CALLBACK,
+  );
+  $items['admin/build/extesea/engine'] = array(
+    'title' => 'List search engines',
+    'description' => 'List search engines.',
+    'access arguments' => array('administer external search'),
+    'page callback' => 'extesea_engine_list',
     'file' => 'extesea.admin.inc',
     'file path' => drupal_get_path('module', 'extesea') . '/includes',
     'weight' => 1,
     'type' => MENU_LOCAL_TASK,
+    'weight' => 2,
+  );
+  $items['admin/build/extesea/engine-add'] = array(
+    'title' => 'Add search engine',
+    'description' => 'Add a search engine.',
+    'access arguments' => array('administer external search'),
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('extesea_engine_form'),
+    'file' => 'extesea.admin.inc',
+    'file path' => drupal_get_path('module', 'extesea') . '/includes',
+    'type' => MENU_LOCAL_TASK,
+    'weight' => 3,
+  );
+  $items['admin/build/extesea/engine-edit/%extesea_engine'] = array(
+    'access arguments' => array('administer external search'),
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('extesea_engine_form', 4),
+    'file' => 'extesea.admin.inc',
+    'file path' => drupal_get_path('module', 'extesea') . '/includes',
+    'type' => MENU_CALLBACK,
+  );
+  $items['admin/build/extesea/engine-delete/%extesea_engine'] = array(
+    'access arguments' => array('administer external search'),
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('extesea_engine_delete', 4),
+    'file' => 'extesea.admin.inc',
+    'file path' => drupal_get_path('module', 'extesea') . '/includes',
+    'type' => MENU_CALLBACK,
   );
 
   return $items;
 }
 
 /**
- * Implementation of hook_theme().
- */
-function extesea_theme() {
-  return array(
-    'admin_blocks_list' => array(
-      'arguments' => array(
-        'form' => array(),
-      ),
-    ),
-  );
-}
-
-/**
  * Implementation of hook_block().
  */
 function extesea_block($op = 'list', $delta = 0, $edit = array()) {
-  $extesea_blocks = extesea_block_load_all();
-
   if ($op == 'list') {
+    $extesea_forms = extesea_form_load_all();
     $blocks = array();
-    foreach ($extesea_blocks as $block) {
-      $blocks[$block->delta] = array('info' => t($block->title) . ' (External Search)');
+    foreach ($extesea_forms as $extesea_form) {
+      $blocks[$extesea_form->fid] = array('info' => t($extesea_form->title));
     }
 
     return $blocks;
   }
   elseif ($op == 'view') {
-    if (isset($extesea_blocks[$delta])) {
-      $block = $extesea_blocks[$delta];
-      extesea_block_engines_load($block);
+    if ($extesea_form = extesea_form_load($delta)) {
       return array(
-        'subject' => $block->title,
-        'content' => drupal_get_form('extesea_search_form_' . $block->delta, $block),
+        'subject' => $extesea_form->title,
+        'content' => $extesea_form->view(),
       );
     }
   }
@@ -109,10 +149,10 @@ function extesea_help($path) {
  * Implementation of hook_forms().
  */
 function extesea_forms() {
-  $blocks = extesea_block_load_all();
+  $extesea_forms = extesea_form_load_all();
   $forms = array();
-  foreach ($blocks as $block) {
-    $forms['extesea_search_form_' . $block->delta] = Array(
+  foreach ($extesea_forms as $extesea_form) {
+    $forms['extesea_search_form_' . $extesea_form->fid] = Array(
       'callback' => 'extesea_search_form',
     );
   }
@@ -121,123 +161,78 @@ function extesea_forms() {
 }
 
 /**
- * Return an array with all predefined engines.
- *
- * @return array
+ * Implementation of hook_extesea_category_info().
  */
-function extesea_engines_load_predefined() {
-  static $engines = array();
-
-  if (empty($engines)) {
-    module_load_include('inc', 'extesea', 'includes/extesea.engines');
-    $engines = module_invoke_all('extesea_engine_info');
-  }
-
-  return $engines;
+function extesea_extesea_category_info() {
+  return array(
+    'custom' => t('Custom'),
+    'dictionaries' => t('Dictionaries and encyclopediae'),
+    'media' => t('Media'),
+    'programming' => t('Programming'),
+    'search' => t('General search sites'),
+  );
 }
 
 /**
- * Return an array with all custom engines.
- *
- * @return array
+ * Implementation of hook_extesea_engine_info().
  */
-function extesea_engines_load_custom() {
-  static $engines = array(), $flag = FALSE;
-
-  if (!$flag) {
-    $result = db_query("SELECT * FROM {extesea_engines}");
-    while ($engine = db_fetch_array($result)) {
-      $engines[$engine['name']] = $engine;
-    }
-    $flag = TRUE;
+function extesea_extesea_engine_info() {
+  module_load_include('inc', 'extesea', 'includes/extesea.engines');
+  $engines_info = extesea_predefined_engines_info();
+  $result = db_query("SELECT * FROM {extesea_engine}");
+  while ($engine_info = db_fetch_array($result)) {
+    $engines_info[$engine_info['name']] = array(
+      '#title' => $engine_info['title'],
+      '#category' => 'custom',
+      '#url' => $engine_info['url'],
+    );
   }
 
-  return $engines;
+  return $engines_info;
 }
 
 /**
- * Return an array with all engines.
- *
- * @return array
+ * Return information about all search engines.
  */
-function extesea_engines_load_all() {
-  return array_merge(extesea_engines_load_predefined(), array(t('Custom') => extesea_engines_load_custom()));
-}
+function extesea_engines_info() {
+  static $engines_info = NULL;
 
-/**
- * Load an engine.
- *
- * @param $name
- *   Type: string; The engine's name.
- *
- * @return
- *   Type: object; An engine object.
- */
-function extesea_engine_load($name) {
-  $engines = extesea_engines_load_all();
-  foreach ($engines as $section) {
-    foreach ($section as $engine_name => $engine) {
-      if ($engine_name == $name) {
-        return $engine;
-      }
-    }
+  if (!$engines_info) {
+    extesea_load_include('classes');
+    $engines_info = module_invoke_all('extesea_engine_info');
+    drupal_alter('extesea_engine_info', $engines_info);
   }
-}
 
-/**
- * Load a block.
- */
-function extesea_block_load($delta) {
-  return array_shift(extesea_block_load_all($delta));
+  return $engines_info;
 }
 
 /**
- * Load all blocks.
+ * Return information about a search engine.
  */
-function extesea_block_load_all($delta = NULL) {
-  $where = '';
-  if ($delta) {
-    $where = ' WHERE delta = ' . $delta;
-  }
-  $result = db_query("SELECT * FROM {extesea_blocks} ORDER BY title" . $where);
-  $blocks = array();
-  while ($block = db_fetch_object($result)) {
-    $blocks[$block->delta] = $block;
-  }
+function extesea_engine_info($engine_name) {
+  $engines_info = extesea_engines_info();
 
-  return $blocks;
+  return $engines_info[$engine_name];
 }
 
 /**
- * Load a block's engines into its object.
+ * Return information about all search engine categories.
  */
-function extesea_block_engines_load(&$block) {
-  $engines = extesea_engines_load_all();
-  $result = db_query("SELECT name, field_values FROM {extesea_block_engines} WHERE delta = %d", $block->delta);
-  while ($engine = db_fetch_array($result)) {
-    // NOTE: Use extesea_engine_load(). -Xano.
-    foreach ($engines as $section) {
-      $name = $engine['name'];
-      if (isset($section[$name])) {
-        $block->engines[$name] = array_merge($section[$name], array('field_values' => unserialize($engine['field_values'])));
-      }
-    }
-  }
+function extesea_categories_info() {
+  return module_invoke_all('extesea_category_info');
 }
 
 /**
- * The search form.
+ * Create a search form.
  * 
- * @param $form
- *   Type: array;
- * @param $engine
- *   Type: string; The machine-readable name of the engine to create the form
- *   for.
+ * @param $form array
+ * @param $extesea_form exteseaForm
+ *   The search form to create.
  *
- * @return
- *   Type: array: A Drupal form.
+ * @return array
+ *   A Drupal form.
  */
-function extesea_search_form($form, $block) {
+function extesea_search_form(array $form, exteseaForm $extesea_form) {
   drupal_add_css(drupal_get_path('module', 'extesea') . '/css/extesea.view.css');
   $form = array(
     '#submit' => array('extesea_search_form_submit'),
@@ -326,4 +321,49 @@ function extesea_search_form_submit($for
   }
 
   drupal_goto($location);
+}
+
+/**
+ * Load a form.
+ */
+function extesea_form_load($fid) {
+  extesea_load_include('classes');
+  if ($extesea_form_data = db_fetch_array(db_query("SELECT * FROM {extesea_form} WHERE fid = %d", $fid))) {
+    // Load and process search engines.
+    $engines = array();
+    $result = db_query("SELECT name, fields FROM {extesea_form_engine} WHERE fid = %d", $fid);
+    while ($engine = db_fetch_array($result)) {
+      $engines[$engine['name']] = new exteseaFormEngine($engine['name'], unserialize($engine['fields'], $fid));
+    }
+  }
+
+  $extesea_form = new exteseaForm($engines);
+  $extesea_form->fid = $extesea_form_data['fid'];
+  $extesea_form->title = $extesea_form_data['title'];
+  $extesea_form->description = $extesea_form_data['description'];
+
+  return $extesea_form;
+}
+
+/**
+ * Load all forms.
+ */
+function extesea_form_load_all() {
+  $forms = array();
+  $result = db_query("SELECT fid FROM {extesea_form} ORDER BY title");
+  while ($fid = db_result($result)) {
+    $forms[] = extesea_form_load($fid);
+  }
+
+  return $forms;
+}
+
+/**
+ * Helper function for loading include files.
+ *
+ * @param $name
+ *   The 'name' of the include. E.g. 'admin' for extesea.admin.inc.
+ */
+function extesea_load_include($name) {
+  module_load_include('inc', 'extesea', "/includes/extesea.$name");
 }
\ No newline at end of file
Index: css/extesea.admin.css
===================================================================
RCS file: css/extesea.admin.css
diff -N css/extesea.admin.css
--- css/extesea.admin.css	17 Apr 2009 15:27:42 -0000	1.1.2.2
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,33 +0,0 @@
-#extesea-admin-blocks-list tr {
-  background-color: transparent;
-}
-#extesea-admin-blocks-list tr.extesea-form {
-  background-color: #edf5fa;
-}
-#extesea-enabled {
-	width: 10%;
-}
-#extesea-title {
-	width: 45%;
-}
-#extesea-fields {
-	width: 45%;
-}
-fieldset.extesea-field {
-  background: none;
-}
-.extesea-field .form-item {
-  float: left;
-  width: 50%;
-}
-html.js .extesea-form th {
-  background: url(../images/collapsed.gif) left center no-repeat;
-  cursor: pointer;
-  padding-left: 15px;
-}
-html.js .extesea-form th.expanded {
-  background-image: url(../images/expanded.gif);
-}
-html.js .extesea-engine {
-  display: none;
-}
\ No newline at end of file
Index: includes/extesea.admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/extesea/includes/Attic/extesea.admin.inc,v
retrieving revision 1.1.2.4
diff -u -p -r1.1.2.4 extesea.admin.inc
--- includes/extesea.admin.inc	17 Apr 2009 19:16:31 -0000	1.1.2.4
+++ includes/extesea.admin.inc	25 Apr 2010 17:13:46 -0000
@@ -2,151 +2,162 @@
 // $Id: extesea.admin.inc,v 1.1.2.4 2009/04/17 19:16:31 xano Exp $
 
 /**
- * @File Create custom search blocks
+ * @File Create custom search forms.
  */
 
 /**
- * Administer custom engines.
+ * List all custom search engines.
  *
- * @return
- *   Type: string; Two rendered forms.
+ * @return array
+ *   A Drupal form.
  */
-function extesea_admin_engines() {
-  return drupal_get_form('extesea_admin_engines_add') . drupal_get_form('extesea_admin_engines_list');
-}
+function extesea_engine_list() {
+  drupal_add_css(drupal_get_path('module', 'extesea') . '/css/extesea.admin.css', 'module', 'all', FALSE);
+  $rows = array();
+  foreach (extesea_engines_info() as $engine_info) {
+    if ($engine_info['#category'] == 'custom') {
+      $rows[] = array(
+        $engine['title'],
+        array(
+          'data' => l(t('edit'), 'admin/build/extesea/engine-edit/' . $engine['name']),
+          'class' => 'extesea-list-operation'
+        ),
+        array(
+          'data' => l(t('delete'), 'admin/build/extesea/engine-delete/' . $engine['name']),
+          'class' => 'extesea-list-operation'
+        ),
+      );
+    }
+  }
+  if (!$rows) {
+    $rows[] = array(array(
+      'data' => t('There are no custom search engines. <a href="@engine_add">Add a new one</a>.', array('@engine_add' => url('admin/build/extesea/engine-add'))),
+      'colspan' => 3,
+    ));
+  }
 
-/**
- * Administer search blocks.
- *
- * @return
- *   Type: string; Two rendered forms.
- */
-function extesea_admin_blocks() {
-  return drupal_get_form('extesea_admin_blocks_add') . drupal_get_form('extesea_admin_blocks_list');
+  $header = array(
+    t('Title'),
+    array (
+      'data' => t('Operations'),
+      'colspan' => 2,
+    ),
+  );
+
+  return theme('table', $header, $rows);
 }
 
 /**
- * Form for adding custom engines.
+ * Form for adding or editing custom engines.
  * 
- * @return
- *   Type: array; A Drupal form.
+ * @return array
+ *   A Drupal form.
  */
-function extesea_admin_engines_add() {
-  $form['add_custom'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Add custom search engine'),
-  );
-  $form['add_custom']['title'] = array(
+function extesea_engine_form() {
+  $form['title'] = array(
     '#type' => 'textfield',
-    '#title' => t('Name'),
+    '#title' => t('Title'),
     '#required' => TRUE,
     '#element_validate' => array('extesea_validate_title'),
   );
-  $form['add_custom']['url'] = array(
+  $form['url'] = array(
     '#type' => 'textfield',
     '#title' => t('URL'),
     '#description' => t('The <abbr title="Uniform Resource Locator">URL</abbr> this search engine uses. <code>[extesea]</code> will be replaced by the search keywords. Example: <code>http://example.com/search?q=[extesea]</code>.'),
     '#required' => TRUE,
     '#element_validate' => array('extesea_validate_url'),
   );
-  $form['add_custom']['submit'] = array(
+  $form['buttons']['submit'] = array(
     '#type' => 'submit',
-    '#value' => t('Add engine'),
+    '#value' => t('Add search engine'),
   );
 
   return $form;
 }
 
 /**
- * Form submission handler for extesea_admin_engines_add().
+ * Form submission handler for extesea_admin_engines_form().
  */
-function extesea_admin_engines_add_submit($form, &$form_state) {
+function extesea_engine_form_submit(array $form, array &$form_state) {
   $values = $form_state['values'];
 
   extesea_engine_insert($values['title'], $values['url']);
 }
 
 /**
- * List all custom and predefined engines.
- *
- * @return
- *   Type: array; A Drupal form.
+ * List all existing search forms.
  */
-function extesea_admin_engines_list() {
-  $engines = extesea_engines_load_custom();
-
-  $form = array();
-  foreach ($engines as $engine) {
-    $form['custom']['engine_enabled_' . $engine['name']] = array(
-      '#type' => 'checkbox',
-      '#title' => t($engine['title']),
-      '#description' => $engine['url'],
-      '#default_value' => TRUE,
+function extesea_form_list() {
+  drupal_add_css(drupal_get_path('module', 'extesea') . '/css/extesea.admin.css', 'module', 'all', FALSE);
+  $extesea_forms = extesea_form_load_all();
+  $rows = array();
+  foreach ($extesea_forms as $extesea_form) {
+    $description = ($extesea_form->description ? '<div class="description">' . $extesea_form->description . '</div>' : NULL);
+    $rows[] = array(
+      $extesea_form->title . $description,
+        array(
+          'data' => l(t('edit'), 'admin/build/extesea/form-edit/' . $extesea_form->fid),
+          'class' => 'extesea-list-operation'
+        ),
+        array(
+          'data' => l(t('delete'), 'admin/build/extesea/form-delete/' . $extesea_form->fid),
+          'class' => 'extesea-list-operation'
+        ),
     );
   }
-  if (count($form)) {
-    $form['custom'] += array(
-      '#type' => 'fieldset',
-      '#title' => t('Custom engines'),
-    );
-    $form['custom']['submit'] = array(
-      '#type' => 'submit',
-      '#value' => t('Delete unchecked engines'),
-    );
+  if (!$rows) {
+    $rows[] = array(array(
+      'data' => t('There are no search forms. <a href="@form_add">Add a new one</a>.', array('@form_add' => url('admin/build/extesea/form-add'))),
+      'colspan' => 3,
+    ));
   }
 
-  return $form;
-}
-
-/**
- * Form submission handler for extesea_admin_engines_list().
- */
-function extesea_admin_engines_list_submit($form, &$form_state) {
-  $values = $form_state['values'];
+  $header = array(
+    t('Title'),
+    array (
+      'data' => t('Operations'),
+      'colspan' => 2,
+    ),
+  );
 
-  $engines = extesea_engines_load_custom();
-  foreach ($engines as $engine) {
-    if ($values['engine_enabled_' . $engine['name']] == FALSE) {
-      extesea_engine_delete($engine['name']);
-    }
-  }
+  return theme('table', $header, $rows);
 }
 
 /**
- * Add a new search block.
+ * Form for adding or editing search forms.
  */
-function extesea_admin_blocks_add() {
-  $form['block'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Add form'),
-  );
-  $form['block']['title'] = array(
+function extesea_form_form() {
+  $form['title'] = array(
     '#type' => 'textfield',
     '#title' => t('Title'),
     '#required' => TRUE,
   );
+  $form['description'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Description'),
+  );
   $options = array();
   $count = 0;
-  foreach(extesea_engines_load_all() as $section => $engines) {
-    foreach ($engines as $name => $engine) {
-      $options[$section][$name] = strip_tags($engine['title']);
+  $categories_info = extesea_categories_info();
+  foreach(extesea_engines_info() as $category => $engines) {
+    foreach ($engines as $engine) {
+      $options[$categories_info[$category]][$engine->delta] = check_plain(strip_tags($engine->title));
       $count++;
     }
-    $count++;
-    if (isset($options[$section])) {
-      uasort($options[$section], 'extesea_engines_sort');
-    }
+  }
+  foreach (array_keys($options) as $category_title) {
+    asort($options[$category_title]);
   }
   ksort($options);
-  $form['block']['engines'] = array(
+  $form['engines'] = array(
     '#type' => 'select',
-    '#title' => t('Engines'),
+    '#title' => t('Search engines'),
     '#required' => TRUE,
     '#options' => $options,
     '#multiple' => TRUE,
-    '#size' => $count > 10 ? 10 : $count,
+    '#size' => count($options) + $count > 10 ? 10 : $count,
   );
-  $form['block']['submit'] = array(
+  $form['buttons']['submit'] = array(
     '#type' => 'submit',
     '#value' => t('Add form'),
   );
@@ -155,192 +166,62 @@ function extesea_admin_blocks_add() {
 }
 
 /**
- * Form submission handler for extesea_admin_blocks_add().
- */
-function extesea_admin_blocks_add_submit($form, &$form_state) {
-  $values = $form_state['values'];
-
-  extesea_block_insert((object) array('title' => $values['title'], 'engines' => $values['engines']));
-}
-
-/**
- * List all existing search blocks.
- */
-function extesea_admin_blocks_list() {
-  $blocks = extesea_block_load_all();
-
-  $form = array();
-  foreach ($blocks as $block) {
-    $form['block_enabled_' . $block->delta] = array(
-      '#type' => 'checkbox',
-      '#default_value' => TRUE,
-    );
-    $form['block_title_' . $block->delta] = array(
-      '#value' => t($block->title),
-    );
-
-    extesea_block_engines_load($block);
-    foreach ($block->engines as $name => $engine) {
-      $prefix = 'block_engines_' . $block->delta . '_' . $name . '_';
-      $form[$prefix . 'title'] = array(
-        '#value' => $engine['title'],
-      );
-      if (isset($engine['fields'])) {
-        foreach ($engine['fields'] as $token => $field) {
-          $field_id = $prefix . $token;
-          $count = count($field['values']);
-          asort($field['values']);
-          $form[$field_id] = array(
-            '#type' => 'fieldset',
-            '#title' => $field['title'],
-            '#attributes' => array(
-              'class' => 'extesea-field',
-            ),
-          );
-          $form[$field_id][$field_id . '_allowed'] = array(
-            '#type' => 'select',
-            '#title' => t('Allowed values'),
-            '#options' => $field['values'],
-            '#multiple' => TRUE,
-            '#size' => $count > 5 ? 5 : $count,
-            '#default_value' => $engine['field_values'][$token]['allowed'],
-          );
-          $form[$field_id][$field_id . '_default'] = array(
-            '#type' => 'select',
-            '#title' => t('Default value'),
-            '#options' => $field['values'],
-            '#default_value' => $engine['field_values'][$token]['default'],
-          );
-        }
-      }
-    }
-  }
-  if (count($form)) {
-    $form['#theme'] = 'admin_blocks_list';
-    $form['submit'] = array(
-      '#type' => 'submit',
-      '#value' => t('Update forms'),
-    );
-  }
-
-  return $form;
-}
-
-/**
- * Form submission handler for extesea_admin_blocks_list().
+ * Form submission handler for extesea_admin_form_form().
  */
-function extesea_admin_blocks_list_submit($form, &$form_state) {
+function extesea_form_form_submit(array $form, array &$form_state) {
+  extesea_load_include('classes');
   $values = $form_state['values'];
-
-  $blocks = extesea_block_load_all();
-  foreach ($blocks as $block) {
-    if ($values['block_enabled_' . $block->delta] == FALSE) {
-      extesea_block_delete($block);
-    }
-    else {
-      extesea_block_engines_load($block);
-      foreach ($block->engines as $name => &$engine) {
-        if (isset($engine['fields'])) {
-          foreach ($engine['fields'] as $token => $field) {
-            $field_id = 'block_engines_' . $block->delta . '_' . $name . '_' . $token;
-            $default_value = $values[$field_id . '_default'];
-            $allowed = array_unique(array_merge($values[$field_id . '_allowed'], array($default_value)));
-            $engine['field_values'][$token]['allowed'] = array_combine($allowed, $allowed);
-            $engine['field_values'][$token]['default'] = $default_value;
-          }
-        }
-      }
-      extesea_block_update($block);
-    }
+  $engines = array();
+  foreach ($values['engines'] as $engine_name) {
+    $engines[$engine_name] = new exteseaFormEngine($engine_name);
   }
-}
 
-/**
- * Insert engine into the DB.
- *
- * @param $title
- *   Type: string; The engine's human-readable name.
- * @param $url
- *   Type: string; The engine's URL.
- */
-function extesea_engine_insert($title, $url) {
-  $id = variable_get('extesea_engine_id', 1);
-  variable_set('extesea_engine_id', $id + 1);
-  $name = 'extesea_engine_' . $id;
-  db_query("INSERT INTO {extesea_engines} (name, title, url) VALUES ('%s', '%s', '%s')", $name, $title, $url);
+  $extesea_form = new exteseaForm($engines);
+  $extesea_form->title = $values['title'];
+  $extesea_form->description = $values['description'];
+  $extesea_form->save();
 }
 
 /**
- * Delete an engine.
+ * Form: the search form delete form.
  *
- * @param $name
- *   Type: integer; The engine's name.
+ * @return array
+ *   A Drupal form.
  */
-function extesea_engine_delete($name) {
-  db_query("DELETE FROM {extesea_engines} WHERE name = '%s'", $name);
-}
+function extesea_form_delete(array &$form_state, exteseaForm $extesea_form) {
+  $form['#redirect'] = 'admin/build/extesea';
+  $form['fid'] = array(
+    '#type' => 'value',
+    '#value' => $extesea_form->fid,
+  );
 
-/**
- * Insert block into the DB.
- *
- * @param $block
- *   Type: object; The block object to insert.
- */
-function extesea_block_insert($block) {
-  drupal_write_record('extesea_blocks', $block);
-  $values = array();
-  foreach ($block->engines as $name) {
-    $engine = extesea_engine_load($name);
-    $values[] = $block->delta;
-    $values[] = $name;
-    $field_values = array();
-    if (isset($engine['fields'])) {
-      foreach ($engine['fields'] as $token => $field) {
-        // Since FAPI returns a select element's values as an associative array
-        // where key and value is the same for each item, we do the same.
-        $keys = array_keys($field['values']);
-        $field_values[$token]['allowed'] = array_combine($keys, $keys);
-        $field_values[$token]['default'] = $keys[0];
-      }
-    }
-    $values[] = serialize($field_values);
-    $placeholders = implode(', ', array_fill(0, count($block->engines), "(%d, '%s', '%s')"));
-  }
-  db_query("INSERT INTO {extesea_block_engines} (delta, name, field_values) VALUES " . $placeholders, $values);
+  return confirm_form($form,
+    t('Are you sure you want to delete %title?', array('%title' => $extesea_form->title)),
+    isset($_GET['destination']) ? $_GET['destination'] : 'admin/build/extesea',
+    t('This action cannot be undone.'),
+    t('Delete'),
+    t('Cancel')
+  );
 }
 
 /**
- * Update a block.
- *
- * @param $block
- *   Type: object; The block object to update.
+ * Submit handler for extesea_form_delete().
  */
-function extesea_block_update($block) {
-  foreach ($block->engines as $name => $engine) {
-    db_query("UPDATE {extesea_block_engines} SET field_values = '%s' WHERE name = '%s' AND delta = %d", serialize($engine['field_values']), $name, $block->delta);
-  }
-}
-
-/**
- * Delete a block.
- *
- * @param $block
- *   Type: object; The object of the block to delete.
- */
-function extesea_block_delete($block) {
-  db_query("DELETE FROM {extesea_blocks} WHERE delta = %d", $block->delta);
-  db_query("DELETE FROM {extesea_block_engines} WHERE delta = %d", $block->delta);
+function extesea_form_delete_submit(array $form, array &$form_state) {
+  $extesea_form = extesea_form_load($form_state['values']['fid']);
+  $extesea_form->delete();
+  drupal_set_message(t('%title has been deleted.', array('%title' => $extesea_form->title)));
 }
 
 /**
  * Form valiation callback for a search engine's title.
  *
- * @param $element
- *   Type: array; The form element to validate.
+ * @param $element array
+ *   The form element to validate.
  */
-function extesea_validate_title($element) {
+function extesea_validate_title(array $element) {
   if (!empty($element['#value'])) {
-    $count = db_result(db_query("SELECT COUNT(*) FROM {extesea_engines} WHERE title = '%s'", $element['#value']));
+    $count = db_result(db_query("SELECT COUNT(*) FROM {extesea_engine} WHERE title = '%s'", $element['#value']));
     if ($count > 0) {
       form_error($element, t('A search engine with this title already exists.'));
     }
@@ -350,87 +231,14 @@ function extesea_validate_title($element
 /**
  * Form valiation callback for a search engine's URL.
  *
- * @param $element
- *   Type: array; The form element to validate.
+ * @param $element array
+ *   The form element to validate.
  */
-function extesea_validate_url($element) {
+function extesea_validate_url(array $element) {
   if (!empty($element['#value']) && !valid_url(str_replace('[extesea]', '', $element['#value']), TRUE)) {
     form_error($element, t('Please enter a valid URL.'));
   }
   elseif (!strpos($element['#value'], '[extesea]')) {
     form_error($element, t('URLs should contain <code>[extesea]</code>.'));
   }
-}
-
-/**
- * Theme a block list.
- */
-function theme_admin_blocks_list($form) {
-  drupal_add_css(drupal_get_path('module', 'extesea') . '/css/extesea.admin.css');
-  drupal_add_js(drupal_get_path('module', 'extesea') . '/js/extesea.admin.js');
-  $blocks = extesea_block_load_all();
-  $rows = array();
-  foreach ($blocks as $block) {
-    // Add a header for this block.
-    $rows[] = array(
-      'data' => array(
-        array(
-          'data' => drupal_render($form['block_enabled_' . $block->delta]),
-        ),
-        array(
-          'data' => drupal_render($form['block_title_' . $block->delta]),
-          'header' => TRUE,
-          'id' => 'extesea-form-' . $block->delta,
-        ),
-        NULL,
-      ),
-      'class' => 'extesea-form',
-    );
-    // Loop through this block's engines. Display each in a separate row.
-    extesea_block_engines_load($block);
-    foreach ($block->engines as $name => $engine) {
-      $prefix = 'block_engines_' . $block->delta . '_' . $name . '_';
-      // Render this block's fields.
-      $fields = '';
-      if (isset($engine['fields'])) {
-        foreach ($engine['fields'] as $token => $field) {
-          $fields .= drupal_render($form[$prefix . $token]);
-        }
-      }
-      $rows[] = array(
-        'data' => array(
-          NULL,
-          drupal_render($form[$prefix . 'title']),
-          $fields,
-        ),
-        'class' => 'extesea-engine extesea-form-' . $block->delta,
-      );
-    }
-  }
-  $header = array(
-    array(
-      'data' => t('Enabled'),
-      'id' => 'extesea-enabled',
-    ),
-    array(
-      'data' => t('Title'),
-      'id' => 'extesea-title',
-    ),
-    array(
-      'data' => t('Fields'),
-      'id' => 'extesea-fields',
-    ),
-  );
-
-  return theme('table', $header, $rows) . drupal_render($form);
-}
-
-/**
- * Alphabetically sort an array of engines per section.
- *
- * @param $engines
- *   Type: array;
- */
-function extesea_engines_sort($a, $b) {
-  return strcmp($a['title'], $b['title']);
 }
\ No newline at end of file
Index: includes/extesea.classes.inc
===================================================================
RCS file: includes/extesea.classes.inc
diff -N includes/extesea.classes.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ includes/extesea.classes.inc	25 Apr 2010 17:13:46 -0000
@@ -0,0 +1,118 @@
+<?php
+// $Id: extesea.admin.inc,v 1.1.2.4 2009/04/17 19:16:31 xano Exp $
+
+/**
+ * @File External Search's classes.
+ */
+
+class exteseaForm {
+  public $fid = 0;
+  public $title = NULL;
+  public $description = NULL;
+  public $engines = array();
+
+  /**
+   * Construct the object with basic properties or properties that have been
+   * passed on.
+   *
+   * @param $engines array
+   *   An array of exteseaEngine objects, where keys are the engines' system
+   *   names.
+   */
+  function __construct(array $engines) {
+    $this->engines = $engines;
+  }
+
+  /**
+   * View this search form.
+   */
+  function view() {
+    // TODO: Add extra theming.
+    return drupal_get_form('extesea_search_form_' . $this);
+  }
+
+  /**
+   * Save this search form to the database.
+   */
+  function save() {
+    $update = $this->fid ? 'fid' : array();
+    drupal_write_record('extesea_form', $this, $update);
+
+    foreach ($this->engines as $engine) {
+      $engine->save();
+    }
+  }
+
+  /**
+   * Delete this search form.
+   */
+  function delete() {
+    db_query("DELETE FROM {extesea_form} WHERE fid = %d", $this->fid);
+    db_query("DELETE FROM {extesea_form_engine} WHERE fid = %d", $this->fid);
+  }
+}
+
+class exteseaEngine {
+  public $delta = NULL;
+  public $title = NULL;
+  public $url = NULL;
+  public $fields = array();
+  protected $custom = FALSE;
+
+  function __construct($delta, $title, $url, array $fields = array(), $custom = FALSE) {
+    $this->delta = $delta;
+    $this->title = $title;
+    $this->url = $url;
+    $this->fields = $fields;
+    $this->custom = $custom;
+  }
+}
+
+class exteseaFormEngine {
+  public $fid = 0;
+  public $delta = NULL;
+  public $fields = array();
+
+  /**
+   * Construct the object with basic properties or properties that have been
+   * passed on.
+   *
+   * @param $delta string
+   *   The search engine's machine name or ID.
+   * @param $fid integer
+   *   The FID of the exteseaForm this search engine belongs to.
+   * @param $fields array
+   *   An array of exteseaEngineField objects.
+   */
+  function __construct($delta, $fid = 0, array $fields = array()) {
+    $this->delta = $delta;
+    $this->fields = $fields;
+    $this->fid = $fid;
+  }
+
+  /**
+   * Save this search form engine to the DB.
+   */
+  function save() {
+    $update = db_result(db_query("SELECT COUNT(*) FROM {extesea_form_engine} WHERE name = '%s'", $this->name)) ? array('fid', 'name') : array();
+    drupal_write_record('extesea_form_engine', $this, $update);
+  }
+}
+
+class exteseaEngineField {
+  public $token = NULL;
+  public $title = NULL;
+  public $values = array();
+
+  function __construct($token, $title, array $values) {
+    $this->token = $token;
+    $this->title = $title;
+    $this->values = $values;
+  }
+}
+
+class exteseaFormEngineField {
+  public $token = NULL;
+  public $allowed_values = array();
+  public $default_value = NULL;
+}
\ No newline at end of file
Index: includes/extesea.engines.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/extesea/includes/Attic/extesea.engines.inc,v
retrieving revision 1.1.2.1
diff -u -p -r1.1.2.1 extesea.engines.inc
--- includes/extesea.engines.inc	17 Apr 2009 10:52:03 -0000	1.1.2.1
+++ includes/extesea.engines.inc	25 Apr 2010 17:13:46 -0000
@@ -2,240 +2,159 @@
 // $Id: extesea.engines.inc,v 1.1.2.1 2009/04/17 10:52:03 xano Exp $
 
 /**
- * Implementation of hook_extesea_engine_info().
+ * Define search engines.
+ *
+ * @return array
+ *   Search engine information according to hook_extesea_engine_info().
  */
-function extesea_extesea_engine_info() {
+function extesea_predefined_engines_info() {
   return array(
-    t('Dictionaries and encyclopediae') => array(
-      'urbandictionary' => array(
-        'title' => t('Urban Dictionary'),
-        'url' => 'http://www.urbandictionary.com/define.php?term=[extesea]',
-      ),
-      'wikibooks' => array(
-        'title' => t('Wikibooks'),
-        'url' => 'http://[language].wikibooks.org/wiki/Special:Search?search=[extesea]',
-        'fields' => array(
-          'language' => array(
-            'title' => t('Language'),
-            'values' => array(
-              'ang' => t('Englisc'),
-              'ar' => t('العربي'),
-              'bg' => t('Български'),
-              'ca' => t('Català'),
-              'cs' => t('Česky'),
-              'da' => t('Dansk'),
-              'de' => t('Deutsch'),
-              'el' => t('Ελληνικά'),
-              'en' => t('English'),
-              'eo' => t('Esperanto'),
-              'es' => t('Español'),
-              'et' => t('Eesti'),
-              'fa' => t('فارسی'),
-              'fi' => t('Suomi'),
-              'fr' => t('Français'),
-              'gl' => t('Galego'),
-              'he' => t('עברית'),
-              'hr' => t('Hrvatski'),
-              'hu' => t('Magyar'),
-              'ia' => t('Interlingua'),
-              'id' => t('Bahasa Indonesia'),
-              'is' => t('Íslenska'),
-              'it' => t('Italiano'),
-              'ja' => t('日本語'),
-              'ka' => t('ქართული'),
-              'ko' => t('한국어'),
-              'la' => t('Latina'),
-              'lt' => t('Lietuvių'),
-              'mk' => t('Македонски'),
-              'mr' => t('मराठी'),
-              'nl' => t('Nederlands'),
-              'no' => t('Norsk'),
-              'oc' => t('Occitan'),
-              'pl' => t('Polski'),
-              'pt' => t('Português'),
-              'ro' => t('Română'),
-              'ru' => t('Русский'),
-              'simple' => t('Simple English'),
-              'sk' => t('Slovenčina'),
-              'sl' => t('Slovenščina'),
-              'sr' => t('Српски'),
-              'sv' => t('Svenska'),
-              'sq' => t('Shqip'),
-              'ta' => t('தமிழ்'),
-              'th' => t('ไทย'),
-              'tl' => t('Tagalog'),
-              'tr' => t('Türkçe'),
-              'ur' => t('اردو'),
-              'vi' => t('Tiếng Việt'),
-              'zh' => t('中文'),
-            ),
-          ),
-        ),
-      ),
-      'wikipedia' => array(
-        'title' => t('Wikipedia'),
-        'url' => 'http://[language].wikipedia.org/wiki/Special:Search?search=[extesea]',
-        'fields' => array(
-          'language' => array(
-            'title' => t('Language'),
-            'values' => array(
-              'af' => t('Afrikaans'),
-              'ca' => t('Català'),
-              'cs' => t('Česky'),
-              'da' => t('Dansk'),
-              'de' => t('Deutsch'),
-              'en' => t('English'),
-              'eo' => t('Esperanto'),
-              'es' => t('Español'),
-              'fi' => t('Suomi'),
-              'fr' => t('Français'),
-              'fy' => t('Frysk'),
-              'hu' => t('Magyar'),
-              'id' => t('Bahasa Indonesia'),
-              'it' => t('Italiano'),
-              'ja' => t('日本語'),
-              'nl' => t('Nederlands'),
-              'nn' => t('Norsk (nynorsk)'),
-              'no' => t('Norsk (bokmål)'),
-              'oc' => t('Occitan'),
-              'pl' => t('Polski'),
-              'pt' => t('Português'),
-              'ru' => t('Русский'),
-              'simple' => t('Simple English'),
-              'sk' => t('Slovenčina'),
-              'sv' => t('Svenska'),
-              'sq' => t('Shqip'),
-              'tr' => t('Türkçe'),
-              'uk' => t('Українська'),
-              'vi' => t('Tiếng Việt'),
-              'vo' => t('Volapük'),
-              'zh' => t('中文'),
-            ),
-          ),
-        ),
-      ),
+    'dictionaries' => array(
+      'urbandictionary' => new exteseaEngine('urbandictionary', t('Urban Dictionary'), 'http://www.urbandictionary.com/define.php?term=[extesea]'),
+      'wikibooks' => new exteseaEngine('wikibooks', t('Wikibooks'), 'http://[language].wikibooks.org/wiki/Special:Search?search=[extesea]', array(
+        'language' => new exteseaEngineField('language', t('Language'), array(
+          'ang' => 'Englisc',
+          'ar' => 'العربي',
+          'bg' => 'Български',
+          'ca' => 'Català',
+          'cs' => 'Česky',
+          'da' => 'Dansk',
+          'de' => 'Deutsch',
+          'el' => 'Ελληνικά',
+          'en' => 'English',
+          'eo' => 'Esperanto',
+          'es' => 'Español',
+          'et' => 'Eesti',
+          'fa' => 'فارسی',
+          'fi' => 'Suomi',
+          'fr' => 'Français',
+          'gl' => 'Galego',
+          'he' => 'עברית',
+          'hr' => 'Hrvatski',
+          'hu' => 'Magyar',
+          'ia' => 'Interlingua',
+          'id' => 'Bahasa Indonesia',
+          'is' => 'Íslenska',
+          'it' => 'Italiano',
+          'ja' => '日本語',
+          'ka' => 'ქართული',
+          'ko' => '한국어',
+          'la' => 'Latina',
+          'lt' => 'Lietuvių',
+          'mk' => 'Македонски',
+          'mr' => 'मराठी',
+          'nl' => 'Nederlands',
+          'no' => 'Norsk',
+          'oc' => 'Occitan',
+          'pl' => 'Polski',
+          'pt' => 'Português',
+          'ro' => 'Română',
+          'ru' => 'Русский',
+          'simple' => 'Simple English',
+          'sk' => 'Slovenčina',
+          'sl' => 'Slovenščina',
+          'sr' => 'Српски',
+          'sv' => 'Svenska',
+          'sq' => 'Shqip',
+          'ta' => 'தமிழ்',
+          'th' => 'ไทย',
+          'tl' => 'Tagalog',
+          'tr' => 'Türkçe',
+          'ur' => 'اردو',
+          'vi' => 'Tiếng Việt',
+          'zh' => '中文',
+        )),
+      )),
+      'wikipedia' => new exteseaEngine('wikipedia', t('Wikipedia'), 'http://[language].wikipedia.org/wiki/Special:Search?search=[extesea]', array(
+        'language' => new exteseaEngineField('language', t('Language'), array(
+          'af' => 'Afrikaans',
+          'ca' => 'Català',
+          'cs' => 'Česky',
+          'da' => 'Dansk',
+          'de' => 'Deutsch',
+          'en' => 'English',
+          'eo' => 'Esperanto',
+          'es' => 'Español',
+          'fi' => 'Suomi',
+          'fr' => 'Français',
+          'fy' => 'Frysk',
+          'hu' => 'Magyar',
+          'id' => 'Bahasa Indonesia',
+          'it' => 'Italiano',
+          'ja' => '日本語',
+          'nl' => 'Nederlands',
+          'nn' => 'Norsk (nynorsk)',
+          'no' => 'Norsk (bokmål)',
+          'oc' => 'Occitan',
+          'pl' => 'Polski',
+          'pt' => 'Português',
+          'ru' => 'Русский',
+          'simple' => 'Simple English',
+          'sk' => 'Slovenčina',
+          'sv' => 'Svenska',
+          'sq' => 'Shqip',
+          'tr' => 'Türkçe',
+          'uk' => 'Українська',
+          'vi' => 'Tiếng Việt',
+          'vo' => 'Volapük',
+          'zh' => '中文',
+        )),
+      )),
     ),
-    t('General search sites') => array(
-      'ask' => array(
-        'title' => t('Ask'),
-        'url' => 'http://ask.com/web?q=[extesea]',
-      ),
-      'google' => array(
-        'title' => t('Google'),
-        'url' => 'http://google.com/search?q=[extesea]',
-      ),
-      'live' => array(
-        'title' => t('Live Search'),
-        'url' => 'http://search.live.com/results.aspx?q=[extesea]',
-      ),
-      'yahoo' => array(
-        'title' => t('Yahoo!'),
-        'url' => 'http://search.yahoo.com/search?p=[extesea]',
-      ),
+    'search' => array(
+      'ask' => new exteseaEngine('ask', t('Ask'), 'http://ask.com/web?q=[extesea]'),
+      'google' => new exteseaEngine('google', t('Google'), 'http://google.com/search?q=[extesea]'),
+      'bing' => new exteseaEngine('bing', t('Bing'), 'http://www.bing.com/search?q=[extesea]'),
+      'yahoo' => new exteseaEngine('yahoo', t('Yahoo!'), 'http://search.yahoo.com/search?p=[extesea]'),
     ),
-    t('Media') => array(
-      'flickr' => array(
-        'title' => t('Flickr'),
-        'url' => 'http://flickr.com/search/?q=[extesea]',
-      ),
-      'googlevideo' => array(
-        'title' => t('Google Video'),
-        'url' => 'http://video.google.com/videosearch?q=[extesea]',
-      ),
-      'gracenote' => array(
-        'title' => t('Gracenote CD database'),
-        'url' => 'http://www.gracenote.com/search/?search_type=[type]&query=[extesea]',
-        'fields' => array(
-          'type' => array(
-            'title' => t('Type'),
-            'values' => array(
-              'artist' => t('Artist'),
-              'album' => t('Album'),
-              'track' => t('Track'),
-            ),
-          ),
-        ),
-      ),
-      'imdb' => array(
-        'title' => t('<abbr title="The Internet Movie Database">IMDb</abbr>'),
-        'url' => 'http://www.imdb.com/find?s=[type]&q=[extesea]',
-        'fields' => array(
-          'type' => array(
-            'title' => t('Type'),
-            'values' => array(
-              'all' => t('All'),
-              'tt' => t('Titles'),
-              'ep' => t('TV Episodes'),
-              'nm' => t('Names'),
-              'co' => t('Companies'),
-              'kw' => t('Keywords'),
-              'char' => t('Characters'),
-            ),
-          ),
-        ),
-      ),
-      'lastfm' => array(
-        'title' => t('Last.fm'),
-        'url' => 'http://last.fm/search?m=[type]&q=[extesea]',
-        'fields' => array(
-          'type' => array(
-            'title' => t('Type'),
-            'values' => array(
-              'all' => t('Music'),
-              'label' => t('Label'),
-              'tag' => t('Tag'),
-            ),
-          ),
-        ),
-      ),
-      'youtube' => array(
-        'title' => t('YouTube'),
-        'url' => 'http://www.youtube.com/results?search_type=search_[type]&search_query=[extesea]',
-        'fields' => array(
-          'type' => array(
-            'title' => t('Type'),
-            'values' => array(
-              'videos' => t('Videos'),
-              'users' => t('Channels'),
-              'playlists' => t('Playlists'),
-            ),
-          ),
-        ),
-      ),
+    'media' => array(
+      'flickr' => new exteseaEngine('flickr', t('Flickr'), 'http://flickr.com/search/?q=[extesea]'),
+      'googlevideo' => new exteseaEngine('googlevideo', t('Google Video'), 'http://video.google.com/videosearch?q=[extesea]'),
+      'gracenote' => new exteseaEngine('gracenote', t('Gracenote CD database'), 'http://www.gracenote.com/search/?search_type=[type]&query=[extesea]', array(
+        'type' => new exteseaEngineField('type', t('Type'), array(
+          'artist' => t('Artist'),
+          'album' => t('Album'),
+          'track' => t('Track'),
+        )),
+      )),
+      'imdb' => new exteseaEngine('imdb', t('<abbr title="The Internet Movie Database">IMDb</abbr>'), 'http://www.imdb.com/find?s=[type]&q=[extesea]', array(
+        'type' => new exteseaEngineField('type', t('Type'), array(
+          'all' => t('All'),
+          'tt' => t('Titles'),
+          'ep' => t('TV Episodes'),
+          'nm' => t('Names'),
+          'co' => t('Companies'),
+          'kw' => t('Keywords'),
+          'char' => t('Characters'),
+        )),
+      )),
+      'lastfm' => new exteseaEngine('lastfm', t('Last.fm'), 'http://last.fm/search?m=[type]&q=[extesea]', array(
+        'type' => new exteseaEngineField('type', t('Type'), array(
+          'all' => t('Music'),
+          'label' => t('Label'),
+          'tag' => t('Tag'),
+        )),
+      )),
+      'youtube' => new exteseaEngine('youtube', t('YouTube'), 'http://www.youtube.com/results?search_type=search_[type]&search_query=[extesea]', array(
+        'type' => new exteseaEngineField('type', t('Type'), array(
+          'videos' => t('Videos'),
+          'users' => t('Channels'),
+          'playlists' => t('Playlists'),
+        )),
+      )),
     ),
-    t('Programming') => array(
-      'drupalapi' => array(
-        'title' => t('Drupal <abbr title="Application Programming Interface">API</abbr> documentation'),
-        'url' => 'http://api.drupal.org/api/search/[version]/[extesea]',
-        'fields' => array(
-          'version' => array(
-            'title' => t('Version'),
-            'values' => array(
-              '5' => t('Drupal 5'),
-              '6' => t('Drupal 6'),
-              '7' => t('Drupal 7'),
-            ),
-          ),
-        ),
-      ),
-      'mysqldocs' => array(
-        'title' => t('MySQL documentation'),
-        'url' => 'http://search.mysql.com/search?q=[extesea]&site=documentation',
-      ),
-      'phpnetfunctions' => array(
-        'title' => t('Php.net function list'),
-        'url' => 'http://php.net/[extesea]',
-      ),
-    ),
-    t('Software') => array(
-      'downloadcom' => array(
-        'title' => t('CNET Downloads (download.com)'),
-        'url' => 'http://www.download.com/1770-20_4-0.html?query=[extesea]&searchtype=downloads',
-      ),
-      'sourceforge' => array(
-        'title' => t('SourceForge.net'),
-        'url' => 'http://sourceforge.net/search/?words=[extesea]',
-      ),
+    'programming' => array(
+      'drupalapi' => new exteseaEngine('drupalapi', t('Drupal <abbr title="Application Programming Interface">API</abbr> documentation'), 'http://api.drupal.org/api/search/[version]/[extesea]', array(
+        'version' => new exteseaEngineField('version', t('Version'), array(
+          '4.6' => t('Drupal 4.6'),
+          '4.7' => t('Drupal 4.7'),
+          '5' => t('Drupal 5'),
+          '6' => t('Drupal 6'),
+          '7' => t('Drupal 7'),
+        )),
+      )),
+      'mysqldocs' => new exteseaEngine('mysqldocs', t('MySQL documentation'), 'http://search.mysql.com/search?q=[extesea]&site=documentation'),
+      'phpnetfunctions' => new exteseaEngine('phpnetfunctions', t('Php.net function list'), 'http://php.net/[extesea]'),
     ),
   );
 }
\ No newline at end of file
Index: js/extesea.admin.js
===================================================================
RCS file: js/extesea.admin.js
diff -N js/extesea.admin.js
--- js/extesea.admin.js	17 Apr 2009 15:32:22 -0000	1.1.2.1
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,40 +0,0 @@
-Drupal.behaviors.exteseaCollapse = function (context) {
-  var timeout = null;
-
-  $('.extesea-form th').click(function() {
-    $(this).toggleClass('expanded').attr('id');
-    var rows = $('.' + this.id);
-    direction = Drupal.settings[this.id];
-    var i = direction ? rows.size() - 1 : 0;
-
-    // If clicked in the middle of expanding a group, stop so we can switch directions.
-    if (timeout) {
-      clearTimeout(timeout);
-    }
-
-    // Function to toggle an individual row according to the current direction.
-    // We set a timeout of 20 ms until the next i will be shown/hidden to
-    // create a sliding effect.
-    function rowToggle() {
-      if (direction) {
-        if (i >= 0) {
-          $(rows[i]).hide();
-          i--;
-          timeout = setTimeout(rowToggle, 20);
-        }
-      }
-      else {
-        if (i < rows.size()) {
-          $(rows[i]).show();
-          i++;
-          timeout = setTimeout(rowToggle, 20);
-        }
-      }
-    }
-
-    Drupal.settings[this.id] = !Drupal.settings[this.id];
-
-    // Kick-off the toggling upon a new click.
-    rowToggle();
-  });
-};
\ No newline at end of file
