Index: wysiwyg_editor.admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/wysiwyg_editor.admin.inc,v
retrieving revision 1.1.2.7
diff -u -p -r1.1.2.7 wysiwyg_editor.admin.inc
--- wysiwyg_editor.admin.inc	9 Jun 2008 18:42:27 -0000	1.1.2.7
+++ wysiwyg_editor.admin.inc	9 Jun 2008 18:43:00 -0000
@@ -220,7 +220,13 @@ function wysiwyg_editor_profile_form($ed
     if (isset($meta['buttons']) && is_array($meta['buttons'])) {
       foreach ($meta['buttons'] as $button) {
         if ($name != 'default') {
-          $img_src = drupal_get_path('module', 'wysiwyg_editor') ."/tinymce/jscripts/tiny_mce/plugins/$name/images/$name.gif";
+          if (!isset($meta['path'])) {
+            $img_src = drupal_get_path('module', 'wysiwyg_editor') ."/tinymce/jscripts/tiny_mce/plugins/$name/images/$name.gif";
+          }
+          // Use plugin path provided in hook_wysiwyg_plugin().
+          else {
+            $img_src = $meta['path'] ."/images/$name.gif";
+          }
 
           // Handle plugins that have more than one button.
           if (!file_exists($img_src)) {
Index: wysiwyg_editor.js
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/wysiwyg_editor.js,v
retrieving revision 1.1.2.3
diff -u -p -r1.1.2.3 wysiwyg_editor.js
--- wysiwyg_editor.js	30 May 2008 02:19:35 -0000	1.1.2.3
+++ wysiwyg_editor.js	9 Jun 2008 18:43:05 -0000
@@ -13,6 +13,9 @@ Drupal.wysiwygEditorInit = function () {
     var config = Drupal.wysiwygEditorCloneObject(Drupal.settings.wysiwygEditor.configs[theme]);
     tinyMCE.init(config);
   }
+  for (var plugin in Drupal.settings.wysiwygEditor.plugins) {
+    tinyMCE.loadPlugin(plugin, Drupal.settings.wysiwygEditor.plugins[plugin]);
+  }
 }
 
 /**
Index: wysiwyg_editor.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/wysiwyg_editor.module,v
retrieving revision 1.1.2.9
diff -u -p -r1.1.2.9 wysiwyg_editor.module
--- wysiwyg_editor.module	9 Jun 2008 18:42:27 -0000	1.1.2.9
+++ wysiwyg_editor.module	9 Jun 2008 18:43:28 -0000
@@ -160,6 +160,7 @@ function wysiwyg_editor_process_textarea
 
       wysiwyg_editor_load();
       wysiwyg_editor_load_config($element['#wysiwyg_style']);
+      wysiwyg_editor_load_plugins();
       if (!isset($element['#attributes'])) {
         $element['#attributes'] = array();
       }
@@ -208,6 +209,25 @@ function wysiwyg_editor_load_config($the
 }
 
 /**
+ * Load external plugins.
+ * 
+ * Note: tinyMCE.loadPlugin() need not be invoked more than once.
+ */
+function wysiwyg_editor_load_plugins() {
+  static $plugins;
+  
+  if (!isset($plugins)) {
+    $plugins = array();
+    $module_plugins = module_invoke_all('wysiwyg_plugin', 'tinymce');
+    foreach ($module_plugins as $name => $plugin) {
+      $name = _wysiwyg_editor_plugin_name('remove', $name);
+      $plugins[$name] = base_path() . $plugin['path'];
+    }
+    drupal_add_js('Drupal.settings.wysiwygEditor.plugins = '. drupal_to_js($plugins) .';', 'inline');
+  }
+}
+
+/**
  * Implementation of hook_user().
  */
 function wysiwyg_editor_user($type, &$edit, &$user, $category = NULL) {
@@ -316,11 +336,22 @@ function _wysiwyg_editor_get_buttons($sk
   include_once drupal_get_path('module', 'wysiwyg_editor') .'/wysiwyg_editor.plugins.inc';
   $plugins = _wysiwyg_editor_plugins();
 
+  // Allow other modules to add external plugins.
+  $plugins = array_merge($plugins, module_invoke_all('wysiwyg_plugin', 'tinymce'));
+
   if ($skip_metadata == FALSE && is_array($plugins)) {
     foreach ($plugins as $name => $plugin) {
-      $file = drupal_get_path('module', 'wysiwyg_editor') .'/tinymce/jscripts/tiny_mce/plugins/'. $name .'/editor_plugin_src.js';
+      if (!isset($plugin['path'])) {
+        $file = drupal_get_path('module', 'wysiwyg_editor') .'/tinymce/jscripts/tiny_mce/plugins/'. $name .'/editor_plugin_src.js';
+      }
+      else {
+        $file = $plugin['path'] .'/editor_plugin_src.js';
+      }
       // Grab the plugin metadata by scanning the *.js file.
       if (file_exists($file)) {
+        if (isset($plugin['path'])) {
+          $metadata[$name]['path'] = $plugin['path'];
+        }
         $lines        = file($file);
         $has_longname = FALSE;
         $has_infourl  = FALSE;
@@ -357,6 +388,32 @@ function _wysiwyg_editor_get_buttons($sk
 }
 
 /**
+ * Add or remove leading hiven to/of (external) plugin names.
+ *
+ * TinyMCE requires that external plugins that should not be loaded from
+ * its own plugin repository include a leading hiven in the name.
+ * 
+ * @param string $op
+ *   Operation to perform, 'add' or 'remove'.
+ * @param string $name
+ *   A plugin name.
+ */
+function _wysiwyg_editor_plugin_name($op, $name) {
+  if ($op == 'add') {
+    if (strpos($name, '-') !== 0) {
+      return '-'. $name;
+    }
+    return $name;
+  }
+  else if ($op == 'remove') {
+    if (strpos($name, '-') === 0) {
+      return substr($name, 1);
+    }
+    return $name;
+  }
+}
+
+/**
  * Return an array of initial Wysiwyg Editor config options from the current role.
  */
 function wysiwyg_editor_config($profile, $theme) {
@@ -462,7 +519,12 @@ function wysiwyg_editor_config($profile,
         if (is_array($plugin_tracker)) {
           foreach ($plugin_tracker as $pname) {
             if ($pname != 'default') {
-              $init['plugins'][] = $pname;
+              if (!isset($plugins[$pname]['path'])) {
+                $init['plugins'][] = $pname;
+              }
+              else {
+                $init['plugins'][] = _wysiwyg_editor_plugin_name('add', $pname);
+              }
             }
             foreach ($plugins[$pname] as $key => $value) {
               // Don't overwrite buttons or extended_valid_elements
@@ -470,7 +532,7 @@ function wysiwyg_editor_config($profile,
                 // $value is an array for extended_valid_elements so just grab the first element in the array (never more than one)
                 $init[$key][] = $value[0];
               }
-              else if (!strstr($key, 'theme_advanced_buttons')) {
+              else if ($key != 'path' && !strstr($key, 'theme_advanced_buttons')) {
                 $init[$key] = $value;
               }
             }
