Index: wysiwyg.admin.inc
===================================================================
RCS file: wysiwyg.admin.inc
diff -N wysiwyg.admin.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ wysiwyg.admin.inc	14 Oct 2008 20:56:35 -0000
@@ -0,0 +1,497 @@
+<?php
+// $Id: wysiwyg.admin.inc,v 1.19 2008/10/13 19:34:29 sun Exp $
+
+/**
+ * @file
+ * Integrate Wysiwyg editors into Drupal.
+ */
+
+/**
+ * Callback handler for admin pages; menu callback.
+ *
+ * @todo Move into hook_menu(), resp. FAPI functions.
+ */
+function wysiwyg_admin($arg = '', $name = '') {
+  switch ($arg) {
+    case 'add':
+      $breadcrumb[] = l(t('Home'), NULL);
+      $breadcrumb[] = l(t('Administer'), 'admin');
+      $breadcrumb[] = l(t('Site configuration'), 'admin/settings');
+      $breadcrumb[] = l(t('Wysiwyg'), 'admin/settings/wysiwyg');
+      $breadcrumb[] = l(t('Wysiwyg Profiles'), 'admin/settings/wysiwyg/profile');
+      drupal_set_breadcrumb($breadcrumb);
+      $profile = new stdClass;
+      return drupal_get_form('wysiwyg_profile_form', $profile);
+
+    case 'edit':
+      drupal_set_title(t('Edit Wysiwyg Editor profile'));
+      return drupal_get_form('wysiwyg_profile_form', wysiwyg_load_profile($name));
+
+    case 'delete':
+      wysiwyg_profile_delete($name);
+      drupal_set_message(t('Wysiwyg profile %name has been deleted.', array('%name' => $name)));
+      drupal_goto('admin/settings/wysiwyg/profile');
+      break;
+
+    default:
+      return wysiwyg_profile_overview();
+  }
+}
+
+/**
+ * Return an HTML form for profile configuration.
+ */
+function wysiwyg_profile_form($form_state, $profile) {
+  // Merge in defaults.
+  settype($profile, 'array');
+  $profile += array(
+    'name' => NULL,
+    'rids' => array(),
+    'settings' => array(),
+  );
+  $profile['settings'] += array(
+    'editor' => NULL,
+    'default' => TRUE,
+    'user_choose' => FALSE,
+    'show_toggle' => TRUE,
+    'theme' => 'advanced',
+    'language' => 'en',
+    'access' => 1,
+    'access_pages' => "node/*\nuser/*\ncomment/*",
+    'buttons' => array(),
+    'toolbar_loc' => 'top',
+    'toolbar_align' => 'left',
+    'path_loc' => 'bottom',
+    'resizing' => TRUE,
+    // Also available, but buggy in TinyMCE 2.x: blockquote,code,dt,dd,samp.
+    'block_formats' => 'p,address,pre,h2,h3,h4,h5,h6,div',
+    'verify_html' => TRUE,
+    'preformatted' => FALSE,
+    'convert_fonts_to_spans' => TRUE,
+    'remove_linebreaks' => TRUE,
+    'apply_source_formatting' => FALSE,
+    'paste_auto_cleanup_on_paste' => FALSE,
+    'css_setting' => 'theme',
+    'css_path' => NULL,
+    'css_classes' => NULL,
+  );
+  settype($profile, 'object');
+
+  $form = array();
+
+  if (arg(4) == 'add') {
+    $submit_label = t('Create profile');
+  }
+  else {
+    $form['old_name'] = array('#type' => 'hidden', '#value' => $profile->name);
+    $submit_label = t('Update profile');
+  }
+
+  $form['basic'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Basic setup'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+  );
+
+  $form['basic']['name'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Profile name'),
+    '#default_value' => $profile->name,
+    '#size' => 40,
+    '#maxlength' => 128,
+    '#description' => t('Enter a name for this profile. This name is only visible within the Wysiwyg Editor administration page.'),
+    '#required' => TRUE,
+  );
+
+  $form['basic']['rids'] = array(
+    '#type' => 'checkboxes',
+    '#title' => t('Roles allowed to use this profile'),
+    '#default_value' => array_keys($profile->rids),
+    '#options' => user_roles(FALSE, 'access wysiwyg editor'),
+    '#description' => t('Only roles with the %access permission will be shown here.', array('%access' => 'access wysiwyg editor')),
+    '#required' => TRUE,
+  );
+
+  $editors = wysiwyg_get_all_editors();
+  $options = array();
+  foreach ($editors as $name => $properties) {
+    if ($properties['installed']) {
+      $options[$name] = $properties['title'];
+    }
+  }
+  $form['basic']['editor'] = array(
+    '#type' => 'select',
+    '#title' => t('Editor'),
+    '#default_value' => $profile->settings['editor'],
+    '#options' => $options,
+    '#description' => t('Choose an editor to use for this profile.'),
+  );
+
+  $form['basic']['default'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Enabled by default'),
+    '#default_value' => $profile->settings['default'],
+    '#return_value' => 1,
+    '#description' => t('The default editor state for users having access to this profile. Users are able to override this state if the next option is enabled.'),
+  );
+
+  $form['basic']['user_choose'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Allow users to choose default'),
+    '#default_value' => $profile->settings['user_choose'],
+    '#return_value' => 1,
+    '#description' => t('If allowed, users will be able to choose their own Wysiwyg Editor default state in their user account settings.'),
+  );
+
+  $form['basic']['show_toggle'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Show %editor toggle link', array('%editor' => t('enable/disable rich text'))),
+    '#default_value' => $profile->settings['show_toggle'],
+    '#return_value' => 1,
+    '#description' => t('Whether or not to show the %editor toggle link below a textarea. If disabled, the user setting or global default is used (see above).', array('%editor' => t('enable/disable rich text'))),
+  );
+
+  $form['basic']['theme'] = array(
+    '#type' => 'hidden',
+    '#value' => $profile->settings['theme'],
+  );
+
+  $form['basic']['language'] = array(
+    '#type' => 'select',
+    '#title' => t('Language'),
+    '#default_value' => $profile->settings['language'],
+    '#options' => drupal_map_assoc(array('ar', 'ca', 'cs', 'cy', 'da', 'de', 'el', 'en', 'es', 'fa', 'fi', 'fr', 'fr_ca', 'he', 'hu', 'is', 'it', 'ja', 'ko', 'nb', 'nl', 'nn', 'pl', 'pt', 'pt_br', 'ru', 'ru_KOI8-R', 'ru_UTF-8', 'si', 'sk', 'sv', 'th', 'zh_cn', 'zh_tw', 'zh_tw_utf8')),
+    '#description' => t('The language for the Wysiwyg Editor interface. Language codes are based on the <a href="http://www.loc.gov/standards/iso639-2/englangn.html">ISO-639-2</a> format.'),
+  );
+
+  $form['buttons'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Buttons and plugins'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+    '#tree' => TRUE,
+    '#theme' => 'wysiwyg_admin_button_table',
+  );
+
+  $plugins = wysiwyg_get_plugins($profile->settings['editor']);
+  // Generate the button list.
+  foreach ($plugins as $name => $meta) {
+    if (isset($meta['buttons']) && is_array($meta['buttons'])) {
+      foreach ($meta['buttons'] as $button => $title) {
+        $icon = '';
+        if (!empty($meta['path'])) {
+          // @todo Button icon locations are different in editors, editor versions,
+          //   and contrib/custom plugins (like Image Assist, f.e.).
+          $img_src = $meta['path'] ."/images/$name.gif";
+          // Handle plugins that have more than one button.
+          if (!file_exists($img_src)) {
+            $img_src = $meta['path'] ."/images/$button.gif";
+          }
+          $icon = file_exists($img_src) ? '<img src="'. base_path() . $img_src .'" title="'. $button .'" style="border: 1px solid grey; vertical-align: middle;" />' : '';
+        }
+        $title = (isset($meta['url']) ? l($title, $meta['url'], array('target' => '_blank')) : $title);
+        $title = (!empty($icon) ? $icon .' '. $title : $title);
+        $form['buttons'][$name][$button] = array(
+          '#type' => 'checkbox',
+          '#title' => $title,
+          '#default_value' => !empty($profile->settings['buttons'][$name][$button]) ? $profile->settings['buttons'][$name][$button] : FALSE,
+        );
+      }
+    }
+    else if (isset($meta['extensions']) && is_array($meta['extensions'])) {
+      foreach ($meta['extensions'] as $extension => $title) {
+        $form['buttons'][$name][$extension] = array(
+          '#type' => 'checkbox',
+          '#title' => isset($meta['url']) ? l($title, $meta['url'], array('target' => '_blank')) : $title,
+          '#default_value' => !empty($profile->settings['buttons'][$name][$extension]) ? $profile->settings['buttons'][$name][$extension] : FALSE,
+        );
+      }
+    }
+  }
+
+  $form['appearance'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Editor appearance'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+  );
+
+  $form['appearance']['toolbar_loc'] = array(
+    '#type' => 'select',
+    '#title' => t('Toolbar location'),
+    '#default_value' => $profile->settings['toolbar_loc'],
+    '#options' => array('bottom' => t('Bottom'), 'top' => t('Top')),
+    '#description' => t('This option controls whether the editor toolbar is displayed above or below the editing area.'),
+  );
+
+  $form['appearance']['toolbar_align'] = array(
+    '#type' => 'select',
+    '#title' => t('Button alignment'),
+    '#default_value' => $profile->settings['toolbar_align'],
+    '#options' => array('center' => t('Center'), 'left' => t('Left'), 'right' => t('Right')),
+    '#description' => t('This option controls the alignment of icons in the editor toolbar.'),
+  );
+
+  $form['appearance']['path_loc'] = array(
+    '#type' => 'select',
+    '#title' => t('Path location'),
+    '#default_value' => $profile->settings['path_loc'],
+    '#options' => array('none' => t('Hide'), 'top' => t('Top'), 'bottom' => t('Bottom')),
+    '#description' => t('Where to display the path to HTML elements (i.e. <code>body > table > tr > td</code>).'),
+  );
+
+  $form['appearance']['resizing'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Enable resizing button'),
+    '#default_value' => $profile->settings['resizing'],
+    '#return_value' => 1,
+    '#description' => t('This option gives you the ability to enable/disable the resizing button. If enabled, the Path location toolbar must be set to "Top" or "Bottom" in order to display the resize icon.'),
+  );
+
+  $form['output'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Cleanup and output'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+  );
+
+  $form['output']['verify_html'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Verify HTML'),
+    '#default_value' => $profile->settings['verify_html'],
+    '#return_value' => 1,
+    '#description' => t('If enabled, potentially malicious code like <code>&lt;HEAD&gt;</code> tags will be removed from HTML contents.'),
+  );
+
+  $form['output']['preformatted'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Preformatted'),
+    '#default_value' => $profile->settings['preformatted'],
+    '#return_value' => 1,
+    '#description' => t('If enabled, the editor will insert TAB characters on tab and preserve other whitespace characters just like a PRE element in HTML does.'),
+  );
+
+  $form['output']['convert_fonts_to_spans'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Convert &lt;font&gt; tags to styles'),
+    '#default_value' => $profile->settings['convert_fonts_to_spans'],
+    '#return_value' => 1,
+    '#description' => t('If enabled, HTML tags declaring the font size, font family, font color and font background color will be replaced by inline CSS styles.'),
+  );
+
+  $form['output']['remove_linebreaks'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Remove linebreaks'),
+    '#default_value' => $profile->settings['remove_linebreaks'],
+    '#return_value' => 1,
+    '#description' => t('If enabled, the editor will remove most linebreaks from contents. Disabling this option could avoid conflicts with other input filters.'),
+  );
+
+  $form['output']['apply_source_formatting'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Apply source formatting'),
+    '#default_value' => $profile->settings['apply_source_formatting'],
+    '#return_value' => 1,
+    '#description' => t('If enabled, the editor will re-format the HTML source code. Disabling this option could avoid conflicts with other input filters.'),
+  );
+
+  $form['output']['paste_auto_cleanup_on_paste'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Force cleanup on standard paste'),
+    '#default_value' => $profile->settings['paste_auto_cleanup_on_paste'],
+    '#return_value' => 1,
+    '#description' => t('If enabled, the default paste function (CTRL-V or SHIFT-INS) behaves like the "paste from word" plugin function.'),
+  );
+
+  $form['css'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('CSS'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+  );
+
+  $form['css']['block_formats'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Block formats'),
+    '#default_value' => $profile->settings['block_formats'],
+    '#size' => 40,
+    '#maxlength' => 250,
+    '#description' => t('Comma separated list of HTML block formats. You can only remove elements, not add.'),
+  );
+
+  $form['css']['css_setting'] = array(
+    '#type' => 'select',
+    '#title' => t('Editor CSS'),
+    '#default_value' => $profile->settings['css_setting'],
+    '#options' => array('theme' => t('Use theme CSS'), 'self' => t('Define CSS'), 'none' => t('TinyMCE default CSS')),
+    '#description' => t('Defines the CSS to be used in the editor area.<br />use theme css - load style.css from current site theme.<br/>define css - enter path for css file below.<br />TinyMCE default - uses default CSS from editor.'),
+  );
+
+  $form['css']['css_path'] = array(
+    '#type' => 'textfield',
+    '#title' => t('CSS path'),
+    '#default_value' => $profile->settings['css_path'],
+    '#size' => 40,
+    '#maxlength' => 255,
+    '#description' => t('If "Define CSS" has been selected above, enter path to a CSS file or a list of CSS files seperated by a comma.') .'<br />'. t('Available tokens: %b (base path, f.e.: /), %t (path to theme, f.e.: themes/garland)') .'<br />'. t('Examples:') .' css/editor.css,/themes/garland/style.css,%b%t/style.css,http://example.com/external.css',
+  );
+
+  $form['css']['css_classes'] = array(
+    '#type' => 'textarea',
+    '#title' => t('CSS classes'),
+    '#default_value' => $profile->settings['css_classes'],
+    '#description' => t('Optionally define CSS classes for the "Font style" dropdown list.<br />Enter one class on each line in the format: !format. Example: !example<br />If left blank, CSS classes are automatically imported from all loaded stylesheet(s).', array('!format' => '<code>[title]=[class]</code>', '!example' => 'My heading=header1')),
+  );
+
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => $submit_label,
+  );
+
+  return $form;
+}
+
+/**
+ * Submit callback for Wysiwyg profile form.
+ *
+ * @see wysiwyg_profile_form()
+ */
+function wysiwyg_profile_form_submit($form, &$form_state) {
+  // Count enabled plugins for this profile.
+  $plugin_count = 0;
+  foreach ($form_state['values']['buttons'] as $plugin => $buttons) {
+    $form_state['values']['buttons'][$plugin] = array_filter($form_state['values']['buttons'][$plugin]);
+    $plugin_count += count($form_state['values']['buttons'][$plugin]);
+  }
+  // Store only enabled buttons.
+  $form_state['values']['buttons'] = array_filter($form_state['values']['buttons']);
+
+  // Filter enabled roles for this profile.
+  $form_state['values']['rids'] = array_filter($form_state['values']['rids']);
+
+  // Delete existing profile(s) with the current profile name.
+  if (!empty($form_state['values']['old_name'])) {
+    db_query("DELETE FROM {wysiwyg_profile} WHERE name = '%s' OR name = '%s'", $form_state['values']['name'], $form_state['values']['old_name']);
+    db_query("DELETE FROM {wysiwyg_role} WHERE name = '%s' OR name = '%s'", $form_state['values']['name'], $form_state['values']['old_name']);
+  }
+
+  // Remove FAPI values.
+  // @see system_settings_form_submit()
+  unset($form_state['values']['submit'], $form_state['values']['form_id'], $form_state['values']['op'], $form_state['values']['form_token']);
+
+  // Insert new profile data.
+  db_query("INSERT INTO {wysiwyg_profile} (name, settings, plugin_count) VALUES ('%s', '%s', %d)", $form_state['values']['name'], serialize($form_state['values']), $plugin_count);
+  foreach ($form_state['values']['rids'] as $rid => $value) {
+    db_query("INSERT INTO {wysiwyg_role} (name, rid) VALUES ('%s', %d)", $form_state['values']['name'], $rid);
+  }
+
+  if (isset($form_state['values']['old_name'])) {
+    drupal_set_message(t('Wysiwyg profile %name has been updated.', array('%name' => $form_state['values']['name'])));
+  }
+  else {
+    drupal_set_message(t('Wysiwyg profile %name has been created.', array('%name' => $form_state['values']['name'])));
+  }
+  drupal_goto('admin/settings/wysiwyg/profile');
+}
+
+/**
+ * Layout for the buttons in the Wysiwyg Editor profile form.
+ */
+function theme_wysiwyg_admin_button_table(&$form) {
+  $buttons = array();
+
+  // Flatten forms array.
+  foreach (element_children($form) as $name) {
+    foreach (element_children($form[$name]) as $button) {
+      $buttons[] = drupal_render($form[$name][$button]);
+    }
+  }
+
+  // Split checkboxes into rows with 3 columns.
+  $total = count($buttons);
+  $rows = array();
+  for ($i = 0; $i < $total; $i++) {
+    $row    = array();
+    $row[]  = array('data' => $buttons[$i]);
+    if (isset($buttons[++$i])) {
+      $row[]  = array('data' => $buttons[$i]);
+    }
+    if (isset($buttons[++$i])) {
+      $row[]  = array('data' => $buttons[$i]);
+    }
+    $rows[] = $row;
+  }
+
+  $output = theme('table', array(), $rows, array('width' => '100%'));
+
+  return $output;
+}
+
+/**
+ * Display overview of setup Wysiwyg Editor profiles; menu callback.
+ */
+function wysiwyg_profile_overview() {
+  $output = '';
+  $usable = TRUE;
+
+  // Check which wysiwyg editors are installed.
+  $editors = wysiwyg_get_all_editors();
+  $count = count($editors);
+  $errors = array();
+  $links = array();
+  foreach ($editors as $editor => $properties) {
+    if (!$properties['installed']) {
+      $count--;
+      $links[$editor] = t('<a href="!vendor-url">@editor</a> (<a href="!download-url">Download</a>)<br />Extract the archive and copy the folder to the following location:<br /><code>@editor-path</code>', array('!vendor-url' => $properties['vendor url'], '@editor' => $properties['title'], '!download-url' => $properties['download url'], '@editor-path' => $properties['editor path']));
+    }
+    if (isset($properties['error'])) {
+      $errors[] = $properties['error'];
+    }
+  }
+  if ($errors) {
+    foreach ($errors as $message) {
+      drupal_set_message($message, 'error');
+    }
+  }
+  elseif (!$count) {
+    drupal_set_message(t('There are no editor libraries installed currently. The following list contains a list of currently supported editors:') . theme('item_list', $links), 'error');
+    $usable = FALSE;
+  }
+
+  // Check if at least one role is granted access to Wysiwyg Editor.
+  $access_check = user_roles(FALSE, 'access wysiwyg editor');
+  if (!$access_check) {
+    drupal_set_message(t('You must <a href="!access-control-url">assign</a> at least one role with the \'access wysiwyg editor\' permission before creating a profile.', array('!access-control-url' => url('admin/user/permissions'))), 'error');
+    $usable = FALSE;
+  }
+  if (!$usable) {
+    return $output;
+  }
+
+  $profiles = wysiwyg_load_profile();
+  if ($profiles) {
+    $roles = user_roles(FALSE, 'access wysiwyg editor');
+    $header = array(t('Profile'), t('Editor'), t('Roles'), t('Operations'));
+    foreach ($profiles as $p) {
+      $rows[] = array(
+        array('data' => $p->name, 'valign' => 'top'),
+        array('data' => $editors[$p->settings['editor']]['title'], 'valign' => 'top'),
+        array('data' => implode("<br />\n", $p->rids)),
+        array('data' => l(t('Edit'), 'admin/settings/wysiwyg/profile/edit/'. urlencode($p->name)) .' '. l(t('Delete'), 'admin/settings/wysiwyg/profile/delete/'. urlencode($p->name)), 'valign' => 'top'));
+    }
+    $output .= theme('table', $header, $rows);
+  }
+  $output .= '<p>'. t('<a href="!create-profile-url">Create new profile</a>', array('!create-profile-url' => url('admin/settings/wysiwyg/profile/add'))) .'</p>';
+
+  return $output;
+}
+
+/**
+ * Remove a profile from the database.
+ */
+function wysiwyg_profile_delete($name) {
+  db_query("DELETE FROM {wysiwyg_profile} WHERE name = '%s'", $name);
+  db_query("DELETE FROM {wysiwyg_role} WHERE name = '%s'", $name);
+}
+
Index: wysiwyg.api.php
===================================================================
RCS file: wysiwyg.api.php
diff -N wysiwyg.api.php
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ wysiwyg.api.php	14 Oct 2008 18:23:25 -0000
@@ -0,0 +1,63 @@
+<?php
+// $Id: wysiwyg.module,v 1.1 2007/10/07 23:41:21 sun Exp $
+
+/**
+ * hook_wysiwyg_plugin(). Return an array of editor plugins.
+ *
+ * Each wysiwyg editor as well as each contrib module implementing an editor
+ * plugin has to return an associative array of available plugins. Each module
+ * can add one or more plugins and editor buttons.
+ *
+ * Notes for TinyMCE:
+ * A module is able to override almost all TinyMCE initialization settings.
+ * However, modules should only make use of that if a plugin really needs to,
+ * because customized configuration settings may clash with overrides by another
+ * module. TinyMCE automatically assigns the baseURL of your plugin to the plugin
+ * object. If you need to load or access additional files from your plugin
+ * directory, retrieve the path via this.baseURL. tinyMCE.baseURL returns the
+ * path of TinyMCE and not your module. For example:
+ * @code
+ * initInstance: function(inst) {
+ *   tinyMCE.importCSS(inst.getDoc(), this.baseURL + '/myplugin.css');
+ * },
+ * @endcode
+ * 
+ * @param string $editor
+ *   An (lowercase) editor name to return plugins for.
+ * @return array
+ *   An associative array having internal plugin names as keys, an array of
+ *   plugin meta-information as values:
+ *   - type: 'external' (optional); if omitted, wysiwyg editors will likely
+ *     search for the plugin in their own plugins folder.
+ *   - title: A human readable title of the plugin.
+ *   - description: A (one-line) description of the plugin.
+ *   - path: The patch to the javascript plugin.
+ *   - callback: A Drupal menu callback returning the plugin UI. A plugin
+ *     should return a callback *or* a path.
+ *   - icon: An icon (usually 16x16 pixels) for the plugin button (optional).
+ *   - ... Any other custom editor settings (optional).
+ *
+ * @todo Move this template into hooks.php.
+ */
+function hook_wysiwyg_plugin($editor) {
+  switch ($editor) {
+    case 'tinymce':
+      return array(
+        'myplugin' => array(
+          'type'        => 'external',
+          'title'       => t('My plugin title'),
+          'description' => t('My plugin title'),
+          // Regular callback URL for external TinyMCE plugins.
+          'path'        => drupal_get_path('module', 'mymodule') .'/myplugin',
+          // Wysiwyg wrapper plugin AJAX callback.
+          'callback'    => url('myplugin/browse'),
+          'icon'        => drupal_get_path('module', 'mymodule') .'/myplugin/myplugin.png',
+          'extended_valid_elements' => array('tag[attribute1|attribute2=default_value]'),
+          // Might need to be set later on; after retrieving customized editor
+          // layout.
+          'theme_advanced_buttons1' => array(t('Button title (optional)') => 'myplugin'),
+        ),
+      );
+  }
+}
+
Index: wysiwyg.css
===================================================================
RCS file: wysiwyg.css
diff -N wysiwyg.css
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ wysiwyg.css	14 Oct 2008 20:06:47 -0000
@@ -0,0 +1,7 @@
+/* $Id: wysiwyg_editor.css,v 1.1 2008/06/29 22:24:19 sun Exp $ */
+
+/* TinyMCE 2.x */
+.mceToolbarTop a, .mceToolbarBottom a { float: left; }
+.mceSeparatorLine { float: left; margin-top: 3px; }
+.mceSelectList { float: left; margin-bottom: 1px; }
+#mce_editor_0_table, #mce_editor_1_table { clear: left; }
Index: wysiwyg.info
===================================================================
RCS file: wysiwyg.info
diff -N wysiwyg.info
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ wysiwyg.info	14 Oct 2008 20:05:10 -0000
@@ -0,0 +1,5 @@
+; $Id: wysiwyg_editor.info,v 1.5 2008/08/01 15:11:12 sun Exp $
+name = Wysiwyg
+description = Allows users to edit contents with client-side editors.
+package = User interface
+core = 6.x
Index: wysiwyg.install
===================================================================
RCS file: wysiwyg.install
diff -N wysiwyg.install
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ wysiwyg.install	14 Oct 2008 20:22:16 -0000
@@ -0,0 +1,103 @@
+<?php
+// $Id: wysiwyg_editor.install,v 1.11 2008/10/06 23:44:52 sun Exp $
+
+/**
+ * Implementation of hook_schema().
+ */
+function wysiwyg_schema() {
+  $schema = array();
+  $schema['wysiwyg_profile'] = array(
+    'description' => t('Stores Wysiwyg Editor profiles.'),
+    'fields' => array(
+      'name' => array('type' => 'varchar', 'length' => 128, 'not null' => TRUE, 'default' => ''),
+      'settings' => array('type' => 'text', 'size' => 'normal'),
+      'plugin_count' => array('type' => 'int', 'size' => 'tiny', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
+    ),
+    'primary key' => array('name'),
+  );
+  $schema['wysiwyg_role'] = array(
+    'description' => t('Stores user role access permissions for Wysiwyg Editor profiles.'),
+    'fields' => array(
+      'name' => array('type' => 'varchar', 'length' => 128, 'not null' => TRUE, 'default' => ''),
+      'rid' => array('type' => 'int', 'size' => 'tiny', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
+    ),
+    'primary key' => array('name', 'rid'),
+  );
+  return $schema;
+}
+
+/**
+ * Implementation of hook_install().
+ */
+function wysiwyg_install() {
+  drupal_install_schema('wysiwyg');
+  // Import data from old editor modules.
+  wysiwyg_migrate_wysiwyg_editor();
+  wysiwyg_migrate_tinymce();
+}
+
+/**
+ * Implementation of hook_uninstall()
+ */
+function wysiwyg_uninstall() {
+  drupal_uninstall_schema('wysiwyg');
+}
+
+/**
+ * Migrate from Wysiwyg Editor.
+ */
+function wysiwyg_migrate_wysiwyg_editor() {
+  if (db_table_exists('wysiwyg_editor_profile')) {
+    // Import Wysiwyg Editor profiles.
+    db_query('INSERT INTO {wysiwyg_profile} (name, settings, plugin_count) SELECT name, settings, plugin_count FROM {wysiwyg_editor_profile}');
+    // Import Wysiwyg Editor profile role associations.
+    db_query('INSERT INTO {wysiwyg_role} (name, rid) SELECT name, rid FROM {wysiwyg_editor_role}');
+
+    // Disable Wysiwyg Editor module.
+    module_disable(array('wysiwyg_editor'));
+    drupal_set_message(t('Wysiwyg Editor module can be safely uninstalled now.'));
+  }
+}
+
+/**
+ * Migrate from TinyMCE.
+ */
+function wysiwyg_migrate_tinymce() {
+  if (db_table_exists('tinymce_settings')) {
+    $schema = db_result(db_query("SELECT schema_version FROM {system} WHERE name = 'tinymce'"));
+    if ($schema >= 1) {
+      // Import TinyMCE settings.
+      db_query('INSERT INTO {wysiwyg_profile} (name, settings) SELECT name, settings FROM {tinymce_settings}');
+      // Import TinyMCE profile role associations.
+      db_query('INSERT INTO {wysiwyg_role} (name, rid) SELECT name, rid FROM {tinymce_role}');
+
+      // Migrate profile configurations.
+      // Convert buttons/plugins into an associative array and fix plugin count.
+      $profiles = db_query("SELECT name, settings, plugin_count FROM {wysiwyg_profile}");
+      while ($profile = db_fetch_array($profiles)) {
+        $settings = unserialize($profile['settings']);
+        if (isset($settings['form_id'])) {
+          $old_buttons = (isset($settings['buttons']) ? $settings['buttons'] : array());
+          $settings['buttons'] = array();
+          $plugin_count = 0;
+          foreach ($old_buttons as $old_button => $enabled) {
+            list($plugin, $button) = explode('-', $old_button, 2);
+            $settings['buttons'][$plugin][$button] = 1;
+            $plugin_count++;
+          }
+          // We can't use update_sql() here because of curly braces in serialized
+          // array.
+          db_query("UPDATE {wysiwyg_profile} SET settings = '%s', plugin_count = %d WHERE name = '%s'", serialize($settings), $plugin_count, $profile['name']);
+        }
+      }
+
+      // Disable TinyMCE module.
+      module_disable(array('tinymce'));
+      drupal_set_message(t('TinyMCE module can be safely uninstalled now.'));
+    }
+    else {
+      drupal_set_message(t('To migrate your existing TinyMCE settings to Wysiwyg Editor, please update TinyMCE module to the latest official release, and re-install Wysiwyg Editor module.'));
+    }
+  }
+}
+
Index: wysiwyg.js
===================================================================
RCS file: wysiwyg.js
diff -N wysiwyg.js
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ wysiwyg.js	14 Oct 2008 20:23:20 -0000
@@ -0,0 +1,170 @@
+// $Id: wysiwyg.js,v 1.11 2008/10/13 23:16:49 sun Exp $
+
+/**
+ * Initialize editor libraries.
+ *
+ * Some editors need to be initialized before the DOM is fully loaded. The
+ * init hook gives them a chance to do so.
+ */
+Drupal.wysiwygInit = function() {
+  jQuery.each(Drupal.wysiwyg.editor.init, function(editor) {
+    // Clone, so original settings are not overwritten.
+    this(Drupal.wysiwyg.clone(Drupal.settings.wysiwyg.configs[editor]));
+  });
+}
+
+/**
+ * Attach editors to input formats and target elements (f.e. textareas).
+ *
+ * This behavior searches for input format selectors and formatting guidelines
+ * that have been preprocessed by Wysiwyg API. All CSS classes of those elements
+ * with the prefix 'wysiwyg-' are parsed into input format parameters, defining
+ * the configured editor, editor theme, target element id, and variable other
+ * properties, which are passed to the attach/detach hooks of the corresponding
+ * editor.
+ *
+ * Furthermore, an "enable/disable rich-text" toggle link is added after the
+ * target element to allow users to alter its contents in plain text.
+ *
+ * This is executed once, while editor attach/detach hooks can be invoked
+ * multiple times.
+ *
+ * @param context
+ *   A DOM element, supplied by Drupal.attachBehaviors().
+ */
+Drupal.behaviors.attachWysiwyg = function(context) {
+  $('.wysiwyg:not(.wysiwyg-processed)', context).each(function() {
+    // Parse the element's CSS classes into parameters.
+    // Format is wysiwyg-name-value.
+    var classes = this.className.split(' ');
+    var params = {};
+    for (var i in classes) {
+      if (classes[i].substr(0, 8) == 'wysiwyg-') {
+        var parts = classes[i].split('-');
+        var value = parts.slice(2).join('-');
+        params[parts[1]] = value;
+      }
+    }
+    $this = $(this);
+    // Directly attach this editor, if the input format is enabled or there is
+    // only one input format at all.
+    if (($this.is(':input') && $this.is(':checked')) || $this.is('div')) {
+      Drupal.wysiwygAttachToggleLink(context, params);
+      Drupal.wysiwygAttach(context, params);
+    }
+    // Attach onChange handlers to input format selector elements.
+    // @todo To support different editors on the same page, we need to store
+    //   the last attached editor of each target element separately.
+    if ($this.is(':input')) {
+      $this.change(function() {
+        Drupal.wysiwygDetach(context, params);
+        Drupal.wysiwygAttach(context, params);
+      });
+    }
+    $this.addClass('wysiwyg-processed');
+  });
+}
+
+/**
+ * Attach an editor to a target element.
+ *
+ * This tests whether the passed in editor implements the attach hook and
+ * invokes it if available. Editor profile settings are cloned first, so they
+ * cannot be overridden. After attaching the editor, the toggle link is shown
+ * again, except in case we are attaching no editor.
+ *
+ * @param context
+ *   A DOM element, supplied by Drupal.attachBehaviors().
+ * @param params
+ *   An object containing input format parameters.
+ */
+Drupal.wysiwygAttach = function(context, params) {
+  if (typeof Drupal.wysiwyg.editor.attach[params.editor] == 'function') {
+    // Attach editor.
+    Drupal.wysiwyg.editor.attach[params.editor](context, params, Drupal.wysiwyg.clone(Drupal.settings.wysiwyg.configs[params.editor]));
+    // Display toggle link.
+    $('#wysiwyg-toggle-' + params.field).show();
+  }
+  // Hide toggle link in case no editor is attached.
+  if (params.editor == 'none') {
+    $('#wysiwyg-toggle-' + params.field).hide();
+  }
+}
+
+/**
+ * Detach all editors from a target element.
+ *
+ * Until there is a central registry of target elements storing the currently
+ * attached editor, we simply invoke the detach hook of all editors to ensure
+ * that no editor is attached to the target element.
+ *
+ * @param context
+ *   A DOM element, supplied by Drupal.attachBehaviors().
+ * @param params
+ *   An object containing input format parameters.
+ */
+Drupal.wysiwygDetach = function(context, params) {
+  jQuery.each(Drupal.wysiwyg.editor.detach, function(editor) {
+    this(context, params);
+  });
+}
+
+/**
+ * Append a editor toggle link to a target element.
+ *
+ * @param context
+ *   A DOM element, supplied by Drupal.attachBehaviors().
+ * @param params
+ *   An object containing input format parameters.
+ */
+Drupal.wysiwygAttachToggleLink = function(context, params) {
+  var text = document.createTextNode(Drupal.settings.wysiwyg.status ? Drupal.settings.wysiwyg.disable : Drupal.settings.wysiwyg.enable);
+  var a = document.createElement('a');
+  $(a).toggle(
+    function() {
+      Drupal.wysiwygDetach(context, params);
+      $('#wysiwyg-toggle-' + params.field).html(Drupal.settings.wysiwyg.enable).blur();
+      // After disabling the editor, re-attach default behaviors.
+      Drupal.wysiwyg.editor.attach.none(context, params);
+    },
+    function() {
+      // Before enabling the editor, detach default behaviors.
+      Drupal.wysiwyg.editor.detach.none(context, params);
+      Drupal.wysiwygAttach(context, params);
+      $('#wysiwyg-toggle-' + params.field).html(Drupal.settings.wysiwyg.disable).blur();
+    })
+    .attr('id', 'wysiwyg-toggle-' + params.field)
+    .attr('href', 'javascript:void(0);')
+    .append(text);
+  var div = document.createElement('div');
+  $(div).append(a);
+  $('#' + params.field).after(div);
+}
+
+/**
+ * Clone a configuration object recursively.
+ *
+ * @param obj
+ *   The object to clone.
+ *
+ * @return
+ *   A copy of the passed in object.
+ */
+Drupal.wysiwyg.clone = function(obj) {
+  var clone = {};
+  for (i in obj) {
+    if ((typeof obj[i] == 'object') || (typeof obj[i] == 'array')) {
+      clone[i] = Drupal.wysiwyg.clone(obj[i]);
+    }
+    else {
+      clone[i] = obj[i];
+    }
+  }
+  return clone;
+}
+
+/**
+ * Allow certain editor libraries to initialize before the DOM is loaded.
+ */
+Drupal.wysiwygInit();
+
Index: wysiwyg.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/wysiwyg.module,v
retrieving revision 1.1
diff -u -p -r1.1 wysiwyg.module
--- wysiwyg.module	7 Oct 2007 23:41:21 -0000	1.1
+++ wysiwyg.module	14 Oct 2008 20:39:31 -0000
@@ -3,289 +3,733 @@
 
 /**
  * @file
- * Implements a WYSIWYG API/framework/controller for Drupal.
- * Implements a generic WYSIWYG editor (wrapper) module for Drupal.
+ * Integrate client-side editors with Drupal.
  */
 
 /**
+ * Implementation of hook_menu().
+ */
+function wysiwyg_menu() {
+  $items = array();
+  $items['admin/settings/wysiwyg/profile'] = array(
+    'title' => 'Wysiwyg',
+    'page callback' => 'wysiwyg_admin',
+    'description' => 'Configure client-side editor profiles.',
+    'access arguments' => array('administer site configuration'),
+    'file' => 'wysiwyg.admin.inc',
+  );
+  return $items;
+}
+
+/**
+ * Implementation of hook_theme().
+ */
+function wysiwyg_theme() {
+  return array(
+    'wysiwyg_admin_button_table' => array('arguments' => array('form')),
+  );
+}
+
+/**
+ * Implementation of hook_help().
+ */
+function wysiwyg_help($path, $arg) {
+  switch ($path) {
+    case 'admin/settings/wysiwyg/profile':
+    case 'admin/help#wysiwyg':
+      $output = '<p>'. t('Profiles can be defined based on user roles. A Wysiwyg Editor profile can define which pages receive this Wysiwyg Editor capability, what buttons or themes are enabled for the editor, how the editor is displayed, and a few other editor functions. Lastly, only users with the %permission <a href="!url">user permission</a> are able to use Wysiwyg Editor.', array('%permission' => 'access wysiwyg editor', '!url' => url('admin/user/permissions'))) .'</p>';
+      return $output;
+  }
+}
+
+/**
  * Implementation of hook_perm().
- *
- * Considerations:
- * - Allow to access an editor.
- * - Allow to access a particular editor profile.
  */
 function wysiwyg_perm() {
-  // Implement common permissions.
-  $permissions = array('use wysiwyg editor', 'administer wysiwyg settings');
-  // Fetch editors or editor profiles.
-  
-  return $permissions;
+  return array('access wysiwyg editor');
 }
 
 /**
- * Implementation of hook_menu().
+ * Implementation of hook_elements().
+ *
+ * Before Drupal 7, there is no way to easily identify form fields that are
+ * input format enabled. This is a workaround: We assign a form #after_build
+ * processing callback that is executed on all forms after they have been
+ * completely built, so that form elements already are in their effective order
+ * and position.
+ *
+ * @see wysiwyg_process_form()
+ *
+ * @todo Remove #wysiwyg_style; the GUI for an editor should be solely handled
+ *   via profiles, when profiles are attached to an input format. It makes no
+ *   sense to display TinyMCE's simple GUI/theme for the user signature, when
+ *   the input format allows users to use advanced HTML and hence, editor
+ *   plugins.  Fix this here, in wysiwyg_process_element(), and lastly
+ *   in wysiwyg_get_editor_config().
  */
-function wysiwyg_menu($may_cache) {
-  $items = array();
-  
-  if ($may_cache) {
-    // General wysiwyg settings, including editor selection, editor/user roles
-    // mapping.
-    
-    // Editor profile setup and custom wysiwyg controller overrides.
-    // Note: Depends on resulting implementation of wysiwyg_controller()
-
-    // Enable/disable default editor plugins and Drupal plugins.
-    
-    // Configure editor-specific layout, including layout of plugins.
-    
-    // Generic Drupal plugin wrapper callback (AJAX).
+function wysiwyg_elements() {
+  $type = array();
+  if (user_access('access wysiwyg editor')) {
+    // @todo Derive editor theme from input format.
+    $type['textarea'] = array('#wysiwyg_style' => 'advanced');
+    $type['form'] = array('#after_build' => array('wysiwyg_process_form'));
   }
-  
-  return $items;
+  return $type;
+}
+
+/**
+ * Implementation of hook_form_alter().
+ */
+function wysiwyg_form_alter(&$form, &$form_state) {
+  // Disable 'teaser' textarea.
+  if (isset($form['body_field'])) {
+    unset($form['body_field']['teaser_js']);
+    $form['body_field']['teaser_include'] = array();
+  }
+}
+
+/**
+ * Process a textarea for Wysiwyg Editor.
+ *
+ * This way, we can recurse into the form and search for certain, hard-coded
+ * elements that have been added by filter_form(). If an input format selector
+ * or input format guidelines element is found, we assume that the preceding
+ * element is the corresponding textarea and use it's #id for attaching
+ * client-side editors.
+ *
+ * @see wysiwyg_elements(), filter_form()
+ */
+function wysiwyg_process_form(&$form) {
+  // Iterate over element children; resetting array keys to access last index.
+  if ($children = array_values(element_children($form))) {
+    foreach ($children as $index => $item) {
+      $element = &$form[$item];
+
+      // filter_form() always uses the key 'format'. We need a type-agnostic
+      // match to prevent false positives. Also, there must have been at least
+      // one element on this level.
+      if ($item === 'format' && $index > 0) {
+        // Make sure we either match a input format selector or input format
+        // guidelines (displayed if user has access to one input format only).
+        if ((isset($element['#type']) && $element['#type'] == 'fieldset') || isset($element['format']['guidelines'])) {
+          // The element before this element is the target form field.
+          $field = &$form[$children[$index - 1]];
+
+          // Disable #resizable to avoid resizable behavior to hi-jack the UI,
+          // but load the behavior, so the 'none' editor can attach/detach it.
+          $extra_class = '';
+          if (!empty($field['#resizable'])) {
+            // Due to our CSS class parsing, we can add arbitrary parameters
+            // for each input format.
+            $extra_class = ' wysiwyg-resizable-1';
+            $field['#resizable'] = FALSE;
+            drupal_add_js('misc/textarea.js');
+          }
+
+          // Determine the available input formats. The last child element is a
+          // link to "More information about formatting options". When only one
+          // input format is displayed, we also have to remove formatting
+          // guidelines, stored in the child 'format'.
+          $formats = element_children($element);
+          array_pop($formats);
+          unset($formats['format']);
+          foreach ($formats as $format) {
+            // Default to 'none' editor (Drupal's default behaviors).
+            $editor = 'none';
+            $theme = '';
+            // Fetch the profile associated to this input format.
+            $profile = wysiwyg_get_profile($format);
+            if ($profile) {
+              $editor = $profile->settings['editor'];
+              // Check editor theme (and reset it if not/no longer available).
+              $theme = wysiwyg_get_editor_themes($profile, $field['#wysiwyg_style']);
+
+              // Add profile settings for this input format.
+              wysiwyg_add_editor_settings($profile, $theme);
+              // Add plugin settings for this input format.
+              wysiwyg_add_plugin_settings($profile);
+
+              $theme = ' wysiwyg-theme-'. $theme;
+            }
+
+            // Use a prefix/suffix for a single input format, or attach to input
+            // format selector radio buttons.
+            if (isset($element['format']['guidelines'])) {
+              $element[$format]['#prefix'] = '<div class="wysiwyg wysiwyg-editor-'. $editor .' wysiwyg-field-'. $field['#id'] . $theme . $extra_class .'">';
+              $element[$format]['#suffix'] = '</div>';
+            }
+            else {
+              if (isset($element[$format]['#attributes']['class'])) {
+                $element[$format]['#attributes']['class'] .= ' ';
+              }
+              else {
+                $element[$format]['#attributes']['class'] = '';
+              }
+              $element[$format]['#attributes']['class'] .= 'wysiwyg wysiwyg-editor-'. $editor .' wysiwyg-field-'. $field['#id'] . $theme . $extra_class;
+            }
+          }
+        }
+        // If this element is 'format', do not recurse further.
+        continue;
+      }
+      // Recurse into children.
+      wysiwyg_process_form($element);
+    }
+  }
+  return $form;
 }
 
 /**
- * Handle an editor profile.
+ * Determine the profile to use for a given input format id.
+ *
+ * This function also performs sanity checks for the configured editor in a
+ * profile to ensure that we do not load a malformed editor.
  *
- * This function should adopt the implementation of node objects in Drupal. It
- * should at least allow to load, save and duplicate an editor profile. Since
- * not all editors support the same settings, the array of settings probably
- * needs to be stored serialized in the database.
+ * @param $format
+ *   The internal id of an input format.
  *
- * Considerations:
- * - Will Drupal editor plugins need to hook into profile operations?
- * - Discuss moving profile rules into render.module.
+ * @return
+ *   A wysiwyg profile.
  *
- * @param string $op
- *   An operation to perform, one of 'load', 'save', 'duplicate'.
- * @param string $profile
- *   An editor profile object containing at least an id, title, array of plugins
- *   and an array of settings.
+ * @see wysiwyg_load_editor(), wysiwyg_get_editor()
  *
- * @todo '_profile' is an ambigious hook in Drupal. Needs a unique name.
+ * @todo Replace wysiwyg_current_profile() with a input format to profile
+ *   association.
  */
-function wysiwyg_profile() {
+function wysiwyg_get_profile($format) {
+  // Determine whether this input format has PHP filter enabled.
+  // Temporary, until input format to profile associations are in place.
+  $filters = filter_list_format($format);
+  foreach ($filters as $filter) {
+    if ($filter->module == 'php') {
+      return FALSE;
+    }
+  }
+  if ($profile = wysiwyg_load_profile(wysiwyg_current_profile())) {
+    if (wysiwyg_load_editor($profile)) {
+      return $profile;
+    }
+  }
+  return FALSE;
 }
 
 /**
- * Wysiwyg editor execution controller.
- * 
- * Disables an editor for certain fields that were never intended to be edited
- * with an editor.
- * @see http://drupal.org/node/81297
- *
- * Considerations:
- * - Take D6 FAPI3 / widgets into account (see #138706 and #86535).
- * - Run registration either in hook_elements() or hook_form_alter().
- *   form_alter() might also allow to load needed JS/CSS.
- * - Use an attribute or class name or both (editor code analysis needed). Or
- *   alter field #type 'textarea' to 'html' (IIRC, suggested by moshe).
- * - Disable an editor for fields using the PHP input format.
- * - D6: Perhaps depend on the input format to *enable* a field.
- * - Allow to take the current path into account.
- * - Allow to force an editor for certain CCK fields (f.e. user input).
- * - Disable core's #resizable if an editor does not support it.
- * - Allow contrib modules to extend this definition.
- * - Allow to manually extend this list via wysiwyg settings page.
- * - Include an editor profile or user role to limit/enhance available plugins.
- * - Take other input formats (e.g. bbcode) into account.
- *
- * @todo Rename function to appropriate/chosen Drupal hook.
- */
-function wysiwyg_controller() {
+ * Load an editor library and initialize basic Wysiwyg settings.
+ *
+ * @param $profile
+ *   A wysiwyg editor profile.
+ *
+ * @return
+ *   TRUE if the editor has been loaded, FALSE if not.
+ *
+ * @see wysiwyg_get_profile()
+ */
+function wysiwyg_load_editor($profile) {
+  static $settings_added;
+  static $loaded = array();
+
+  $name = $profile->settings['editor'];
+  // Library files must be loaded only once.
+  if (!isset($loaded[$name])) {
+    // Load editor.
+    $editor = wysiwyg_get_editor($name);
+    if ($editor) {
+      // Determine library files to load.
+      // @todo Allow to configure the library/execMode to use.
+      if (isset($profile->settings['library']) && isset($editor['libraries'][$profile->settings['library']])) {
+        $library = $profile->settings['library'];
+        $files = $editor['libraries'][$profile->settings['library']]['files'];
+      }
+      else {
+        // Fallback to the first by default (external libraries can change).
+        $library = key($editor['libraries']);
+        $files = array_shift($editor['libraries']);
+        $files = $files['files'];
+      }
+      foreach ($files as $file) {
+        drupal_add_js($editor['library path'] . '/' . $file);
+      }
+      // Load JavaScript integration files for this editor.
+      if (isset($editor['js files'])) {
+        $files = $editor['js files'];
+      }
+      foreach ($files as $file) {
+        drupal_add_js($editor['js path'] . '/' . $file, 'module', 'footer');
+      }
+
+      $status = wysiwyg_user_get_status($profile);
+      drupal_add_js(array('wysiwyg' => array(
+        'configs' => array($editor['name'] => array()),
+        'showToggle' => $profile->settings['show_toggle'],
+        'status' => $status,
+        // If JS compression is enabled, at least TinyMCE is unable to determine
+        // its own base path and exec mode since it can't find the script name.
+        'editorBasePath' => base_path() . $editor['library path'],
+        'execMode' => $library,
+      )), 'setting');
+
+      $loaded[$name] = TRUE;
+    }
+    else {
+      $loaded[$name] = FALSE;
+    }
+  }
+
+  // Add basic Wysiwyg settings if any editor has been added.
+  if (!isset($settings_added) && $loaded[$name]) {
+    drupal_add_js(array('wysiwyg' => array(
+      'configs' => array(),
+      'disable' => t('Disable rich-text'),
+      'enable' => t('Enable rich-text'),
+    )), 'setting');
+
+    // Initialize our namespaces in the *header* to do not force editor
+    // integration scripts to check and define Drupal.wysiwyg on its own.
+    drupal_add_js(wysiwyg_get_path('wysiwyg.init.js'));
+
+    // The 'none' editor is a special editor implementation, allowing us to
+    // attach and detach regular Drupal behaviors just like any other editor.
+    drupal_add_js(wysiwyg_get_path('editors/js/none.js'), 'module', 'footer');
+
+    // Add wysiwyg.js to the footer to ensure it's executed after the
+    // Drupal.settings array has been rendered and populated. Also, since editor
+    // library initialization functions must be loaded first by the browser,
+    // Drupal.wysiwygInit() must be executed AFTER editors registered
+    // their callbacks, and BEFORE Drupal.behaviors are applied, this must come
+    // last.
+    drupal_add_js(wysiwyg_get_path('wysiwyg.js'), 'module', 'footer');
+
+    // Add our stylesheet to stack editor buttons into one row.
+    // @todo This is TinyMCE 2.x specific at the moment.
+    drupal_add_css(wysiwyg_get_path('wysiwyg.css'));
+
+    $settings_added = TRUE;
+  }
+
+  return $loaded[$name];
+}
+
+/**
+ * Register a theme.
+ */
+function wysiwyg_add_editor_settings($profile, $theme) {
+  static $themes = array();
+
+  if (!isset($themes[$theme])) {
+    $config = wysiwyg_get_editor_config($profile, $theme);
+    // Convert the config values into the form expected by Wysiwyg Editor.
+    // @todo Is this conversion TinyMCE specific?
+    foreach ($config as $key => $value) {
+      if (is_bool($value)) {
+        continue;
+      }
+      if (is_array($value)) {
+        $config[$key] = implode(',', $config[$key]);
+      }
+    }
+    drupal_add_js(array('wysiwyg' => array('configs' => array($profile->settings['editor'] => array($theme => $config)))), 'setting');
+    $themes[$theme] = TRUE;
+  }
 }
 
 /**
- * hook_wysiwyg_plugin(). Return an array of editor plugins.
- *
- * Each wysiwyg editor as well as each contrib module implementing an editor
- * plugin has to return an associative array of available plugins. Each module
- * can add one or more plugins and editor buttons.
- *
- * Notes for TinyMCE:
- * A module is able to override almost all TinyMCE initialization settings.
- * However, modules should only make use of that if a plugin really needs to,
- * because customized configuration settings may clash with overrides by another
- * module. TinyMCE automatically assigns the baseURL of your plugin to the plugin
- * object. If you need to load or access additional files from your plugin
- * directory, retrieve the path via this.baseURL. tinyMCE.baseURL returns the
- * path of TinyMCE and not your module. For example:
- * @code
- * initInstance: function(inst) {
- *   tinyMCE.importCSS(inst.getDoc(), this.baseURL + '/myplugin.css');
- * },
- * @endcode
+ * Add settings for external plugins.
  * 
- * @param string $editor
- *   An (lowercase) editor name to return plugins for.
- * @return array
- *   An associative array having internal plugin names as keys, an array of
- *   plugin meta-information as values:
- *   - type: 'external' (optional); if omitted, wysiwyg editors will likely
- *     search for the plugin in their own plugins folder.
- *   - title: A human readable title of the plugin.
- *   - description: A (one-line) description of the plugin.
- *   - path: The patch to the javascript plugin.
- *   - callback: A Drupal menu callback returning the plugin UI. A plugin
- *     should return a callback *or* a path.
- *   - icon: An icon (usually 16x16 pixels) for the plugin button (optional).
- *   - ... Any other custom editor settings (optional).
- *
- * @todo Move this template into hooks.php.
- */
-function hook_wysiwyg_plugin($editor) {
-  switch ($editor) {
-    case 'tinymce':
-      return array(
-        'myplugin' => array(
-          'type'        => 'external',
-          'title'       => t('My plugin title'),
-          'description' => t('My plugin title'),
-          // Regular callback URL for external TinyMCE plugins.
-          'path'        => drupal_get_path('module', 'mymodule') .'/myplugin',
-          // Wysiwyg wrapper plugin AJAX callback.
-          'callback'    => url('myplugin/browse'),
-          'icon'        => drupal_get_path('module', 'mymodule') .'/myplugin/myplugin.png',
-          'extended_valid_elements' => array('tag[attribute1|attribute2=default_value]'),
-          // Might need to be set later on; after retrieving customized editor
-          // layout.
-          'theme_advanced_buttons1' => array(t('Button title (optional)') => 'myplugin'),
-        ),
-      );
+ * @param $profile
+ *   A wysiwyg editor profile.
+ */
+function wysiwyg_add_plugin_settings($profile) {
+  static $plugins_added = array();
+  
+  if (!isset($plugins_added[$profile->settings['editor']])) {
+    $plugins = array();
+    $editor = wysiwyg_get_editor($profile->settings['editor']);
+    // Collect editor plugins provided via hook_wysiwyg_plugin().
+    $info = module_invoke_all('wysiwyg_plugin', $editor['name'], $editor['installed version']);
+    // Only keep enabled plugins in this profile.
+    foreach ($info as $plugin => $meta) {
+      if (!isset($profile->settings['buttons'][$plugin])) {
+        unset($info[$plugin]);
+      }
+    }
+
+    if (isset($editor['plugin settings callback']) && function_exists($editor['plugin settings callback'])) {
+      $plugins = $editor['plugin settings callback']($editor, $profile, $info);
+    }
+
+    drupal_add_js(array('wysiwyg' => array('plugins' => array($profile->settings['editor'] => $plugins))), 'setting');
+
+    $plugins_added[$profile->settings['editor']] = TRUE;
   }
 }
 
 /**
- * Implementation of hook_wysiwyg_plugin().
+ * Grab the themes available to Wysiwyg Editor.
  *
- * Implements a generic wrapper plugin for Drupal (module-based) editor plugins.
+ * Wysiwyg Editor themes control the functionality and buttons that are available to a
+ * user. Themes are only looked for within the default Wysiwyg Editor theme directory.
  *
- * Considerations:
- * - By comparing the javascript of available editor plugins, most of them are
- *   based on img_assist.
- * - An editor plugin basically consists of a title, icon (button), description
- *   (localized) and menu path returning the actual Drupal module. This
- *   meta-information is available via hook_wysiwyg_plugin().
- * - Each editor implements its own plugin API, but the JS-PHP-JS communication
- *   is always the same.
- * - Since Drupal 5, jQuery is always available and allows to perform AJAX/HTTP
- *   requests at any time.
- * - Each editor plugin returns HTML via Javascript to the editor.
- * - Do we really require each Drupal editor plugin to write their own
- *   javascript or are we able to supply Drupal editor plugins to all editors
- *   through a wrapper module?
- * - We can transform a regular editor plugin template into a wrapper plugin or
- *   we can use a Drupal menu path to serve a editor plugin template which
- *   (optionally) already has additional plugin code injected.
- */
-function wysiwyg_wysiwyg_plugin($editor) {
-  switch ($editor) {
-    case 'tinymce':
-      // Define generic plugin properties.
-      $path = drupal_get_path('module', 'wysiwyg') .'/wrapper/tinymce'
-      
-      // Load all Drupal editor plugins into an array.
-      $plugins = module_invoke_all('wysiwyg_plugin', $editor);
-      
-      // Define all Drupal editor plugins by looping through the array,
-      // omitting all non-'external' plugins; assigning type, title, icon,
-      // custom editor settings and most important:
-      // wrapper plugin path followed by callback path (if JS: use query string).
-      
-      
-      return $plugins;
+ * @param $profile
+ *   A wysiwyg editor profile; passed/altered by reference.
+ * @param $selected_theme
+ *   An optional theme name that ought to be used.
+ *
+ * @return
+ *   An array of theme names, or a single, checked theme name if $selected_theme
+ *   was given.
+ */
+function wysiwyg_get_editor_themes(&$profile, $selected_theme = NULL) {
+  static $themes = array();
+
+  if (!isset($themes[$profile->settings['editor']])) {
+    $editor = wysiwyg_get_editor($profile->settings['editor']);
+    if (isset($editor['themes callback']) && function_exists($editor['themes callback'])) {
+      $themes[$editor['name']] = $editor['themes callback']($editor, $profile);
+    }
+    // Fallback to 'default' otherwise.
+    else {
+      $themes[$editor['name']] = array('default');
+    }
   }
+
+  // Check optional $selected_theme argument, if given.
+  if (isset($selected_theme)) {
+    // If the passed theme name does not exist, use the first available.
+    if (!isset($themes[$profile->settings['editor']][$selected_theme])) {
+      $selected_theme = $profile->settings['theme'] = $themes[$profile->settings['editor']][0];
+    }
+  }
+
+  return isset($selected_theme) ? $selected_theme : $themes[$profile->settings['editor']];
 }
 
 /**
- * Return an array of editor plugins.
- *
- * @param string $op
- *   A performed action:
- *   - 'list': Plugins are about to be listed or registered.
- *   - 'load': Plugins are about to be loaded.
- * @param string $editor
- *   An (lowercase) editor name to retrieve plugins for.
+ * Return plugin metadata from the plugin registry.
  *
- * @see hook_wysiwyg_plugin()
+ * @param $editor_name
+ *   The internal name of an editor to return plugins for.
  *
- * @todo Implement TinyMCE-specific code as hook into wysiwyg_tinymce.inc.
+ * @return
+ *   An array for each plugin.
  */
-function wysiwyg_get_plugins($op, $editor) {
-  static $plugins;
+function wysiwyg_get_plugins($editor_name) {
+  $plugins = array();
+  if (!empty($editor_name)) {
+    $editor = wysiwyg_get_editor($editor_name);
+    // Add internal editor plugins.
+    if (isset($editor['plugin callback']) && function_exists($editor['plugin callback'])) {
+      $plugins = $editor['plugin callback']($editor);
+    }
+    // Load our own plugins.
+    include_once drupal_get_path('module', 'wysiwyg') .'/wysiwyg.plugins.inc';
   
-  if (!isset($plugins)) {
-    $plugins = module_invoke_all('wysiwyg_plugin', $editor);
+    // Add editor plugins provided via hook_wysiwyg_plugin().
+    $plugins = array_merge($plugins, module_invoke_all('wysiwyg_plugin', $editor['name'], $editor['installed version']));
   }
-  
-  switch ($editor) {
-    case 'tinymce':
-      if ($op == 'list') {
-        // Do not alter cached array.
-        $plugin_list = $plugins;
-        foreach ($plugin_list as $name => $plugin) {
-          if ($plugin['type'] == 'external') {
-            $plugin_list[$name] = _wysiwyg_plugin_name('add', $name);
-          }
-        }
-        return $plugin_list;
-      }
-      else if ($op == 'load') {
-        static $plugins_added;
-        
-        if (!isset($plugins_added)) {
-          $plugins_added = TRUE;
-
-          $init_plugins = '';
-          foreach ($plugins as $name => $plugin) {
-            // Ensure there is no leading hiven in external plugin names.
-            $name = _wysiwyg_plugin_name('remove', $name);
-            
-            $init_plugins .= "tinyMCE.loadPlugin('$name', '". base_path() . $plugin['path'] ."');\n";
-          }
-          if (!empty($init_plugins)) {
-            drupal_add_js($init_plugins, 'inline');
-          }
-        }
+  return $plugins;
+}
+
+/**
+ * Return an array of initial Wysiwyg Editor config options from the current role.
+ */
+function wysiwyg_get_editor_config($profile, $theme) {
+  $editor = wysiwyg_get_editor($profile->settings['editor']);
+  $settings = array();
+  if (!empty($editor['settings callback']) && function_exists($editor['settings callback'])) {
+    $settings = $editor['settings callback']($editor, $profile->settings, $theme);
+  }
+  return $settings;
+}
+
+/**
+ * Return the name of the current user's default profile.
+ */
+function wysiwyg_current_profile() {
+  global $user;
+  static $profile_name;
+
+  if (!isset($profile_name)) {
+    $profile_name = db_result(db_query_range('SELECT p.name FROM {wysiwyg_profile} p INNER JOIN {wysiwyg_role} r ON r.name = p.name WHERE r.rid IN (%s) ORDER BY plugin_count DESC', implode(',', array_keys($user->roles)), 0, 1));
+  }
+  return $profile_name;
+}
+
+/**
+ * Load all profiles. Just load one profile if $name is passed in.
+ */
+function wysiwyg_load_profile($name = '') {
+  static $profiles;
+
+  // If $name is passed from wysiwyg_current_profile(), it may be FALSE,
+  // which means the user is not allowed to use a wysiwyg editor.
+  if ($name === FALSE) {
+    return FALSE;
+  }
+  if (!isset($profiles)) {
+    $profiles = array();
+    $roles = user_roles();
+    $result = db_query('SELECT * FROM {wysiwyg_profile}');
+    while ($profile = db_fetch_object($result)) {
+      $profile->settings = unserialize($profile->settings);
+      $result2 = db_query("SELECT rid FROM {wysiwyg_role} WHERE name = '%s'", $profile->name);
+      $profile_roles = array();
+      while ($role = db_fetch_object($result2)) {
+        $profile_roles[$role->rid] = $roles[$role->rid];
       }
-      break;
+      $profile->rids = $profile_roles;
+
+      $profiles[$profile->name] = $profile;
+    }
+  }
+
+  return ($name && isset($profiles[$name]) ? $profiles[$name] : ($name ? FALSE : $profiles));
+}
+
+/**
+ * Implementation of hook_user().
+ */
+function wysiwyg_user($type, &$edit, &$user, $category = NULL) {
+  if ($type == 'form' && $category == 'account' && user_access('access wysiwyg editor')) {
+    $profile = wysiwyg_user_get_profile($user);
+    if (isset($profile->settings['user_choose']) && $profile->settings['user_choose']) {
+      $form['wysiwyg'] = array(
+        '#type' => 'fieldset',
+        '#title' => t('Wysiwyg Editor settings'),
+        '#weight' => 10,
+        '#collapsible' => TRUE,
+        '#collapsed' => TRUE,
+      );
+      $form['wysiwyg']['wysiwyg_status'] = array(
+        '#type' => 'checkbox',
+        '#title' => t('Enable editor by default'),
+        '#default_value' => isset($user->wysiwyg_status) ? $user->wysiwyg_status : (isset($profile->settings['default']) ? $profile->settings['default'] : FALSE),
+        '#return_value' => 1,
+        '#description' => t('If enabled, rich-text editing is enabled by default in textarea fields.'),
+      );
+      return array('wysiwyg' => $form);
+    }
+  }
+  if ($type == 'validate') {
+    return array('wysiwyg_status' => $edit['wysiwyg_status']);
+  }
+}
+
+function wysiwyg_user_get_profile($account) {
+  $profile_name = db_result(db_query('SELECT s.name FROM {wysiwyg_profile} s INNER JOIN {wysiwyg_role} r ON r.name = s.name WHERE r.rid IN (%s)', implode(',', array_keys($account->roles))));
+  if ($profile_name) {
+    return wysiwyg_load_profile($profile_name);
+  }
+  else {
+    return FALSE;
+  }
+}
+
+function wysiwyg_user_get_status($profile) {
+  global $user;
+  $settings = $profile->settings;
+
+  if ($settings['user_choose'] && isset($user->wysiwyg_status)) {
+    $status = $user->wysiwyg_status;
+  }
+  else {
+    $status = isset($settings['default']) ? $settings['default'] : FALSE;
   }
-} 
 
+  return $status;
+}
 
 /**
- * Add or remove leading hiven to/of (external) plugin names.
+ * @defgroup wysiwyg_api Wysiwyg API
+ * @{
  *
- * Externally loaded TinyMCE plugins need to be prefixed with a hiven. TinyMCE
- * will not try to add and load external plugins from the default plugins
- * folder.
- * 
- * @param string $op
- *   Operation to perform, 'add' or 'remove'.
- * @param string $editor
- *   An editor name.
- * @param string $name
- *   A plugin name.
- *
- * @todo Implement TinyMCE-specific code as hook into wysiwyg_tinymce.inc.
- */
-function _wysiwyg_plugin_name($op, $editor, $name) {
-  switch ($editor) {
-    case 'tinymce':
-      if ($op == 'add') {
-        if (strpos($name, '-') !== 0) {
-          return '-'. $name;
-        }
-        return $name;
+ * @todo Forked from Panels; abstract into a separate API module that allows
+ *   contrib modules to define supported include/plugin types.
+ */
+
+/**
+ * Return library information for a given editor.
+ *
+ * @param $name
+ *   The internal name of an editor.
+ *
+ * @return
+ *   The library information for the editor, or FALSE if $name is unknown or not
+ *   installed properly.
+ */
+function wysiwyg_get_editor($name) {
+  $editors = wysiwyg_get_all_editors();
+  return isset($editors[$name]) && $editors[$name]['installed'] ? $editors[$name] : FALSE;
+}
+
+/**
+ * Compile a list holding all supported editors including installed editor version information.
+ */
+function wysiwyg_get_all_editors() {
+  static $editors;
+
+  if (isset($editors)) {
+    return $editors;
+  }
+
+  $editors = wysiwyg_load_includes('editors', 'editor');
+  foreach ($editors as $editor => $properties) {
+    // Fill in required properties.
+    $editors[$editor] += array(
+      'title' => '',
+      'vendor url' => '',
+      'download url' => '',
+      'editor path' => wysiwyg_get_path($properties['name']),
+      'library path' => wysiwyg_get_path($properties['name']),
+      'libraries' => array(),
+      'version callback' => NULL,
+      'themes callback' => NULL,
+      'settings callback' => NULL,
+      'plugin callback' => NULL,
+      'plugin settings callback' => NULL,
+      'versions' => array(),
+      'js path' => $properties['path'] .'/js',
+      'css path' => $properties['path'] .'/css',
+    );
+    // Check whether library is present.
+    if (!($editors[$editor]['installed'] = file_exists($properties['library path']))) {
+      continue;
+    }
+    // Detect library version.
+    if (function_exists($editors[$editor]['version callback'])) {
+      $editors[$editor]['installed version'] = $editors[$editor]['version callback']($properties);
+    }
+    if (empty($editors[$editor]['installed version'])) {
+      $editors[$editor]['error'] = t('The version of %editor could not be detected.', array('%editor' => $properties['title']));
+      $editors[$editor]['installed'] = FALSE;
+      continue;
+    }
+    // Determine to which supported version the installed version maps.
+    ksort($editors[$editor]['versions']);
+    $version = 0;
+    foreach ($editors[$editor]['versions'] as $supported_version => $version_properties) {
+      if (version_compare($editors[$editor]['installed version'], $supported_version, '>=')) {
+        $version = $supported_version;
       }
-      else {
-        if (strpos($name, '-') === 0) {
-          return substr($name, 1);
-        }
-        return $name;
+    }
+    if (!$version) {
+      $editors[$editor]['error'] = t('The installed version of %editor is not supported.', array('%editor' => $properties['title']));
+      $editors[$editor]['installed'] = FALSE;
+      continue;
+    }
+    // Apply library version specific definitions and overrides.
+    $editors[$editor] = array_merge($editors[$editor], $editors[$editor]['versions'][$version]);
+    unset($editors[$editor]['versions']);
+    $editors[$editor]['title'] = $editors[$editor]['title'] . ' ' . $editors[$editor]['installed version'];
+  }
+  return $editors;
+}
+
+/**
+ * Load include files for wysiwyg implemented by all modules.
+ *
+ * @param $type
+ *   The type of includes to search for, can be 'editors'.
+ * @param $hook
+ *   The hook name to invoke.
+ * @param $file
+ *   An optional include file name without .inc extension to limit the search to.
+ *
+ * @see wysiwyg_get_directories(), _wysiwyg_process_include()
+ */
+function wysiwyg_load_includes($type = 'editors', $hook = 'editor', $file = NULL) {
+  // Determine implementations.
+  $directories = wysiwyg_get_directories($type);
+  $directories['wysiwyg_'] = wysiwyg_get_path($type);
+  $file_list = array();
+  foreach ($directories as $module => $path) {
+    $file_list[$module] = drupal_system_listing("$file" . '.inc$', $path, 'name', 0);
+  }
+
+  // Load implementations.
+  $info = array();
+  foreach (array_filter($file_list) as $module => $files) {
+    foreach ($files as $file) {
+      include_once './' . $file->filename;
+      $result = _wysiwyg_process_include('wysiwyg', $module . $file->name, dirname($file->filename), $hook);
+      if (is_array($result)) {
+        $info = array_merge($info, $result);
       }
-      break;
+    }
+  }
+  return $info;
+}
+
+/**
+ * Helper function to build module/file paths.
+ *
+ * @param $file
+ *   A file or directory in a module to return.
+ * @param $base_path
+ *   Whether to prefix the resulting path with base_path().
+ * @param $module
+ *   The module name to use as prefix.
+ *
+ * @return
+ *   The path to the specified file in a module.
+ */
+function wysiwyg_get_path($file = '', $base_path = FALSE, $module = 'wysiwyg') {
+  $base_path = ($base_path ? base_path() : '');
+  return $base_path . drupal_get_path('module', $module) . '/' . $file;
+}
+
+/**
+ * Return a list of directories by modules implementing wysiwyg_include_directory().
+ *
+ * @param $plugintype
+ *   The type of a plugin; can be 'editors'.
+ *
+ * @return
+ *   An array containing module names suffixed with '_' and their defined
+ *   directory.
+ *
+ * @see wysiwyg_load_includes(), _wysiwyg_process_include()
+ */
+function wysiwyg_get_directories($plugintype) {
+  $directories = array();
+  foreach (module_implements('wysiwyg_include_directory') as $module) {
+    $result = module_invoke($module, 'wysiwyg_include_directory', $plugintype);
+    if (isset($result) && is_string($result)) {
+      $directories[$module .'_'] = drupal_get_path('module', $module) .'/'. $result;
+    }
+  }
+  return $directories;
+}
+
+/**
+ * Process a single hook implementation of a wysiwyg editor.
+ *
+ * @param $module
+ *   The module that owns the hook.
+ * @param $identifier
+ *   Either the module or 'wysiwyg_' . $file->name
+ * @param $hook
+ *   The name of the hook being invoked.
+ */
+function _wysiwyg_process_include($module, $identifier, $path, $hook) {
+  $function = $identifier . '_' . $hook;
+  if (!function_exists($function)) {
+    return NULL;
+  }
+  $result = $function();
+  if (!isset($result) || !is_array($result)) {
+    return NULL;
   }
+
+  // Fill in defaults.
+  foreach ($result as $editor => $properties) {
+    $result[$editor]['module'] = $module;
+    $result[$editor]['name'] = $editor;
+    $result[$editor]['path'] = $path;
+  }
+  return $result;
 }
 
+/**
+ * @} End of "defgroup wysiwyg_api".
+ */
+
Index: wysiwyg.plugins.inc
===================================================================
RCS file: wysiwyg.plugins.inc
diff -N wysiwyg.plugins.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ wysiwyg.plugins.inc	14 Oct 2008 20:23:37 -0000
@@ -0,0 +1,23 @@
+<?php
+// $Id: wysiwyg.plugins.inc,v 1.7 2008/10/14 18:08:00 sun Exp $
+
+
+/**
+ * Implementation of hook_wysiwyg_plugin().
+ */
+function wysiwyg_wysiwyg_plugin($editor, $version) {
+  switch ($editor) {
+    case 'tinymce':
+      if ($version < 3) {
+        return array(
+          'wysiwyg' => array(
+            'path' => drupal_get_path('module', 'wysiwyg') .'/plugins/break/editor_plugin.js',
+            'buttons' => array('break' => t('Teaser break')),
+            'url' => 'http://drupal.org/project/wysiwyg',
+          ),
+        );
+      }
+      break;
+  }
+}
+
Index: wysiwyg_editor.admin.inc
===================================================================
RCS file: wysiwyg_editor.admin.inc
diff -N wysiwyg_editor.admin.inc
--- wysiwyg_editor.admin.inc	13 Oct 2008 19:34:29 -0000	1.19
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,498 +0,0 @@
-<?php
-// $Id: wysiwyg_editor.admin.inc,v 1.19 2008/10/13 19:34:29 sun Exp $
-
-
-/**
- * @file
- * Integrate Wysiwyg editors into Drupal.
- */
-
-/**
- * Callback handler for admin pages; menu callback.
- *
- * @todo Move into hook_menu(), resp. FAPI functions.
- */
-function wysiwyg_editor_admin($arg = '', $name = '') {
-  switch ($arg) {
-    case 'add':
-      $breadcrumb[] = l(t('Home'), NULL);
-      $breadcrumb[] = l(t('Administer'), 'admin');
-      $breadcrumb[] = l(t('Site configuration'), 'admin/settings');
-      $breadcrumb[] = l(t('Wysiwyg'), 'admin/settings/wysiwyg');
-      $breadcrumb[] = l(t('Wysiwyg Profiles'), 'admin/settings/wysiwyg/profile');
-      drupal_set_breadcrumb($breadcrumb);
-      $profile = new stdClass;
-      return drupal_get_form('wysiwyg_editor_profile_form', $profile);
-
-    case 'edit':
-      drupal_set_title(t('Edit Wysiwyg Editor profile'));
-      return drupal_get_form('wysiwyg_editor_profile_form', wysiwyg_editor_load_profile($name));
-
-    case 'delete':
-      wysiwyg_editor_profile_delete($name);
-      drupal_set_message(t('Wysiwyg profile %name has been deleted.', array('%name' => $name)));
-      drupal_goto('admin/settings/wysiwyg/profile');
-      break;
-
-    default:
-      return wysiwyg_editor_profile_overview();
-  }
-}
-
-/**
- * Return an HTML form for profile configuration.
- */
-function wysiwyg_editor_profile_form($form_state, $profile) {
-  // Merge in defaults.
-  settype($profile, 'array');
-  $profile += array(
-    'name' => NULL,
-    'rids' => array(),
-    'settings' => array(),
-  );
-  $profile['settings'] += array(
-    'editor' => NULL,
-    'default' => TRUE,
-    'user_choose' => FALSE,
-    'show_toggle' => TRUE,
-    'theme' => 'advanced',
-    'language' => 'en',
-    'access' => 1,
-    'access_pages' => "node/*\nuser/*\ncomment/*",
-    'buttons' => array(),
-    'toolbar_loc' => 'top',
-    'toolbar_align' => 'left',
-    'path_loc' => 'bottom',
-    'resizing' => TRUE,
-    // Also available, but buggy in TinyMCE 2.x: blockquote,code,dt,dd,samp.
-    'block_formats' => 'p,address,pre,h2,h3,h4,h5,h6,div',
-    'verify_html' => TRUE,
-    'preformatted' => FALSE,
-    'convert_fonts_to_spans' => TRUE,
-    'remove_linebreaks' => TRUE,
-    'apply_source_formatting' => FALSE,
-    'paste_auto_cleanup_on_paste' => FALSE,
-    'css_setting' => 'theme',
-    'css_path' => NULL,
-    'css_classes' => NULL,
-  );
-  settype($profile, 'object');
-
-  $form = array();
-
-  if (arg(4) == 'add') {
-    $submit_label = t('Create profile');
-  }
-  else {
-    $form['old_name'] = array('#type' => 'hidden', '#value' => $profile->name);
-    $submit_label = t('Update profile');
-  }
-
-  $form['basic'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Basic setup'),
-    '#collapsible' => TRUE,
-    '#collapsed' => TRUE,
-  );
-
-  $form['basic']['name'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Profile name'),
-    '#default_value' => $profile->name,
-    '#size' => 40,
-    '#maxlength' => 128,
-    '#description' => t('Enter a name for this profile. This name is only visible within the Wysiwyg Editor administration page.'),
-    '#required' => TRUE,
-  );
-
-  $form['basic']['rids'] = array(
-    '#type' => 'checkboxes',
-    '#title' => t('Roles allowed to use this profile'),
-    '#default_value' => array_keys($profile->rids),
-    '#options' => user_roles(FALSE, 'access wysiwyg editor'),
-    '#description' => t('Only roles with the %access permission will be shown here.', array('%access' => 'access wysiwyg editor')),
-    '#required' => TRUE,
-  );
-
-  $editors = wysiwyg_get_all_editors();
-  $options = array();
-  foreach ($editors as $name => $properties) {
-    if ($properties['installed']) {
-      $options[$name] = $properties['title'];
-    }
-  }
-  $form['basic']['editor'] = array(
-    '#type' => 'select',
-    '#title' => t('Editor'),
-    '#default_value' => $profile->settings['editor'],
-    '#options' => $options,
-    '#description' => t('Choose an editor to use for this profile.'),
-  );
-
-  $form['basic']['default'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Enabled by default'),
-    '#default_value' => $profile->settings['default'],
-    '#return_value' => 1,
-    '#description' => t('The default editor state for users having access to this profile. Users are able to override this state if the next option is enabled.'),
-  );
-
-  $form['basic']['user_choose'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Allow users to choose default'),
-    '#default_value' => $profile->settings['user_choose'],
-    '#return_value' => 1,
-    '#description' => t('If allowed, users will be able to choose their own Wysiwyg Editor default state in their user account settings.'),
-  );
-
-  $form['basic']['show_toggle'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Show %editor toggle link', array('%editor' => t('enable/disable rich text'))),
-    '#default_value' => $profile->settings['show_toggle'],
-    '#return_value' => 1,
-    '#description' => t('Whether or not to show the %editor toggle link below a textarea. If disabled, the user setting or global default is used (see above).', array('%editor' => t('enable/disable rich text'))),
-  );
-
-  $form['basic']['theme'] = array(
-    '#type' => 'hidden',
-    '#value' => $profile->settings['theme'],
-  );
-
-  $form['basic']['language'] = array(
-    '#type' => 'select',
-    '#title' => t('Language'),
-    '#default_value' => $profile->settings['language'],
-    '#options' => drupal_map_assoc(array('ar', 'ca', 'cs', 'cy', 'da', 'de', 'el', 'en', 'es', 'fa', 'fi', 'fr', 'fr_ca', 'he', 'hu', 'is', 'it', 'ja', 'ko', 'nb', 'nl', 'nn', 'pl', 'pt', 'pt_br', 'ru', 'ru_KOI8-R', 'ru_UTF-8', 'si', 'sk', 'sv', 'th', 'zh_cn', 'zh_tw', 'zh_tw_utf8')),
-    '#description' => t('The language for the Wysiwyg Editor interface. Language codes are based on the <a href="http://www.loc.gov/standards/iso639-2/englangn.html">ISO-639-2</a> format.'),
-  );
-
-  $form['buttons'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Buttons and plugins'),
-    '#collapsible' => TRUE,
-    '#collapsed' => TRUE,
-    '#tree' => TRUE,
-    '#theme' => 'wysiwyg_editor_admin_button_table',
-  );
-
-  $plugins = wysiwyg_editor_get_plugins($profile->settings['editor']);
-  // Generate the button list.
-  foreach ($plugins as $name => $meta) {
-    if (isset($meta['buttons']) && is_array($meta['buttons'])) {
-      foreach ($meta['buttons'] as $button => $title) {
-        $icon = '';
-        if (!empty($meta['path'])) {
-          // @todo Button icon locations are different in editors, editor versions,
-          //   and contrib/custom plugins (like Image Assist, f.e.).
-          $img_src = $meta['path'] ."/images/$name.gif";
-          // Handle plugins that have more than one button.
-          if (!file_exists($img_src)) {
-            $img_src = $meta['path'] ."/images/$button.gif";
-          }
-          $icon = file_exists($img_src) ? '<img src="'. base_path() . $img_src .'" title="'. $button .'" style="border: 1px solid grey; vertical-align: middle;" />' : '';
-        }
-        $title = (isset($meta['url']) ? l($title, $meta['url'], array('target' => '_blank')) : $title);
-        $title = (!empty($icon) ? $icon .' '. $title : $title);
-        $form['buttons'][$name][$button] = array(
-          '#type' => 'checkbox',
-          '#title' => $title,
-          '#default_value' => !empty($profile->settings['buttons'][$name][$button]) ? $profile->settings['buttons'][$name][$button] : FALSE,
-        );
-      }
-    }
-    else if (isset($meta['extensions']) && is_array($meta['extensions'])) {
-      foreach ($meta['extensions'] as $extension => $title) {
-        $form['buttons'][$name][$extension] = array(
-          '#type' => 'checkbox',
-          '#title' => isset($meta['url']) ? l($title, $meta['url'], array('target' => '_blank')) : $title,
-          '#default_value' => !empty($profile->settings['buttons'][$name][$extension]) ? $profile->settings['buttons'][$name][$extension] : FALSE,
-        );
-      }
-    }
-  }
-
-  $form['appearance'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Editor appearance'),
-    '#collapsible' => TRUE,
-    '#collapsed' => TRUE,
-  );
-
-  $form['appearance']['toolbar_loc'] = array(
-    '#type' => 'select',
-    '#title' => t('Toolbar location'),
-    '#default_value' => $profile->settings['toolbar_loc'],
-    '#options' => array('bottom' => t('Bottom'), 'top' => t('Top')),
-    '#description' => t('This option controls whether the editor toolbar is displayed above or below the editing area.'),
-  );
-
-  $form['appearance']['toolbar_align'] = array(
-    '#type' => 'select',
-    '#title' => t('Button alignment'),
-    '#default_value' => $profile->settings['toolbar_align'],
-    '#options' => array('center' => t('Center'), 'left' => t('Left'), 'right' => t('Right')),
-    '#description' => t('This option controls the alignment of icons in the editor toolbar.'),
-  );
-
-  $form['appearance']['path_loc'] = array(
-    '#type' => 'select',
-    '#title' => t('Path location'),
-    '#default_value' => $profile->settings['path_loc'],
-    '#options' => array('none' => t('Hide'), 'top' => t('Top'), 'bottom' => t('Bottom')),
-    '#description' => t('Where to display the path to HTML elements (i.e. <code>body > table > tr > td</code>).'),
-  );
-
-  $form['appearance']['resizing'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Enable resizing button'),
-    '#default_value' => $profile->settings['resizing'],
-    '#return_value' => 1,
-    '#description' => t('This option gives you the ability to enable/disable the resizing button. If enabled, the Path location toolbar must be set to "Top" or "Bottom" in order to display the resize icon.'),
-  );
-
-  $form['output'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Cleanup and output'),
-    '#collapsible' => TRUE,
-    '#collapsed' => TRUE,
-  );
-
-  $form['output']['verify_html'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Verify HTML'),
-    '#default_value' => $profile->settings['verify_html'],
-    '#return_value' => 1,
-    '#description' => t('If enabled, potentially malicious code like <code>&lt;HEAD&gt;</code> tags will be removed from HTML contents.'),
-  );
-
-  $form['output']['preformatted'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Preformatted'),
-    '#default_value' => $profile->settings['preformatted'],
-    '#return_value' => 1,
-    '#description' => t('If enabled, the editor will insert TAB characters on tab and preserve other whitespace characters just like a PRE element in HTML does.'),
-  );
-
-  $form['output']['convert_fonts_to_spans'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Convert &lt;font&gt; tags to styles'),
-    '#default_value' => $profile->settings['convert_fonts_to_spans'],
-    '#return_value' => 1,
-    '#description' => t('If enabled, HTML tags declaring the font size, font family, font color and font background color will be replaced by inline CSS styles.'),
-  );
-
-  $form['output']['remove_linebreaks'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Remove linebreaks'),
-    '#default_value' => $profile->settings['remove_linebreaks'],
-    '#return_value' => 1,
-    '#description' => t('If enabled, the editor will remove most linebreaks from contents. Disabling this option could avoid conflicts with other input filters.'),
-  );
-
-  $form['output']['apply_source_formatting'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Apply source formatting'),
-    '#default_value' => $profile->settings['apply_source_formatting'],
-    '#return_value' => 1,
-    '#description' => t('If enabled, the editor will re-format the HTML source code. Disabling this option could avoid conflicts with other input filters.'),
-  );
-
-  $form['output']['paste_auto_cleanup_on_paste'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Force cleanup on standard paste'),
-    '#default_value' => $profile->settings['paste_auto_cleanup_on_paste'],
-    '#return_value' => 1,
-    '#description' => t('If enabled, the default paste function (CTRL-V or SHIFT-INS) behaves like the "paste from word" plugin function.'),
-  );
-
-  $form['css'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('CSS'),
-    '#collapsible' => TRUE,
-    '#collapsed' => TRUE,
-  );
-
-  $form['css']['block_formats'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Block formats'),
-    '#default_value' => $profile->settings['block_formats'],
-    '#size' => 40,
-    '#maxlength' => 250,
-    '#description' => t('Comma separated list of HTML block formats. You can only remove elements, not add.'),
-  );
-
-  $form['css']['css_setting'] = array(
-    '#type' => 'select',
-    '#title' => t('Editor CSS'),
-    '#default_value' => $profile->settings['css_setting'],
-    '#options' => array('theme' => t('Use theme CSS'), 'self' => t('Define CSS'), 'none' => t('TinyMCE default CSS')),
-    '#description' => t('Defines the CSS to be used in the editor area.<br />use theme css - load style.css from current site theme.<br/>define css - enter path for css file below.<br />TinyMCE default - uses default CSS from editor.'),
-  );
-
-  $form['css']['css_path'] = array(
-    '#type' => 'textfield',
-    '#title' => t('CSS path'),
-    '#default_value' => $profile->settings['css_path'],
-    '#size' => 40,
-    '#maxlength' => 255,
-    '#description' => t('If "Define CSS" has been selected above, enter path to a CSS file or a list of CSS files seperated by a comma.') .'<br />'. t('Available tokens: %b (base path, f.e.: /), %t (path to theme, f.e.: themes/garland)') .'<br />'. t('Examples:') .' css/editor.css,/themes/garland/style.css,%b%t/style.css,http://example.com/external.css',
-  );
-
-  $form['css']['css_classes'] = array(
-    '#type' => 'textarea',
-    '#title' => t('CSS classes'),
-    '#default_value' => $profile->settings['css_classes'],
-    '#description' => t('Optionally define CSS classes for the "Font style" dropdown list.<br />Enter one class on each line in the format: !format. Example: !example<br />If left blank, CSS classes are automatically imported from all loaded stylesheet(s).', array('!format' => '<code>[title]=[class]</code>', '!example' => 'My heading=header1')),
-  );
-
-  $form['submit'] = array(
-    '#type' => 'submit',
-    '#value' => $submit_label,
-  );
-
-  return $form;
-}
-
-/**
- * Form submit callback for wysiwyg_editor_profile_form_build().
- *
- * @see wysiwyg_editor_profile_form()
- */
-function wysiwyg_editor_profile_form_submit($form, &$form_state) {
-  // Count enabled plugins for this profile.
-  $plugin_count = 0;
-  foreach ($form_state['values']['buttons'] as $plugin => $buttons) {
-    $form_state['values']['buttons'][$plugin] = array_filter($form_state['values']['buttons'][$plugin]);
-    $plugin_count += count($form_state['values']['buttons'][$plugin]);
-  }
-  // Store only enabled buttons.
-  $form_state['values']['buttons'] = array_filter($form_state['values']['buttons']);
-
-  // Filter enabled roles for this profile.
-  $form_state['values']['rids'] = array_filter($form_state['values']['rids']);
-
-  // Delete existing profile(s) with the current profile name.
-  if (!empty($form_state['values']['old_name'])) {
-    db_query("DELETE FROM {wysiwyg_editor_profile} WHERE name = '%s' OR name = '%s'", $form_state['values']['name'], $form_state['values']['old_name']);
-    db_query("DELETE FROM {wysiwyg_editor_role} WHERE name = '%s' OR name = '%s'", $form_state['values']['name'], $form_state['values']['old_name']);
-  }
-
-  // Remove FAPI values.
-  // @see system_settings_form_submit()
-  unset($form_state['values']['submit'], $form_state['values']['form_id'], $form_state['values']['op'], $form_state['values']['form_token']);
-
-  // Insert new profile data.
-  db_query("INSERT INTO {wysiwyg_editor_profile} (name, settings, plugin_count) VALUES ('%s', '%s', %d)", $form_state['values']['name'], serialize($form_state['values']), $plugin_count);
-  foreach ($form_state['values']['rids'] as $rid => $value) {
-    db_query("INSERT INTO {wysiwyg_editor_role} (name, rid) VALUES ('%s', %d)", $form_state['values']['name'], $rid);
-  }
-
-  if (isset($form_state['values']['old_name'])) {
-    drupal_set_message(t('Wysiwyg Editor profile %name has been updated.', array('%name' => $form_state['values']['name'])));
-  }
-  else {
-    drupal_set_message(t('Wysiwyg Editor profile %name has been created.', array('%name' => $form_state['values']['name'])));
-  }
-  drupal_goto('admin/settings/wysiwyg/profile');
-}
-
-/**
- * Layout for the buttons in the Wysiwyg Editor profile form.
- */
-function theme_wysiwyg_editor_admin_button_table(&$form) {
-  $buttons = array();
-
-  // Flatten forms array.
-  foreach (element_children($form) as $name) {
-    foreach (element_children($form[$name]) as $button) {
-      $buttons[] = drupal_render($form[$name][$button]);
-    }
-  }
-
-  // Split checkboxes into rows with 3 columns.
-  $total = count($buttons);
-  $rows = array();
-  for ($i = 0; $i < $total; $i++) {
-    $row    = array();
-    $row[]  = array('data' => $buttons[$i]);
-    if (isset($buttons[++$i])) {
-      $row[]  = array('data' => $buttons[$i]);
-    }
-    if (isset($buttons[++$i])) {
-      $row[]  = array('data' => $buttons[$i]);
-    }
-    $rows[] = $row;
-  }
-
-  $output = theme('table', array(), $rows, array('width' => '100%'));
-
-  return $output;
-}
-
-/**
- * Display overview of setup Wysiwyg Editor profiles; menu callback.
- */
-function wysiwyg_editor_profile_overview() {
-  $output = '';
-  $usable = TRUE;
-
-  // Check which wysiwyg editors are installed.
-  $editors = wysiwyg_get_all_editors();
-  $count = count($editors);
-  $errors = array();
-  $links = array();
-  foreach ($editors as $editor => $properties) {
-    if (!$properties['installed']) {
-      $count--;
-      $links[$editor] = t('<a href="!vendor-url">@editor</a> (<a href="!download-url">Download</a>)<br />Extract the archive and copy the folder to the following location:<br /><code>@editor-path</code>', array('!vendor-url' => $properties['vendor url'], '@editor' => $properties['title'], '!download-url' => $properties['download url'], '@editor-path' => $properties['editor path']));
-    }
-    if (isset($properties['error'])) {
-      $errors[] = $properties['error'];
-    }
-  }
-  if ($errors) {
-    foreach ($errors as $message) {
-      drupal_set_message($message, 'error');
-    }
-  }
-  elseif (!$count) {
-    drupal_set_message(t('There are no editor libraries installed currently. The following list contains a list of currently supported editors:') . theme('item_list', $links), 'error');
-    $usable = FALSE;
-  }
-
-  // Check if at least one role is granted access to Wysiwyg Editor.
-  $access_check = user_roles(FALSE, 'access wysiwyg editor');
-  if (!$access_check) {
-    drupal_set_message(t('You must <a href="!access-control-url">assign</a> at least one role with the \'access wysiwyg editor\' permission before creating a profile.', array('!access-control-url' => url('admin/user/permissions'))), 'error');
-    $usable = FALSE;
-  }
-  if (!$usable) {
-    return $output;
-  }
-
-  $profiles = wysiwyg_editor_load_profile();
-  if ($profiles) {
-    $roles = user_roles(FALSE, 'access wysiwyg editor');
-    $header = array(t('Profile'), t('Editor'), t('Roles'), t('Operations'));
-    foreach ($profiles as $p) {
-      $rows[] = array(
-        array('data' => $p->name, 'valign' => 'top'),
-        array('data' => $editors[$p->settings['editor']]['title'], 'valign' => 'top'),
-        array('data' => implode("<br />\n", $p->rids)),
-        array('data' => l(t('Edit'), 'admin/settings/wysiwyg/profile/edit/'. urlencode($p->name)) .' '. l(t('Delete'), 'admin/settings/wysiwyg/profile/delete/'. urlencode($p->name)), 'valign' => 'top'));
-    }
-    $output .= theme('table', $header, $rows);
-  }
-  $output .= '<p>'. t('<a href="!create-profile-url">Create new profile</a>', array('!create-profile-url' => url('admin/settings/wysiwyg/profile/add'))) .'</p>';
-
-  return $output;
-}
-
-/**
- * Remove a profile from the database.
- */
-function wysiwyg_editor_profile_delete($name) {
-  db_query("DELETE FROM {wysiwyg_editor_profile} WHERE name = '%s'", $name);
-  db_query("DELETE FROM {wysiwyg_editor_role} WHERE name = '%s'", $name);
-}
-
Index: wysiwyg_editor.css
===================================================================
RCS file: wysiwyg_editor.css
diff -N wysiwyg_editor.css
--- wysiwyg_editor.css	29 Jun 2008 22:24:19 -0000	1.1
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,7 +0,0 @@
-/* $Id: wysiwyg_editor.css,v 1.1 2008/06/29 22:24:19 sun Exp $ */
-
-/* TinyMCE */
-.mceToolbarTop a, .mceToolbarBottom a { float: left; }
-.mceSeparatorLine { float: left; margin-top: 3px; }
-.mceSelectList { float: left; margin-bottom: 1px; }
-#mce_editor_0_table, #mce_editor_1_table { clear: left; }
Index: wysiwyg_editor.info
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/wysiwyg_editor.info,v
retrieving revision 1.5
diff -u -p -r1.5 wysiwyg_editor.info
--- wysiwyg_editor.info	1 Aug 2008 15:11:12 -0000	1.5
+++ wysiwyg_editor.info	14 Oct 2008 20:06:18 -0000
@@ -1,5 +1,5 @@
 ; $Id: wysiwyg_editor.info,v 1.5 2008/08/01 15:11:12 sun Exp $
-name = Wysiwyg Editor
-description = Allows users to edit contents with client-side editors.
+name = Wysiwyg Editor (UNINSTALL)
+description = Please uninstall this module after running update.php.
 package = User interface
 core = 6.x
Index: wysiwyg_editor.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/wysiwyg_editor.install,v
retrieving revision 1.11
diff -u -p -r1.11 wysiwyg_editor.install
--- wysiwyg_editor.install	6 Oct 2008 23:44:52 -0000	1.11
+++ wysiwyg_editor.install	14 Oct 2008 21:04:33 -0000
@@ -1,7 +1,6 @@
 <?php
 // $Id: wysiwyg_editor.install,v 1.11 2008/10/06 23:44:52 sun Exp $
 
-
 /**
  * Implementation of hook_schema().
  */
@@ -28,15 +27,6 @@ function wysiwyg_editor_schema() {
 }
 
 /**
- * Implementation of hook_install().
- */
-function wysiwyg_editor_install() {
-  drupal_install_schema('wysiwyg_editor');
-  // Import data from old editor modules.
-  wysiwyg_editor_migrate_tinymce();
-}
-
-/**
  * Implementation of hook_uninstall()
  */
 function wysiwyg_editor_uninstall() {
@@ -44,57 +34,6 @@ function wysiwyg_editor_uninstall() {
 }
 
 /**
- * Migrate from TinyMCE.
- */
-function wysiwyg_editor_migrate_tinymce() {
-  if (db_table_exists('tinymce_settings')) {
-    $schema = db_result(db_query("SELECT schema_version FROM {system} WHERE name = 'tinymce'"));
-    if ($schema >= 1) {
-      // Import TinyMCE settings.
-      db_query('INSERT INTO {wysiwyg_editor_profile} (name, settings) SELECT name, settings FROM {tinymce_settings}');
-      // Import TinyMCE profile role assignments.
-      db_query('INSERT INTO {wysiwyg_editor_role} (name, rid) SELECT name, rid FROM {tinymce_role}');
-
-      // Disable TinyMCE module.
-      module_disable(array('tinymce'));
-
-      // Update configuration.
-      wysiwyg_editor_update_5001();
-    }
-    else {
-      drupal_set_message(t('To migrate your existing TinyMCE settings to Wysiwyg Editor, please update TinyMCE module to the latest official release, and re-install Wysiwyg Editor module.'));
-    }
-  }
-}
-
-/**
- * Convert buttons and plugins into associative array and fix plugin count for old profiles.
- *
- * Note: This update is required for wysiwyg_editor_migrate_tinymce().
- */
-function wysiwyg_editor_update_5001() {
-  $ret = array();
-  $profiles = db_query("SELECT name, settings, plugin_count FROM {wysiwyg_editor_profile}");
-  while ($profile = db_fetch_array($profiles)) {
-    $settings = unserialize($profile['settings']);
-    if (isset($settings['form_id'])) {
-      $old_buttons = (isset($settings['buttons']) ? $settings['buttons'] : array());
-      $settings['buttons'] = array();
-      $plugin_count = 0;
-      foreach ($old_buttons as $old_button => $enabled) {
-        list($plugin, $button) = explode('-', $old_button, 2);
-        $settings['buttons'][$plugin][$button] = 1;
-        $plugin_count++;
-      }
-      // We can't use update_sql() here because of curly braces in serialized
-      // array.
-      db_query("UPDATE {wysiwyg_editor_profile} SET settings = '%s', plugin_count = %d WHERE name = '%s'", serialize($settings), $plugin_count, $profile['name']);
-    }
-  }
-  return $ret;
-}
-
-/**
  * Add editor key to wysiwyg profiles.
  */
 function wysiwyg_editor_update_6002() {
@@ -112,3 +51,20 @@ function wysiwyg_editor_update_6002() {
   return $ret;
 }
 
+/**
+ * Enable Wysiwyg module.
+ */
+function wysiwyg_editor_update_6003() {
+  $ret = array();
+  if (!module_exists('wysiwyg')) {
+    // Load Wysiwyg module.
+    module_enable(array('wysiwyg'));
+    // Invoke module installation.
+    module_invoke('wysiwyg', 'install');
+    // Disable Wysiwyg Editor module.
+    module_disable(array('wysiwyg_editor'));
+    $ret[] = array('success' => TRUE, 'query' => t('Wysiwyg Editor module has been disabled and replaced with Wysiwyg module.  Your Wysiwyg profiles should have been migrated.'));
+  }
+  return $ret;
+}
+
Index: wysiwyg_editor.js
===================================================================
RCS file: wysiwyg_editor.js
diff -N wysiwyg_editor.js
--- wysiwyg_editor.js	13 Oct 2008 23:16:49 -0000	1.11
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,170 +0,0 @@
-// $Id: wysiwyg_editor.js,v 1.11 2008/10/13 23:16:49 sun Exp $
-
-/**
- * Initialize editor libraries.
- *
- * Some editors need to be initialized before the DOM is fully loaded. The
- * init hook gives them a chance to do so.
- */
-Drupal.wysiwygInit = function() {
-  jQuery.each(Drupal.wysiwyg.editor.init, function(editor) {
-    // Clone, so original settings are not overwritten.
-    this(Drupal.wysiwyg.clone(Drupal.settings.wysiwyg.configs[editor]));
-  });
-}
-
-/**
- * Attach editors to input formats and target elements (f.e. textareas).
- *
- * This behavior searches for input format selectors and formatting guidelines
- * that have been preprocessed by Wysiwyg API. All CSS classes of those elements
- * with the prefix 'wysiwyg-' are parsed into input format parameters, defining
- * the configured editor, editor theme, target element id, and variable other
- * properties, which are passed to the attach/detach hooks of the corresponding
- * editor.
- *
- * Furthermore, an "enable/disable rich-text" toggle link is added after the
- * target element to allow users to alter its contents in plain text.
- *
- * This is executed once, while editor attach/detach hooks can be invoked
- * multiple times.
- *
- * @param context
- *   A DOM element, supplied by Drupal.attachBehaviors().
- */
-Drupal.behaviors.attachWysiwyg = function(context) {
-  $('.wysiwyg:not(.wysiwyg-processed)', context).each(function() {
-    // Parse the element's CSS classes into parameters.
-    // Format is wysiwyg-name-value.
-    var classes = this.className.split(' ');
-    var params = {};
-    for (var i in classes) {
-      if (classes[i].substr(0, 8) == 'wysiwyg-') {
-        var parts = classes[i].split('-');
-        var value = parts.slice(2).join('-');
-        params[parts[1]] = value;
-      }
-    }
-    $this = $(this);
-    // Directly attach this editor, if the input format is enabled or there is
-    // only one input format at all.
-    if (($this.is(':input') && $this.is(':checked')) || $this.is('div')) {
-      Drupal.wysiwygAttachToggleLink(context, params);
-      Drupal.wysiwygAttach(context, params);
-    }
-    // Attach onChange handlers to input format selector elements.
-    // @todo To support different editors on the same page, we need to store
-    //   the last attached editor of each target element separately.
-    if ($this.is(':input')) {
-      $this.change(function() {
-        Drupal.wysiwygDetach(context, params);
-        Drupal.wysiwygAttach(context, params);
-      });
-    }
-    $this.addClass('wysiwyg-processed');
-  });
-}
-
-/**
- * Attach an editor to a target element.
- *
- * This tests whether the passed in editor implements the attach hook and
- * invokes it if available. Editor profile settings are cloned first, so they
- * cannot be overridden. After attaching the editor, the toggle link is shown
- * again, except in case we are attaching no editor.
- *
- * @param context
- *   A DOM element, supplied by Drupal.attachBehaviors().
- * @param params
- *   An object containing input format parameters.
- */
-Drupal.wysiwygAttach = function(context, params) {
-  if (typeof Drupal.wysiwyg.editor.attach[params.editor] == 'function') {
-    // Attach editor.
-    Drupal.wysiwyg.editor.attach[params.editor](context, params, Drupal.wysiwyg.clone(Drupal.settings.wysiwyg.configs[params.editor]));
-    // Display toggle link.
-    $('#wysiwyg-toggle-' + params.field).show();
-  }
-  // Hide toggle link in case no editor is attached.
-  if (params.editor == 'none') {
-    $('#wysiwyg-toggle-' + params.field).hide();
-  }
-}
-
-/**
- * Detach all editors from a target element.
- *
- * Until there is a central registry of target elements storing the currently
- * attached editor, we simply invoke the detach hook of all editors to ensure
- * that no editor is attached to the target element.
- *
- * @param context
- *   A DOM element, supplied by Drupal.attachBehaviors().
- * @param params
- *   An object containing input format parameters.
- */
-Drupal.wysiwygDetach = function(context, params) {
-  jQuery.each(Drupal.wysiwyg.editor.detach, function(editor) {
-    this(context, params);
-  });
-}
-
-/**
- * Append a editor toggle link to a target element.
- *
- * @param context
- *   A DOM element, supplied by Drupal.attachBehaviors().
- * @param params
- *   An object containing input format parameters.
- */
-Drupal.wysiwygAttachToggleLink = function(context, params) {
-  var text = document.createTextNode(Drupal.settings.wysiwyg.status ? Drupal.settings.wysiwyg.disable : Drupal.settings.wysiwyg.enable);
-  var a = document.createElement('a');
-  $(a).toggle(
-    function() {
-      Drupal.wysiwygDetach(context, params);
-      $('#wysiwyg-toggle-' + params.field).html(Drupal.settings.wysiwyg.enable).blur();
-      // After disabling the editor, re-attach default behaviors.
-      Drupal.wysiwyg.editor.attach.none(context, params);
-    },
-    function() {
-      // Before enabling the editor, detach default behaviors.
-      Drupal.wysiwyg.editor.detach.none(context, params);
-      Drupal.wysiwygAttach(context, params);
-      $('#wysiwyg-toggle-' + params.field).html(Drupal.settings.wysiwyg.disable).blur();
-    })
-    .attr('id', 'wysiwyg-toggle-' + params.field)
-    .attr('href', 'javascript:void(0);')
-    .append(text);
-  var div = document.createElement('div');
-  $(div).append(a);
-  $('#' + params.field).after(div);
-}
-
-/**
- * Clone a configuration object recursively.
- *
- * @param obj
- *   The object to clone.
- *
- * @return
- *   A copy of the passed in object.
- */
-Drupal.wysiwyg.clone = function(obj) {
-  var clone = {};
-  for (i in obj) {
-    if ((typeof obj[i] == 'object') || (typeof obj[i] == 'array')) {
-      clone[i] = Drupal.wysiwyg.clone(obj[i]);
-    }
-    else {
-      clone[i] = obj[i];
-    }
-  }
-  return clone;
-}
-
-/**
- * Allow certain editor libraries to initialize before the DOM is loaded.
- */
-Drupal.wysiwygInit();
-
Index: wysiwyg_editor.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/wysiwyg_editor.module,v
retrieving revision 1.31
diff -u -p -r1.31 wysiwyg_editor.module
--- wysiwyg_editor.module	13 Oct 2008 20:52:43 -0000	1.31
+++ wysiwyg_editor.module	14 Oct 2008 18:59:18 -0000
@@ -1,741 +1,5 @@
 <?php
 // $Id: wysiwyg_editor.module,v 1.31 2008/10/13 20:52:43 sun Exp $
 
-
-/**
- * @file
- * Integrate client-side editors with Drupal.
- *
- * Please note that this module is slowly moving to become wysiwyg.module. Most
- * functions are still based on the obsolete wysiwyg_editor name, which was
- * originally chosen, because it was not clear whether editors would just be
- * one component of a larger API.
- */
-
-/**
- * Implementation of hook_menu().
- */
-function wysiwyg_editor_menu() {
-  $items = array();
-  $items['admin/settings/wysiwyg/profile'] = array(
-    'title' => 'Wysiwyg Editor',
-    'page callback' => 'wysiwyg_editor_admin',
-    'description' => 'Configure client-side editors profiles.',
-    'access arguments' => array('administer site configuration'),
-    'file' => 'wysiwyg_editor.admin.inc',
-  );
-  return $items;
-}
-
-/**
- * Implementation of hook_theme().
- */
-function wysiwyg_editor_theme() {
-  return array(
-    'wysiwyg_editor_admin_button_table' => array('arguments' => array('form')),
-  );
-}
-
-/**
- * Implementation of hook_help().
- */
-function wysiwyg_editor_help($path, $arg) {
-  switch ($path) {
-    case 'admin/settings/wysiwyg/profile':
-    case 'admin/help#wysiwyg_editor':
-      $output = '<p>'. t('Profiles can be defined based on user roles. A Wysiwyg Editor profile can define which pages receive this Wysiwyg Editor capability, what buttons or themes are enabled for the editor, how the editor is displayed, and a few other editor functions. Lastly, only users with the %permission <a href="!url">user permission</a> are able to use Wysiwyg Editor.', array('%permission' => 'access wysiwyg editor', '!url' => url('admin/user/permissions'))) .'</p>';
-      return $output;
-  }
-}
-
-/**
- * Implementation of hook_perm().
- */
-function wysiwyg_editor_perm() {
-  return array('access wysiwyg editor');
-}
-
-/**
- * Implementation of hook_elements().
- *
- * Before Drupal 7, there is no way to easily identify form fields that are
- * input format enabled. This is a workaround: We assign a form #after_build
- * processing callback that is executed on all forms after they have been
- * completely built, so that form elements already are in their effective order
- * and position.
- *
- * @see wysiwyg_process_form()
- *
- * @todo Remove #wysiwyg_style; the GUI for an editor should be solely handled
- *   via profiles, when profiles are attached to an input format. It makes no
- *   sense to display TinyMCE's simple GUI/theme for the user signature, when
- *   the input format allows users to use advanced HTML and hence, editor
- *   plugins.  Fix this here, in wysiwyg_editor_process_element(), and lastly
- *   in wysiwyg_editor_get_config().
- */
-function wysiwyg_editor_elements() {
-  $type = array();
-  if (user_access('access wysiwyg editor')) {
-    // @todo Derive editor theme from input format.
-    $type['textarea'] = array('#wysiwyg_style' => 'advanced');
-    $type['form'] = array('#after_build' => array('wysiwyg_process_form'));
-  }
-  return $type;
-}
-
-/**
- * Implementation of hook_form_alter().
- */
-function wysiwyg_editor_form_alter(&$form, &$form_state) {
-  // Disable 'teaser' textarea.
-  if (isset($form['body_field'])) {
-    unset($form['body_field']['teaser_js']);
-    $form['body_field']['teaser_include'] = array();
-  }
-}
-
-/**
- * Process a textarea for Wysiwyg Editor.
- *
- * This way, we can recurse into the form and search for certain, hard-coded
- * elements that have been added by filter_form(). If an input format selector
- * or input format guidelines element is found, we assume that the preceding
- * element is the corresponding textarea and use it's #id for attaching
- * client-side editors.
- *
- * @see wysiwyg_editor_elements(), filter_form()
- */
-function wysiwyg_process_form(&$form) {
-  // Iterate over element children; resetting array keys to access last index.
-  if ($children = array_values(element_children($form))) {
-    foreach ($children as $index => $item) {
-      $element = &$form[$item];
-
-      // filter_form() always uses the key 'format'. We need a type-agnostic
-      // match to prevent false positives. Also, there must have been at least
-      // one element on this level.
-      if ($item === 'format' && $index > 0) {
-        // Make sure we either match a input format selector or input format
-        // guidelines (displayed if user has access to one input format only).
-        if ((isset($element['#type']) && $element['#type'] == 'fieldset') || isset($element['format']['guidelines'])) {
-          // The element before this element is the target form field.
-          $field = &$form[$children[$index - 1]];
-
-          // Disable #resizable to avoid resizable behavior to hi-jack the UI,
-          // but load the behavior, so the 'none' editor can attach/detach it.
-          $extra_class = '';
-          if (!empty($field['#resizable'])) {
-            // Due to our CSS class parsing, we can add arbitrary parameters
-            // for each input format.
-            $extra_class = ' wysiwyg-resizable-1';
-            $field['#resizable'] = FALSE;
-            drupal_add_js('misc/textarea.js');
-          }
-
-          // Determine the available input formats. The last child element is a
-          // link to "More information about formatting options". When only one
-          // input format is displayed, we also have to remove formatting
-          // guidelines, stored in the child 'format'.
-          $formats = element_children($element);
-          array_pop($formats);
-          unset($formats['format']);
-          foreach ($formats as $format) {
-            // Default to 'none' editor (Drupal's default behaviors).
-            $editor = 'none';
-            $theme = '';
-            // Fetch the profile associated to this input format.
-            $profile = wysiwyg_get_profile($format);
-            if ($profile) {
-              $editor = $profile->settings['editor'];
-              // Check editor theme (and reset it if not/no longer available).
-              $theme = wysiwyg_editor_get_themes($profile, $field['#wysiwyg_style']);
-
-              // Add profile settings for this input format.
-              wysiwyg_editor_add_settings($profile, $theme);
-              // Add plugin settings for this input format.
-              wysiwyg_editor_add_plugin_settings($profile);
-
-              $theme = ' wysiwyg-theme-'. $theme;
-            }
-
-            // Use a prefix/suffix for a single input format, or attach to input
-            // format selector radio buttons.
-            if (isset($element['format']['guidelines'])) {
-              $element[$format]['#prefix'] = '<div class="wysiwyg wysiwyg-editor-'. $editor .' wysiwyg-field-'. $field['#id'] . $theme . $extra_class .'">';
-              $element[$format]['#suffix'] = '</div>';
-            }
-            else {
-              if (isset($element[$format]['#attributes']['class'])) {
-                $element[$format]['#attributes']['class'] .= ' ';
-              }
-              else {
-                $element[$format]['#attributes']['class'] = '';
-              }
-              $element[$format]['#attributes']['class'] .= 'wysiwyg wysiwyg-editor-'. $editor .' wysiwyg-field-'. $field['#id'] . $theme . $extra_class;
-            }
-          }
-        }
-        // If this element is 'format', do not recurse further.
-        continue;
-      }
-      // Recurse into children.
-      wysiwyg_process_form($element);
-    }
-  }
-  return $form;
-}
-
-/**
- * Determine the profile to use for a given input format id.
- *
- * This function also performs sanity checks for the configured editor in a
- * profile to ensure that we do not load a malformed editor.
- *
- * @param $format
- *   The internal id of an input format.
- *
- * @return
- *   A wysiwyg profile.
- *
- * @see wysiwyg_editor_load_editor(), wysiwyg_get_editor()
- *
- * @todo Replace wysiwyg_editor_current_profile() with a input format to profile
- *   association.
- */
-function wysiwyg_get_profile($format) {
-  // Determine whether this input format has PHP filter enabled.
-  // Temporary, until input format to profile associations are in place.
-  $filters = filter_list_format($format);
-  foreach ($filters as $filter) {
-    if ($filter->module == 'php') {
-      return FALSE;
-    }
-  }
-  if ($profile = wysiwyg_editor_load_profile(wysiwyg_editor_current_profile())) {
-    if (wysiwyg_editor_load_editor($profile)) {
-      return $profile;
-    }
-  }
-  return FALSE;
-}
-
-/**
- * Load an editor library and initialize basic Wysiwyg settings.
- *
- * @param $profile
- *   A wysiwyg editor profile.
- *
- * @return
- *   TRUE if the editor has been loaded, FALSE if not.
- *
- * @see wysiwyg_get_profile()
- */
-function wysiwyg_editor_load_editor($profile) {
-  static $settings_added;
-  static $loaded = array();
-
-  $name = $profile->settings['editor'];
-  // Library files must be loaded only once.
-  if (!isset($loaded[$name])) {
-    // Load editor.
-    $editor = wysiwyg_get_editor($name);
-    if ($editor) {
-      // Determine library files to load.
-      // @todo Allow to configure the library/execMode to use.
-      if (isset($profile->settings['library']) && isset($editor['libraries'][$profile->settings['library']])) {
-        $library = $profile->settings['library'];
-        $files = $editor['libraries'][$profile->settings['library']]['files'];
-      }
-      else {
-        // Fallback to the first by default (external libraries can change).
-        $library = key($editor['libraries']);
-        $files = array_shift($editor['libraries']);
-        $files = $files['files'];
-      }
-      foreach ($files as $file) {
-        drupal_add_js($editor['library path'] . '/' . $file);
-      }
-      // Load JavaScript integration files for this editor.
-      if (isset($editor['js files'])) {
-        $files = $editor['js files'];
-      }
-      foreach ($files as $file) {
-        drupal_add_js($editor['js path'] . '/' . $file, 'module', 'footer');
-      }
-
-      $status = wysiwyg_editor_user_get_status($profile);
-      drupal_add_js(array('wysiwyg' => array(
-        'configs' => array($editor['name'] => array()),
-        'showToggle' => $profile->settings['show_toggle'],
-        'status' => $status,
-        // If JS compression is enabled, at least TinyMCE is unable to determine
-        // its own base path and exec mode since it can't find the script name.
-        'editorBasePath' => base_path() . $editor['library path'],
-        'execMode' => $library,
-      )), 'setting');
-
-      $loaded[$name] = TRUE;
-    }
-    else {
-      $loaded[$name] = FALSE;
-    }
-  }
-
-  // Add basic Wysiwyg settings if any editor has been added.
-  if (!isset($settings_added) && $loaded[$name]) {
-    drupal_add_js(array('wysiwyg' => array(
-      'configs' => array(),
-      'disable' => t('Disable rich-text'),
-      'enable' => t('Enable rich-text'),
-    )), 'setting');
-
-    // Initialize our namespaces in the *header* to do not force editor
-    // integration scripts to check and define Drupal.wysiwyg on its own.
-    drupal_add_js(wysiwyg_get_path('wysiwyg.init.js'));
-
-    // The 'none' editor is a special editor implementation, allowing us to
-    // attach and detach regular Drupal behaviors just like any other editor.
-    drupal_add_js(wysiwyg_get_path('editors/js/none.js'), 'module', 'footer');
-
-    // Add wysiwyg_editor.js to the footer to ensure it's executed after the
-    // Drupal.settings array has been rendered and populated. Also, since editor
-    // library initialization functions must be loaded first by the browser,
-    // Drupal.wysiwygInit() must be executed AFTER editors registered
-    // their callbacks, and BEFORE Drupal.behaviors are applied, this must come
-    // last.
-    drupal_add_js(wysiwyg_get_path('wysiwyg_editor.js'), 'module', 'footer');
-
-    // Add our stylesheet to stack editor buttons into one row.
-    // @todo This is TinyMCE 2.x specific at the moment.
-    drupal_add_css(wysiwyg_get_path('wysiwyg_editor.css'));
-
-    $settings_added = TRUE;
-  }
-
-  return $loaded[$name];
-}
-
-/**
- * Register a theme.
- */
-function wysiwyg_editor_add_settings($profile, $theme) {
-  static $themes = array();
-
-  if (!isset($themes[$theme])) {
-    $config = wysiwyg_editor_get_config($profile, $theme);
-    // Convert the config values into the form expected by Wysiwyg Editor.
-    // @todo Is this conversion TinyMCE specific?
-    foreach ($config as $key => $value) {
-      if (is_bool($value)) {
-        continue;
-      }
-      if (is_array($value)) {
-        $config[$key] = implode(',', $config[$key]);
-      }
-    }
-    drupal_add_js(array('wysiwyg' => array('configs' => array($profile->settings['editor'] => array($theme => $config)))), 'setting');
-    $themes[$theme] = TRUE;
-  }
-}
-
-/**
- * Add settings for external plugins.
- * 
- * @param $profile
- *   A wysiwyg editor profile.
- */
-function wysiwyg_editor_add_plugin_settings($profile) {
-  static $plugins_added = array();
-  
-  if (!isset($plugins_added[$profile->settings['editor']])) {
-    $plugins = array();
-    $editor = wysiwyg_get_editor($profile->settings['editor']);
-    // Collect editor plugins provided via hook_wysiwyg_plugin().
-    $info = module_invoke_all('wysiwyg_plugin', $editor['name'], $editor['installed version']);
-    // Only keep enabled plugins in this profile.
-    foreach ($info as $plugin => $meta) {
-      if (!isset($profile->settings['buttons'][$plugin])) {
-        unset($info[$plugin]);
-      }
-    }
-
-    if (isset($editor['plugin settings callback']) && function_exists($editor['plugin settings callback'])) {
-      $plugins = $editor['plugin settings callback']($editor, $profile, $info);
-    }
-
-    drupal_add_js(array('wysiwyg' => array('plugins' => array($profile->settings['editor'] => $plugins))), 'setting');
-
-    $plugins_added[$profile->settings['editor']] = TRUE;
-  }
-}
-
-/**
- * Grab the themes available to Wysiwyg Editor.
- *
- * Wysiwyg Editor themes control the functionality and buttons that are available to a
- * user. Themes are only looked for within the default Wysiwyg Editor theme directory.
- *
- * @param $profile
- *   A wysiwyg editor profile; passed/altered by reference.
- * @param $selected_theme
- *   An optional theme name that ought to be used.
- *
- * @return
- *   An array of theme names, or a single, checked theme name if $selected_theme
- *   was given.
- */
-function wysiwyg_editor_get_themes(&$profile, $selected_theme = NULL) {
-  static $themes = array();
-
-  if (!isset($themes[$profile->settings['editor']])) {
-    $editor = wysiwyg_get_editor($profile->settings['editor']);
-    if (isset($editor['themes callback']) && function_exists($editor['themes callback'])) {
-      $themes[$editor['name']] = $editor['themes callback']($editor, $profile);
-    }
-    // Fallback to 'default' otherwise.
-    else {
-      $themes[$editor['name']] = array('default');
-    }
-  }
-
-  // Check optional $selected_theme argument, if given.
-  if (isset($selected_theme)) {
-    // If the passed theme name does not exist, use the first available.
-    if (!isset($themes[$profile->settings['editor']][$selected_theme])) {
-      $selected_theme = $profile->settings['theme'] = $themes[$profile->settings['editor']][0];
-    }
-  }
-
-  return isset($selected_theme) ? $selected_theme : $themes[$profile->settings['editor']];
-}
-
-/**
- * Return plugin metadata from the plugin registry.
- *
- * @param $editor_name
- *   The internal name of an editor to return plugins for.
- *
- * @return
- *   An array for each plugin.
- */
-function wysiwyg_editor_get_plugins($editor_name) {
-  $plugins = array();
-  if (!empty($editor_name)) {
-    $editor = wysiwyg_get_editor($editor_name);
-    // Add internal editor plugins.
-    if (isset($editor['plugin callback']) && function_exists($editor['plugin callback'])) {
-      $plugins = $editor['plugin callback']($editor);
-    }
-    // Load our own plugins.
-    include_once drupal_get_path('module', 'wysiwyg_editor') .'/wysiwyg_editor.plugins.inc';
-  
-    // Add editor plugins provided via hook_wysiwyg_plugin().
-    $plugins = array_merge($plugins, module_invoke_all('wysiwyg_plugin', $editor['name'], $editor['installed version']));
-  }
-  return $plugins;
-}
-
-/**
- * Return an array of initial Wysiwyg Editor config options from the current role.
- */
-function wysiwyg_editor_get_config($profile, $theme) {
-  $editor = wysiwyg_get_editor($profile->settings['editor']);
-  $settings = array();
-  if (!empty($editor['settings callback']) && function_exists($editor['settings callback'])) {
-    $settings = $editor['settings callback']($editor, $profile->settings, $theme);
-  }
-  return $settings;
-}
-
-/**
- * Return the name of the current user's default profile.
- */
-function wysiwyg_editor_current_profile() {
-  global $user;
-  static $profile_name;
-
-  if (!isset($profile_name)) {
-    $profile_name = db_result(db_query_range('SELECT p.name FROM {wysiwyg_editor_profile} p INNER JOIN {wysiwyg_editor_role} r ON r.name = p.name WHERE r.rid IN (%s) ORDER BY plugin_count DESC', implode(',', array_keys($user->roles)), 0, 1));
-  }
-  return $profile_name;
-}
-
-/**
- * Load all profiles. Just load one profile if $name is passed in.
- */
-function wysiwyg_editor_load_profile($name = '') {
-  static $profiles;
-
-  // If $name is passed from wysiwyg_editor_current_profile(), it may be FALSE,
-  // which means the user is not allowed to use a wysiwyg editor.
-  if ($name === FALSE) {
-    return FALSE;
-  }
-  if (!isset($profiles)) {
-    $profiles = array();
-    $roles = user_roles();
-    $result = db_query('SELECT * FROM {wysiwyg_editor_profile}');
-    while ($profile = db_fetch_object($result)) {
-      $profile->settings = unserialize($profile->settings);
-      $result2 = db_query("SELECT rid FROM {wysiwyg_editor_role} WHERE name = '%s'", $profile->name);
-      $profile_roles = array();
-      while ($role = db_fetch_object($result2)) {
-        $profile_roles[$role->rid] = $roles[$role->rid];
-      }
-      $profile->rids = $profile_roles;
-
-      $profiles[$profile->name] = $profile;
-    }
-  }
-
-  return ($name && isset($profiles[$name]) ? $profiles[$name] : ($name ? FALSE : $profiles));
-}
-
-/**
- * Implementation of hook_user().
- */
-function wysiwyg_editor_user($type, &$edit, &$user, $category = NULL) {
-  if ($type == 'form' && $category == 'account' && user_access('access wysiwyg editor')) {
-    $profile = wysiwyg_editor_user_get_profile($user);
-    if (isset($profile->settings['user_choose']) && $profile->settings['user_choose']) {
-      $form['wysiwyg'] = array(
-        '#type' => 'fieldset',
-        '#title' => t('Wysiwyg Editor settings'),
-        '#weight' => 10,
-        '#collapsible' => TRUE,
-        '#collapsed' => TRUE,
-      );
-      $form['wysiwyg']['wysiwyg_editor_status'] = array(
-        '#type' => 'checkbox',
-        '#title' => t('Enable editor by default'),
-        '#default_value' => isset($user->wysiwyg_editor_status) ? $user->wysiwyg_editor_status : (isset($profile->settings['default']) ? $profile->settings['default'] : FALSE),
-        '#return_value' => 1,
-        '#description' => t('If enabled, rich-text editing is enabled by default in textarea fields.'),
-      );
-      return array('wysiwyg_editor' => $form);
-    }
-  }
-  if ($type == 'validate') {
-    return array('wysiwyg_editor_status' => $edit['wysiwyg_editor_status']);
-  }
-}
-
-function wysiwyg_editor_user_get_profile($account) {
-  $profile_name = db_result(db_query('SELECT s.name FROM {wysiwyg_editor_profile} s INNER JOIN {wysiwyg_editor_role} r ON r.name = s.name WHERE r.rid IN (%s)', implode(',', array_keys($account->roles))));
-  if ($profile_name) {
-    return wysiwyg_editor_load_profile($profile_name);
-  }
-  else {
-    return FALSE;
-  }
-}
-
-function wysiwyg_editor_user_get_status($profile) {
-  global $user;
-  $settings = $profile->settings;
-
-  if ($settings['user_choose'] && isset($user->wysiwyg_editor_status)) {
-    $status = $user->wysiwyg_editor_status;
-  }
-  else {
-    $status = isset($settings['default']) ? $settings['default'] : FALSE;
-  }
-
-  return $status;
-}
-
-/**
- * @defgroup wysiwyg_api Wysiwyg API
- * @{
- *
- * @todo Forked from Panels; abstract into a separate API module that allows
- *   contrib modules to define supported include/plugin types.
- */
-
-/**
- * Return library information for a given editor.
- *
- * @param $name
- *   The internal name of an editor.
- *
- * @return
- *   The library information for the editor, or FALSE if $name is unknown or not
- *   installed properly.
- */
-function wysiwyg_get_editor($name) {
-  $editors = wysiwyg_get_all_editors();
-  return isset($editors[$name]) && $editors[$name]['installed'] ? $editors[$name] : FALSE;
-}
-
-/**
- * Compile a list holding all supported editors including installed editor version information.
- */
-function wysiwyg_get_all_editors() {
-  static $editors;
-
-  if (isset($editors)) {
-    return $editors;
-  }
-
-  $editors = wysiwyg_load_includes('editors', 'editor');
-  foreach ($editors as $editor => $properties) {
-    // Fill in required properties.
-    $editors[$editor] += array(
-      'title' => '',
-      'vendor url' => '',
-      'download url' => '',
-      'editor path' => wysiwyg_get_path($properties['name']),
-      'library path' => wysiwyg_get_path($properties['name']),
-      'libraries' => array(),
-      'version callback' => NULL,
-      'themes callback' => NULL,
-      'settings callback' => NULL,
-      'plugin callback' => NULL,
-      'plugin settings callback' => NULL,
-      'versions' => array(),
-      'js path' => $properties['path'] .'/js',
-      'css path' => $properties['path'] .'/css',
-    );
-    // Check whether library is present.
-    if (!($editors[$editor]['installed'] = file_exists($properties['library path']))) {
-      continue;
-    }
-    // Detect library version.
-    if (function_exists($editors[$editor]['version callback'])) {
-      $editors[$editor]['installed version'] = $editors[$editor]['version callback']($properties);
-    }
-    if (empty($editors[$editor]['installed version'])) {
-      $editors[$editor]['error'] = t('The version of %editor could not be detected.', array('%editor' => $properties['title']));
-      $editors[$editor]['installed'] = FALSE;
-      continue;
-    }
-    // Determine to which supported version the installed version maps.
-    ksort($editors[$editor]['versions']);
-    $version = 0;
-    foreach ($editors[$editor]['versions'] as $supported_version => $version_properties) {
-      if (version_compare($editors[$editor]['installed version'], $supported_version, '>=')) {
-        $version = $supported_version;
-      }
-    }
-    if (!$version) {
-      $editors[$editor]['error'] = t('The installed version of %editor is not supported.', array('%editor' => $properties['title']));
-      $editors[$editor]['installed'] = FALSE;
-      continue;
-    }
-    // Apply library version specific definitions and overrides.
-    $editors[$editor] = array_merge($editors[$editor], $editors[$editor]['versions'][$version]);
-    unset($editors[$editor]['versions']);
-    $editors[$editor]['title'] = $editors[$editor]['title'] . ' ' . $editors[$editor]['installed version'];
-  }
-  return $editors;
-}
-
-/**
- * Load include files for wysiwyg implemented by all modules.
- *
- * @param $type
- *   The type of includes to search for, can be 'editors'.
- * @param $hook
- *   The hook name to invoke.
- * @param $file
- *   An optional include file name without .inc extension to limit the search to.
- *
- * @see wysiwyg_get_directories(), _wysiwyg_process_include()
- */
-function wysiwyg_load_includes($type = 'editors', $hook = 'editor', $file = NULL) {
-  // Determine implementations.
-  $directories = wysiwyg_get_directories($type);
-  $directories['wysiwyg_'] = wysiwyg_get_path($type);
-  $file_list = array();
-  foreach ($directories as $module => $path) {
-    $file_list[$module] = drupal_system_listing("$file" . '.inc$', $path, 'name', 0);
-  }
-
-  // Load implementations.
-  $info = array();
-  foreach (array_filter($file_list) as $module => $files) {
-    foreach ($files as $file) {
-      include_once './' . $file->filename;
-      $result = _wysiwyg_process_include('wysiwyg', $module . $file->name, dirname($file->filename), $hook);
-      if (is_array($result)) {
-        $info = array_merge($info, $result);
-      }
-    }
-  }
-  return $info;
-}
-
-/**
- * Helper function to build module/file paths.
- *
- * @param $file
- *   A file or directory in a module to return.
- * @param $base_path
- *   Whether to prefix the resulting path with base_path().
- * @param $module
- *   The module name to use as prefix.
- *
- * @return
- *   The path to the specified file in a module.
- */
-function wysiwyg_get_path($file = '', $base_path = FALSE, $module = 'wysiwyg_editor') {
-  $base_path = ($base_path ? base_path() : '');
-  return $base_path . drupal_get_path('module', $module) . '/' . $file;
-}
-
-/**
- * Return a list of directories by modules implementing wysiwyg_include_directory().
- *
- * @param $plugintype
- *   The type of a plugin; can be 'editors'.
- *
- * @return
- *   An array containing module names suffixed with '_' and their defined
- *   directory.
- *
- * @see wysiwyg_load_includes(), _wysiwyg_process_include()
- */
-function wysiwyg_get_directories($plugintype) {
-  $directories = array();
-  foreach (module_implements('wysiwyg_include_directory') as $module) {
-    $result = module_invoke($module, 'wysiwyg_include_directory', $plugintype);
-    if (isset($result) && is_string($result)) {
-      $directories[$module .'_'] = drupal_get_path('module', $module) .'/'. $result;
-    }
-  }
-  return $directories;
-}
-
-/**
- * Process a single hook implementation of a wysiwyg editor.
- *
- * @param $module
- *   The module that owns the hook.
- * @param $identifier
- *   Either the module or 'wysiwyg_' . $file->name
- * @param $hook
- *   The name of the hook being invoked.
- */
-function _wysiwyg_process_include($module, $identifier, $path, $hook) {
-  $function = $identifier . '_' . $hook;
-  if (!function_exists($function)) {
-    return NULL;
-  }
-  $result = $function();
-  if (!isset($result) || !is_array($result)) {
-    return NULL;
-  }
-
-  // Fill in defaults.
-  foreach ($result as $editor => $properties) {
-    $result[$editor]['module'] = $module;
-    $result[$editor]['name'] = $editor;
-    $result[$editor]['path'] = $path;
-  }
-  return $result;
-}
-
-/**
- * @} End of "defgroup wysiwyg_api".
- */
-
+// Dummy file to protect users who did not remove the complete module folder.
+// Remove after publishing 0.4 release.
Index: wysiwyg_editor.plugins.inc
===================================================================
RCS file: wysiwyg_editor.plugins.inc
diff -N wysiwyg_editor.plugins.inc
--- wysiwyg_editor.plugins.inc	14 Oct 2008 18:08:00 -0000	1.7
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,23 +0,0 @@
-<?php
-// $Id: wysiwyg_editor.plugins.inc,v 1.7 2008/10/14 18:08:00 sun Exp $
-
-
-/**
- * Implementation of hook_wysiwyg_plugin().
- */
-function wysiwyg_editor_wysiwyg_plugin($editor, $version) {
-  switch ($editor) {
-    case 'tinymce':
-      if ($version < 3) {
-        return array(
-          'wysiwyg' => array(
-            'path' => drupal_get_path('module', 'wysiwyg_editor') .'/plugins/break/editor_plugin.js',
-            'buttons' => array('break' => t('Teaser break')),
-            'url' => 'http://drupal.org/project/wysiwyg',
-          ),
-        );
-      }
-      break;
-  }
-}
-
Index: editors/tinymce.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/editors/tinymce.inc,v
retrieving revision 1.7
diff -u -p -r1.7 tinymce.inc
--- editors/tinymce.inc	13 Oct 2008 23:16:50 -0000	1.7
+++ editors/tinymce.inc	14 Oct 2008 20:26:57 -0000
@@ -148,12 +148,12 @@ function wysiwyg_tinymce_settings($edito
     // later.
     $init['extensions'] = array();
     // $init['extended_valid_elements'] are just stacked, unique'd later, and
-    // transformed into a comma-separated string in wysiwyg_editor_add_settings().
+    // transformed into a comma-separated string in wysiwyg_add_editor_settings().
     // @todo Needs a complete plugin API redesign using arrays for
     //   tag => attributes definitions and array_merge_recursive().
     $init['extended_valid_elements'] = array();
 
-    $plugins = wysiwyg_editor_get_plugins($editor['name']);
+    $plugins = wysiwyg_get_plugins($editor['name']);
     foreach ($config['buttons'] as $plugin => $buttons) {
       foreach ($buttons as $button => $enabled) {
         // Iterate separately over buttons and extensions properties.
Index: plugins/break.inc
===================================================================
RCS file: plugins/break.inc
diff -N plugins/break.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ plugins/break.inc	14 Oct 2008 20:24:17 -0000
@@ -0,0 +1,21 @@
+<?php
+// $Id: break.inc,v 1.1.2.7 2008/10/05 04:05:04 sun Exp $
+
+
+/**
+ * Implementation of hook_wysiwyg_plugin().
+ */
+function wysiwyg_break_plugin() {
+  $plugins = array();
+  $plugins['break'] = array(
+    'title' => t('Teaser break'),
+    'vendor url' => 'http://drupal.org/project/wysiwyg',
+    'icon file' => 'break.gif',
+    'icon title' => t('Separate the teaser and body of this content'),
+    'settings' => array(
+      'path' => wysiwyg_get_path('plugins/break', TRUE),
+    ),
+  );
+  return $plugins;
+}
+
Index: plugins/break/break.js
===================================================================
RCS file: plugins/break/break.js
diff -N plugins/break/break.js
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ plugins/break/break.js	13 Oct 2008 20:32:48 -0000
@@ -0,0 +1,48 @@
+// $Id: editor_plugin.js 201 2008-02-12 15:56:56Z sun $
+
+Drupal.wysiwyg.plugins.break = {
+
+  // Return whether the passed node belongs to this plugin.
+  isNode: function(node) {
+    return ($(node).is('img.wysiwyg-break'));
+  },
+
+  // Execute the button.
+  invoke: function(data, settings, instanceId) {
+    if (data.format == 'html') {
+      // Prevent duplicating a teaser break.
+      if ($(data.node).is('img.wysiwyg-break')) {
+        return;
+      }
+      var content = this._getPlaceholder(settings);
+    }
+    else {
+    	// Prevent duplicating a teaser break.
+      // @todo data.content is the selection only; needs access to complete content.
+    	if (data.content.match(/<!--break-->/)) {
+        return;
+    	}
+    	var content = '<!--break-->';
+    }
+    if (typeof content != 'undefined') {
+      Drupal.wysiwyg.invoke.insert(instanceId, content);
+    }
+  },
+
+  // Replace all <!--break--> tags with images.
+  attach: function(content, settings, instanceId) {
+    content = content.replace(/<!--break-->/g, this._getPlaceholder(settings));
+    return content;
+  },
+
+  // Replace images with <!--break--> tags in editor contents upon data.save.
+  detach: function(content, settings, instanceId) {
+    $content = $('<div>' + content + '</div>'); // No .outerHTML() in jQuery :(
+    $('img.wysiwyg-break', $content).replaceWith('<!--break-->');
+    return $content.html();
+  },
+
+  _getPlaceholder: function (settings) {
+    return '<img src="' + settings.path + '/images/spacer.gif" alt="&lt;--break-&gt;" title="&lt;--break--&gt;" class="wysiwyg-break" />';
+  }
+};
