Index: wysiwyg-dialog-page.tpl.php
===================================================================
RCS file: wysiwyg-dialog-page.tpl.php
diff -N wysiwyg-dialog-page.tpl.php
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ wysiwyg-dialog-page.tpl.php	26 Jan 2009 19:51:06 -0000
@@ -0,0 +1,86 @@
+<?php
+// $Id: page.tpl.php,v 1.11 2008/01/24 09:42:51 goba Exp $
+
+/**
+ * @file page.tpl.php
+ *
+ * Theme implementation to display a single Drupal page.
+ *
+ * Available variables:
+ *
+ * General utility variables:
+ * - $base_path: The base URL path of the Drupal installation. At the very
+ *   least, this will always default to /.
+ * - $css: An array of CSS files for the current page.
+ * - $directory: The directory the theme is located in, e.g. themes/garland or
+ *   themes/garland/minelli.
+ * - $logged_in: TRUE if the user is registered and signed in.
+ * - $is_admin: TRUE if the user has permission to access administration pages.
+ *
+ * Page metadata:
+ * - $language: (object) The language the site is being displayed in.
+ *   $language->language contains its textual representation.
+ *   $language->dir contains the language direction. It will either be 'ltr' or 'rtl'.
+ * - $head_title: A modified version of the page title, for use in the TITLE tag.
+ * - $head: Markup for the HEAD section (including meta tags, keyword tags, and
+ *   so on).
+ * - $styles: Style tags necessary to import all CSS files for the page.
+ * - $scripts: Script tags necessary to load the JavaScript files and settings
+ *   for the page.
+ *
+ * Site identity:
+ * - $site_name: The name of the site, empty when display has been disabled
+ *   in theme settings.
+ *
+ * Page content (in order of occurrance in the default page.tpl.php):
+ * - $breadcrumb: The breadcrumb trail for the current page.
+ * - $title: The page title, for use in the actual HTML content.
+ * - $help: Dynamic help text, mostly for admin pages.
+ * - $messages: HTML for status and error messages. Should be displayed prominently.
+ * - $tabs: Tabs linking to any sub-pages beneath the current page (e.g., the view
+ *   and edit tabs when displaying a node).
+ *
+ * - $content: The main content of the current Drupal page.
+ *
+ * Footer/closing data:
+ * - $footer : The footer region.
+ * - $closure: Final closing markup from any modules that have altered the page.
+ *   This variable should always be output last, after all other dynamic content.
+ *
+ * @see template_preprocess()
+ * @see template_preprocess_external_plugin_page()
+ */
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php print $language->language ?>" lang="<?php print $language->language ?>" dir="<?php print $language->dir ?>">
+
+<head>
+  <title><?php print $head_title; ?></title>
+  <?php print $head; ?>
+  <?php print $styles; ?>
+  <?php print $scripts; ?>
+  <script type="text/javascript"><?php /* Needed to avoid Flash of Unstyled Content in IE */ ?> </script>
+</head>
+<body>
+  <div id="page">
+    <div id="container" class="clear-block">
+      <div id="main" class="column">
+        <?php if (!empty($breadcrumb)): ?><div id="breadcrumb"><?php print $breadcrumb; ?></div><?php endif; ?>
+
+        <div id="content">
+          <?php if (!empty($title)): ?><h1 class="title" id="page-title"><?php print $title; ?></h1><?php endif; ?>
+          <?php if (!empty($tabs)): ?><div class="tabs"><?php print $tabs; ?></div><?php endif; ?>
+          <?php if (!empty($messages)): print $messages; endif; ?>
+          <?php if (!empty($help)): print $help; endif; ?>
+          <div id="content-content" class="clear-block">
+            <?php print $content; ?>
+          </div>
+        </div>
+
+      </div>
+    </div>
+  </div>
+<?php print $closure; ?>
+</body>
+</html>
Index: wysiwyg.dialog.inc
===================================================================
RCS file: wysiwyg.dialog.inc
diff -N wysiwyg.dialog.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ wysiwyg.dialog.inc	27 Jan 2009 00:19:30 -0000
@@ -0,0 +1,55 @@
+<?php
+// $Id: wysiwyg.admin.inc,v 1.6 2008/12/01 14:32:41 sun Exp $
+
+/**
+ * @file
+ * Wysiwyg dialog page handling.
+ */
+
+/**
+ * Menu callback; Output a wysiwyg plugin dialog page.
+ */
+function wysiwyg_dialog($plugin, $instance) {
+  $plugins = wysiwyg_get_all_plugins();
+  if (!isset($plugins[$plugin])) {
+    return drupal_access_denied();
+  }
+  $callback = $plugin . '_wysiwyg_dialog';
+  if (!function_exists($callback)) {
+    return drupal_not_found();
+  }
+  // Suppress admin menu.
+  module_invoke('admin_menu', 'suppress');
+  // Add editor instance id to Drupal.settings.
+  drupal_add_js(array('instance' => $instance), 'setting');
+
+  $output = $callback($instance);
+
+  echo theme('wysiwyg_dialog_page', $output);
+}
+
+function template_preprocess_wysiwyg_dialog_page(&$variables) {
+  global $theme;
+
+  // Construct page title
+  $head_title = array(strip_tags(drupal_get_title()), variable_get('site_name', 'Drupal'));
+
+  $variables['head_title']        = implode(' | ', $head_title);
+  $variables['base_path']         = base_path();
+  $variables['front_page']        = url();
+//  $variables['breadcrumb']        = theme('breadcrumb', drupal_get_breadcrumb());
+  $variables['head']              = drupal_get_html_head();
+  $variables['help']              = theme('help');
+  $variables['language']          = $GLOBALS['language'];
+  $variables['language']->dir     = $GLOBALS['language']->direction ? 'rtl' : 'ltr';
+  $variables['messages']          = $variables['show_messages'] ? theme('status_messages') : '';
+  $variables['site_name']         = (theme_get_setting('toggle_name') ? variable_get('site_name', 'Drupal') : '');
+  $variables['css']               = drupal_add_css();
+  $variables['styles']            = drupal_get_css();
+  $variables['scripts']           = drupal_get_js();
+  $variables['tabs']              = theme('menu_local_tasks');
+  $variables['title']             = drupal_get_title();
+  // Closure should be filled last.
+  $variables['closure']           = theme('closure');
+}
+
Index: wysiwyg.init.js
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/wysiwyg.init.js,v
retrieving revision 1.2
diff -u -p -r1.2 wysiwyg.init.js
--- wysiwyg.init.js	30 Nov 2008 17:16:27 -0000	1.2
+++ wysiwyg.init.js	1 Dec 2008 15:17:34 -0000
@@ -2,7 +2,7 @@
 
 Drupal.wysiwyg = Drupal.wysiwyg || { 'instances': {} };
 
-Drupal.wysiwyg.editor = Drupal.wysiwyg.editor || { 'init': {}, 'attach': {}, 'detach': {} };
+Drupal.wysiwyg.editor = Drupal.wysiwyg.editor || { 'init': {}, 'attach': {}, 'detach': {}, 'instance': {} };
 
 Drupal.wysiwyg.plugins = Drupal.wysiwyg.plugins || {};
 
Index: wysiwyg.js
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/wysiwyg.js,v
retrieving revision 1.7
diff -u -p -r1.7 wysiwyg.js
--- wysiwyg.js	18 Jan 2009 22:04:13 -0000	1.7
+++ wysiwyg.js	26 Jan 2009 20:58:44 -0000
@@ -76,8 +76,16 @@ Drupal.wysiwygAttach = function(context,
   if (typeof Drupal.wysiwyg.editor.attach[params.editor] == 'function') {
     // (Re-)initialize field instance.
     Drupal.wysiwyg.instances[params.field] = {};
+    // Provide editor callbacks for plugins.
+    if (typeof Drupal.wysiwyg.editor.instance[params.editor] == 'object') {
+      Drupal.wysiwyg.instances[params.field] = Drupal.wysiwyg.editor.instance[params.editor];
+    }
     // Store new editor name and status for this field.
     Drupal.wysiwyg.instances[params.field].editor = params.editor;
+    // Store instance id for this field.
+    Drupal.wysiwyg.instances[params.field].field = params.field;
+    // Store this field id, so (external) plugins can use it.
+    Drupal.wysiwyg.activeId = params.field;
     // Attach or update toggle link.
     Drupal.wysiwygAttachToggleLink(context, params);
     // Attach editor, if enabled by default or last state was enabled.
@@ -131,6 +139,7 @@ Drupal.wysiwygAttachToggleLink = functio
       Drupal.wysiwygDetach(context, params);
       // After disabling the editor, re-attach default behaviors.
       Drupal.wysiwyg.editor.attach.none(context, params);
+      Drupal.wysiwyg.instances[params.field] = Drupal.wysiwyg.editor.instance.none;
       Drupal.wysiwyg.instances[params.field].editor = 'none';
       $(this).html(Drupal.settings.wysiwyg.enable).blur();
     }
Index: wysiwyg.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/wysiwyg.module,v
retrieving revision 1.20
diff -u -p -r1.20 wysiwyg.module
--- wysiwyg.module	24 Jan 2009 23:32:02 -0000	1.20
+++ wysiwyg.module	27 Jan 2009 00:12:54 -0000
@@ -18,16 +18,31 @@ function wysiwyg_menu() {
     'access arguments' => array('administer filters'),
     'file' => 'wysiwyg.admin.inc',
   );
+  $items['wysiwyg/%'] = array(
+    'page callback' => 'wysiwyg_dialog',
+    'page arguments' => array(1),
+    'access arguments' => array('access content'),
+    'type' => MENU_CALLBACK,
+    'file' => 'wysiwyg.dialog.inc',
+  );
   return $items;
 }
 
 /**
  * Implementation of hook_theme().
+ *
+ * @see drupal_common_theme(), common.inc
+ * @see template_preprocess_page(), theme.inc
  */
 function wysiwyg_theme() {
   return array(
     'wysiwyg_profile_overview' => array('arguments' => array('form' => NULL)),
     'wysiwyg_admin_button_table' => array('arguments' => array('form' => NULL)),
+    'wysiwyg_dialog_page' => array(
+      'arguments' => array('content' => NULL, 'show_messages' => TRUE),
+      'file' => 'wysiwyg.dialog.inc',
+      'template' => 'wysiwyg-dialog-page',
+    ),
   );
 }
 
@@ -124,10 +139,10 @@ function wysiwyg_process_form(&$form) {
               // Check editor theme (and reset it if not/no longer available).
               $theme = wysiwyg_get_editor_themes($profile, (isset($profile->settings['theme']) ? $profile->settings['theme'] : ''));
 
+              // Add plugin settings (first) for this input format.
+              wysiwyg_add_plugin_settings($profile);
               // 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);
             }
 
             // Use a prefix/suffix for a single input format, or attach to input
@@ -320,24 +335,55 @@ function wysiwyg_add_editor_settings($pr
 function wysiwyg_add_plugin_settings($profile) {
   static $plugins_added = array();
   
-  if (!isset($plugins_added[$profile->editor])) {
-    $plugins = array();
-    $editor = wysiwyg_get_editor($profile->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) {
+  // External plugins must only be loaded once.
+  // @todo Actually, each native plugin must not be added twice, but different
+  //   profiles can have different plugin-sets for the same editor.  We should
+  //   check each plugin for each editor.
+  if (isset($plugins_added[$profile->editor])) {
+    return;
+  }
+  
+  $editor = wysiwyg_get_editor($profile->editor);
+  // Assume that this editor does not support neither native external plugins,
+  // nor Drupal plugins if it does not provide a callback.
+  if (isset($editor['plugin settings callback']) && function_exists($editor['plugin settings callback'])) {
+    // Collect native editor plugins provided via hook_wysiwyg_plugin().
+    $plugins = module_invoke_all('wysiwyg_plugin', $editor['name'], $editor['installed version']);
+    // Only keep enabled native plugins in this profile.
+    foreach ($plugins as $plugin => $meta) {
       if (!isset($profile->settings['buttons'][$plugin])) {
-        unset($info[$plugin]);
+        unset($plugins[$plugin]);
       }
     }
-
-    if (isset($editor['plugin settings callback']) && function_exists($editor['plugin settings callback'])) {
-      $plugins = $editor['plugin settings callback']($editor, $profile, $info);
+    // Invoke the editor's plugin settings callback, so it can populate the
+    // settings for external plugins with custom, required values.
+    $plugins = $editor['plugin settings callback']($editor, $profile, $plugins);
+
+    drupal_add_js(array('wysiwyg' => array('plugins' => array($profile->editor => array('native' => $plugins)))), 'setting');
+
+    // Collect, load, and add API plugins provided by Drupal modules.
+    if (isset($editor['proxy plugin']) && isset($editor['proxy plugin settings callback']) && function_exists($editor['proxy plugin settings callback'])) {
+      $plugins += $editor['proxy plugin'];
+      $proxy = key($editor['proxy plugin']);
+      $proxy_plugins = array();
+      foreach (wysiwyg_get_all_plugins() as $plugin_name => $meta) {
+        if (isset($profile->settings['buttons'][$proxy][$plugin_name])) {
+          $proxy_plugins[$plugin_name] = $meta;
+          // Load the Drupal plugin's JavaScript.
+          drupal_add_js($meta['js path'] .'/'. $meta['js file']);
+          // Add plugin specific settings.
+          if (isset($meta['settings'])) {
+            drupal_add_js(array('wysiwyg' => array('plugins' => array('drupal' => array($plugin_name => $meta['settings'])))), 'setting');
+          }
+        }
+      }
+      // Invoke the editor's proxy plugin settings callback, so it can populate
+      // the settings for Drupal plugins with custom, required values.
+      $proxy_plugins = $editor['proxy plugin settings callback']($editor, $profile, $proxy_plugins);
+  
+      drupal_add_js(array('wysiwyg' => array('plugins' => array($profile->editor => array('drupal' => $proxy_plugins)))), 'setting');
     }
 
-    drupal_add_js(array('wysiwyg' => array('plugins' => array($profile->editor => $plugins))), 'setting');
-
     $plugins_added[$profile->editor] = TRUE;
   }
 }
@@ -398,11 +444,17 @@ function wysiwyg_get_plugins($editor_nam
     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';
-  
     // Add editor plugins provided via hook_wysiwyg_plugin().
     $plugins = array_merge($plugins, module_invoke_all('wysiwyg_plugin', $editor['name'], $editor['installed version']));
+    // Add API plugins provided by Drupal modules.
+    // @todo We need to pass the filepath to the plugin icon for Drupal plugins.
+    if (isset($editor['proxy plugin'])) {
+      $plugins += $editor['proxy plugin'];
+      $proxy = key($editor['proxy plugin']);
+      foreach (wysiwyg_get_all_plugins() as $plugin_name => $info) {
+        $plugins[$proxy]['buttons'][$plugin_name] = $info['title'];
+      }
+    }
   }
   return $plugins;
 }
@@ -566,6 +618,46 @@ function wysiwyg_get_all_editors() {
 }
 
 /**
+ * Invoke hook_wysiwyg_plugin() in all modules.
+ */
+function wysiwyg_get_all_plugins() {
+  static $plugins;
+
+  if (isset($plugins)) {
+    return $plugins;
+  }
+
+  $plugins = wysiwyg_load_includes('plugins', 'plugin');
+  foreach ($plugins as $name => $properties) {
+    $plugin = &$plugins[$name];
+    // Fill in required/default properties.
+    $plugin += array(
+      'title' => $plugin['name'],
+      'vendor url' => '',
+      'js path' => $plugin['path'] . '/' . $plugin['name'],
+      'js file' => $plugin['name'] . '.js',
+      'css path' => $plugin['path'] . '/' . $plugin['name'],
+      'css file' => $plugin['name'] . '.css',
+      'icon path' => $plugin['path'] . '/' . $plugin['name'] . '/images',
+      'icon file' => $plugin['name'] . '.png',
+      'dialog path' => $plugin['name'],
+      'dialog settings' => array(),
+      'settings callback' => NULL,
+      'settings form callback' => NULL,
+    );
+    // Fill in default settings.
+    $plugin['settings'] += array(
+      'path' => base_path() . $plugin['path'] . '/' . $plugin['name'],
+    );
+    // Check whether library is present.
+    if (!($plugin['installed'] = file_exists($plugin['js path'] . '/' . $plugin['js file']))) {
+      continue;
+    }
+  }
+  return $plugins;
+}
+
+/**
  * Load include files for wysiwyg implemented by all modules.
  *
  * @param $type
Index: wysiwyg.plugins.inc
===================================================================
RCS file: wysiwyg.plugins.inc
diff -N wysiwyg.plugins.inc
--- wysiwyg.plugins.inc	14 Oct 2008 21:45:07 -0000	1.1
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,23 +0,0 @@
-<?php
-// $Id: wysiwyg.plugins.inc,v 1.1 2008/10/14 21:45:07 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: editors/tinymce.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/editors/tinymce.inc,v
retrieving revision 1.21
diff -u -p -r1.21 tinymce.inc
--- editors/tinymce.inc	7 Jan 2009 09:02:25 -0000	1.21
+++ editors/tinymce.inc	27 Jan 2009 00:01:48 -0000
@@ -33,12 +33,19 @@ function wysiwyg_tinymce_editor() {
     'settings callback' => 'wysiwyg_tinymce_settings',
     'plugin callback' => 'wysiwyg_tinymce_plugins',
     'plugin settings callback' => 'wysiwyg_tinymce_plugin_settings',
+    'proxy plugin settings callback' => 'wysiwyg_tinymce_proxy_plugin_settings',
     'versions' => array( // Each version can override global editor properties.
       '2.1' => array(
         // 'include files' => array('tinymce-2.inc'),
         'js files' => array('tinymce-2.js'),
         'css files' => array('tinymce-2.css'),
         'download url' => 'http://sourceforge.net/project/showfiles.php?group_id=103281&package_id=111430&release_id=557383',
+        'proxy plugin' => array(
+          'drupal' => array(
+            'load' => TRUE,
+            'internal' => TRUE,
+          ),
+        ),
       ),
       '3.2' => array(
         // 'include files' => array('tinymce-3.inc'),
@@ -61,6 +68,12 @@ function wysiwyg_tinymce_editor() {
             'files' => array('tiny_mce_src.js'),
           ),
         ),
+        'proxy plugin' => array(
+          'drupal' => array(
+            'load' => TRUE,
+            'internal' => TRUE,
+          ),
+        ),
       ),
     ),
     // Optional properties
@@ -191,7 +204,12 @@ function wysiwyg_tinymce_settings($edito
           }
           // Add internal buttons that also need to be loaded as extension.
           else if ($type == 'buttons' && !empty($plugins[$plugin]['load'])) {
-            $init['extensions'][$plugin] = 1;
+            if ($plugin == 'drupal') {
+              $init['extensions'][$button] = 1;
+            }
+            else {
+              $init['extensions'][$plugin] = 1;
+            }
           }
           // Add plain extensions.
           else if ($type == 'extensions' && !empty($plugins[$plugin]['load'])) {
@@ -293,10 +311,10 @@ function wysiwyg_tinymce_themes($editor,
 }
 
 /**
- * Build a JS settings array of external plugins that need to be loaded separately.
+ * Build a JS settings array of native external plugins that need to be loaded separately.
  *
  * TinyMCE requires that external plugins (i.e. not residing in the editor's
- * directory) are loaded (once) after the editor has been initialized.
+ * directory) are loaded (once) upon initializing the editor.
  */
 function wysiwyg_tinymce_plugin_settings($editor, $profile, $plugins) {
   $settings = array();
@@ -309,6 +327,23 @@ function wysiwyg_tinymce_plugin_settings
 }
 
 /**
+ * Build a JS settings array for Drupal plugins loaded via the proxy plugin.
+ */
+function wysiwyg_tinymce_proxy_plugin_settings($editor, $profile, $plugins) {
+  $settings = array();
+  foreach ($plugins as $name => $plugin) {
+    $settings[$name] = $plugin['dialog settings'] + array(
+      'title' => $plugin['title'],
+      'icon' => base_path() . $plugin['icon path'] .'/'. $plugin['icon file'],
+      'iconTitle' => $plugin['icon title'],
+      // @todo These should only be set if the plugin defined them.
+      'css' => base_path() . $plugin['css path'] .'/'. $plugin['css file'],
+    );
+  }
+  return $settings;
+}
+
+/**
  * Add or remove leading hiven to/of external plugin names.
  *
  * TinyMCE requires that external plugins, which should not be loaded from
Index: editors/js/none.js
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/editors/js/none.js,v
retrieving revision 1.3
diff -u -p -r1.3 none.js
--- editors/js/none.js	28 Oct 2008 22:46:05 -0000	1.3
+++ editors/js/none.js	26 Jan 2009 21:09:29 -0000
@@ -43,3 +43,28 @@ Drupal.wysiwyg.editor.detach.none = func
   }
 };
 
+/**
+ * Instance methods for plain text areas.
+ */
+Drupal.wysiwyg.editor.instance.none = {
+  insert: function(content) {
+    var editor = document.getElementById(this.field);
+
+    // IE support.
+    if (document.selection) {
+      editor.focus();
+      sel = document.selection.createRange();
+      sel.text = content;
+    }
+    // Mozilla/Firefox/Netscape 7+ support.
+    else if (editor.selectionStart || editor.selectionStart == '0') {
+      var startPos = editor.selectionStart;
+      var endPos = editor.selectionEnd;
+      editor.value = editor.value.substring(0, startPos) + content + editor.value.substring(endPos, editor.value.length);
+    }
+    // Fallback, just add to the end of the content.
+    else {
+      editor.value += content;
+    }
+  }
+};
Index: editors/js/tinymce-2.js
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/editors/js/tinymce-2.js,v
retrieving revision 1.7
diff -u -p -r1.7 tinymce-2.js
--- editors/js/tinymce-2.js	1 Dec 2008 14:14:41 -0000	1.7
+++ editors/js/tinymce-2.js	27 Jan 2009 01:04:17 -0000
@@ -21,8 +21,13 @@ Drupal.wysiwyg.editor.init.tinymce = fun
   for (var format in settings) {
     tinyMCE.init(settings[format]);
   }
-  for (var plugin in Drupal.settings.wysiwyg.plugins.tinymce) {
-    tinyMCE.loadPlugin(plugin, Drupal.settings.wysiwyg.plugins.tinymce[plugin]);
+  // Load native external plugins.
+  for (var plugin in Drupal.settings.wysiwyg.plugins.tinymce.native) {
+    tinyMCE.loadPlugin(plugin, Drupal.settings.wysiwyg.plugins.tinymce.native[plugin]);
+  }
+  // Load Drupal plugins.
+  for (var plugin in Drupal.settings.wysiwyg.plugins.tinymce.drupal) {
+    Drupal.wysiwyg.editor.instance.tinymce.addPlugin(plugin, Drupal.settings.wysiwyg.plugins.tinymce.drupal[plugin], Drupal.settings.wysiwyg.plugins.drupal[plugin]);
   }
 };
 
@@ -56,3 +61,135 @@ Drupal.wysiwyg.editor.detach.tinymce = f
 //  }
 };
 
+Drupal.wysiwyg.editor.instance.tinymce = {
+  addPlugin: function(plugin, settings, pluginSettings) {
+    if (typeof Drupal.wysiwyg.plugins[plugin] != 'object') {
+      return;
+    }
+    tinyMCE.addPlugin(plugin, {
+
+      // Register an editor command for this plugin, invoked by the plugin's button.
+      execCommand: function(editor_id, element, command, user_interface, value) {
+        switch (command) {
+          case plugin:
+            if (typeof Drupal.wysiwyg.plugins[plugin].invoke == 'function') {
+              var ed = tinyMCE.getInstanceById(editor_id);
+              var data = { format: 'html', node: ed.getFocusElement(), content: ed.getFocusElement() };
+              Drupal.wysiwyg.plugins[plugin].invoke(data, pluginSettings, ed.formTargetElementId);
+              return true;
+            }
+        }
+        // Pass to next handler in chain.
+        return false;
+      },
+
+      // Register the plugin button.
+      getControlHTML: function(control_name) {
+        switch (control_name) {
+          case plugin:
+            return tinyMCE.getButtonHTML(control_name, settings.iconTitle, settings.icon, plugin);
+        }
+        return '';
+      },
+
+      // Load custom CSS for editor contents on startup.
+      initInstance: function(ed) {
+        if (settings.css) {
+          tinyMCE.importCSS(ed.getDoc(), settings.css);
+        }
+      },
+
+      cleanup: function(type, content) {
+        switch (type) {
+          case 'insert_to_editor':
+            // Attach: Replace plain text with HTML representations.
+            if (typeof Drupal.wysiwyg.plugins[plugin].attach == 'function') {
+              content = Drupal.wysiwyg.plugins[plugin].attach(content, pluginSettings, tinyMCE.selectedInstance.editorId);
+              content = Drupal.wysiwyg.editor.instance.tinymce.prepareContent(content);
+            }
+            break;
+
+          case 'get_from_editor':
+            // Detach: Replace HTML representations with plain text.
+            if (typeof Drupal.wysiwyg.plugins[plugin].detach == 'function') {
+              content = Drupal.wysiwyg.plugins[plugin].detach(content, pluginSettings, tinyMCE.selectedInstance.editorId);
+            }
+            break;
+        }
+        // Pass through to next handler in chain
+        return content;
+      },
+
+      // isNode: Return whether the plugin button should be enabled for the
+      // current selection.
+      handleNodeChange: function(editor_id, node, undo_index, undo_levels, visual_aid, any_selection) {
+        if (node == null) {
+          return;
+        }
+        if (typeof Drupal.wysiwyg.plugins[plugin].isNode == 'function') {
+          if (Drupal.wysiwyg.plugins[plugin].isNode(node)) {
+            tinyMCE.switchClass(editor_id + '_' + plugin, 'mceButtonSelected');
+            return true;
+          }
+        }
+        tinyMCE.switchClass(editor_id + '_' + plugin, 'mceButtonNormal');
+        return true;
+      },
+
+      /**
+       * Return information about the plugin as a name/value array.
+       */
+      getInfo: function() {
+        return {
+          longname: settings.title
+        };
+      }
+    });
+  },
+
+  openDialog: function(dialog, params) {
+    var editor = tinyMCE.getInstanceById(this.field);
+    tinyMCE.openWindow({
+      file: dialog.url + '/' + this.field,
+      width: dialog.width,
+      height: dialog.height,
+      inline: 1
+    }, params);
+  },
+
+  closeDialog: function(dialog) {
+    var editor = tinyMCE.getInstanceById(this.field);
+    tinyMCEPopup.close();
+  },
+
+  prepareContent: function(content) {
+    // Certain content elements need to have additional DOM properties applied
+    // to prevent this editor from highlighting an internal button in addition
+    // to the button of a Drupal plugin.
+    var specialProperties = {
+      img: { name: 'mce_drupal' }
+    };
+    $content = $('<div>' + content + '</div>'); // No .outerHTML() in jQuery :(
+    jQuery.each(specialProperties, function(element, properties) {
+      $content.find(element).each(function() {
+        for (var property in properties) {
+          if (property == 'class') {
+            $(this).addClass(properties[property]);
+          }
+          else {
+            $(this).attr(property, properties[property]);
+          }
+        }
+      });
+    });
+    return $content.html();
+  },
+
+  insert: function(content) {
+    content = this.prepareContent(content);
+    var editor = tinyMCE.getInstanceById(this.field);
+    editor.execCommand('mceInsertContent', false, content);
+    editor.repaint();
+  }
+};
+
Index: editors/js/tinymce-3.js
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/editors/js/tinymce-3.js,v
retrieving revision 1.9
diff -u -p -r1.9 tinymce-3.js
--- editors/js/tinymce-3.js	1 Dec 2008 14:14:41 -0000	1.9
+++ editors/js/tinymce-3.js	26 Jan 2009 23:59:16 -0000
@@ -22,8 +22,13 @@ Drupal.wysiwyg.editor.init.tinymce = fun
   for (var format in settings) {
     tinyMCE.init(settings[format]);
   }
-  for (var plugin in Drupal.settings.wysiwyg.plugins.tinymce) {
-    tinymce.PluginManager.load(plugin, Drupal.settings.wysiwyg.plugins.tinymce[plugin]);
+  // Load native external plugins.
+  for (var plugin in Drupal.settings.wysiwyg.plugins.tinymce.native) {
+    tinymce.PluginManager.load(plugin, Drupal.settings.wysiwyg.plugins.tinymce.native[plugin]);
+  }
+  // Load Drupal plugins.
+  for (var plugin in Drupal.settings.wysiwyg.plugins.tinymce.drupal) {
+    Drupal.wysiwyg.editor.instance.tinymce.addPlugin(plugin, Drupal.settings.wysiwyg.plugins.tinymce.drupal[plugin], Drupal.settings.wysiwyg.plugins.drupal[plugin]);
   }
 };
 
@@ -34,11 +39,13 @@ Drupal.wysiwyg.editor.init.tinymce = fun
  */
 Drupal.wysiwyg.editor.attach.tinymce = function(context, params, settings) {
   // Configure editor settings for this input format.
-  for (var setting in settings) {
-    tinyMCE.settings[setting] = settings[setting];
-  }
+  var ed = new tinymce.Editor(params.field, settings);
+  // Reset active instance id on any event.
+  ed.onEvent.add(function(ed, e) {
+    Drupal.wysiwyg.activeId = ed.id;
+  });
   // Attach editor.
-  tinyMCE.execCommand('mceAddControl', true, params.field);
+  ed.render();
 };
 
 /**
@@ -64,3 +71,133 @@ Drupal.wysiwyg.editor.detach.tinymce = f
   }
 };
 
+Drupal.wysiwyg.editor.instance.tinymce = {
+  addPlugin: function(plugin, settings, pluginSettings) {
+    if (typeof Drupal.wysiwyg.plugins[plugin] != 'object') {
+      return;
+    }
+    tinymce.create('tinymce.plugins.' + plugin, {
+      /**
+       * Initialize the plugin, executed after the plugin has been created.
+       *
+       * @param ed
+       *   The tinymce.Editor instance the plugin is initialized in.
+       * @param url
+       *   The absolute URL of the plugin location.
+       */
+      init: function(ed, url) {
+        // Register an editor command for this plugin, invoked by the plugin's button.
+        ed.addCommand(plugin, function() {
+          if (typeof Drupal.wysiwyg.plugins[plugin].invoke == 'function') {
+            var data = { format: 'html', node: ed.selection.getNode(), content: ed.selection.getContent() };
+            Drupal.wysiwyg.plugins[plugin].invoke(data, pluginSettings, ed.id);
+          }
+          // @todo
+          if (0) {
+            ed.windowManager.open({
+              // @todo Token replacement for passing parameters like editor
+              //   instance id required?
+              file : BASE_URL + 'index.php?q=' + settings.dialogPath,
+              width : settings.width + parseInt(ed.getLang(plugin + '.delta_width', 0)),
+              height : settings.height + parseInt(ed.getLang(plugin + '.delta_height', 0)),
+              inline : settings.inline
+            }, data);
+          }
+        });
+
+        // Register the plugin button.
+        ed.addButton(plugin, {
+          title : settings.iconTitle,
+          cmd : plugin,
+          image : settings.icon
+        });
+
+        // Load custom CSS for editor contents on startup.
+        ed.onInit.add(function() {
+          if (settings.css) {
+            ed.dom.loadCSS(settings.css);
+          }
+        });
+
+        // Attach: Replace plain text with HTML representations.
+        ed.onBeforeSetContent.add(function(ed, data) {
+          if (typeof Drupal.wysiwyg.plugins[plugin].attach == 'function') {
+            data.content = Drupal.wysiwyg.plugins[plugin].attach(data.content, pluginSettings, ed.id);
+            data.content = Drupal.wysiwyg.editor.instance.tinymce.prepareContent(data.content);
+          }
+        });
+
+        // Detach: Replace HTML representations with plain text.
+        ed.onGetContent.add(function(ed, data) {
+          if (typeof Drupal.wysiwyg.plugins[plugin].detach == 'function') {
+            data.content = Drupal.wysiwyg.plugins[plugin].detach(data.content, pluginSettings, ed.id);
+          }
+        });
+
+        // isNode: Return whether the plugin button should be enabled for the
+        // current selection.
+        ed.onNodeChange.add(function(ed, command, node) {
+          if (typeof Drupal.wysiwyg.plugins[plugin].isNode == 'function') {
+            command.setActive(plugin, Drupal.wysiwyg.plugins[plugin].isNode(node));
+          }
+        });
+      },
+
+      /**
+       * Return information about the plugin as a name/value array.
+       */
+      getInfo: function() {
+        return {
+          longname: settings.title
+        };
+      }
+    });
+
+    // Register plugin.
+    tinymce.PluginManager.add(plugin, tinymce.plugins[plugin]);
+  },
+
+  openDialog: function(dialog, params) {
+    var editor = tinyMCE.get(this.field);
+    editor.windowManager.open({
+      file: dialog.url + '/' + this.field,
+      width: dialog.width,
+      height: dialog.height,
+      inline: 1
+    }, params);
+  },
+
+  closeDialog: function(dialog) {
+    var editor = tinyMCE.get(this.field);
+    editor.windowManager.close(dialog);
+  },
+
+  prepareContent: function(content) {
+    // Certain content elements need to have additional DOM properties applied
+    // to prevent this editor from highlighting an internal button in addition
+    // to the button of a Drupal plugin.
+    var specialProperties = {
+      img: { class: 'mceItem' }
+    };
+    $content = $('<div>' + content + '</div>'); // No .outerHTML() in jQuery :(
+    jQuery.each(specialProperties, function(element, properties) {
+      $content.find(element).each(function() {
+        for (var property in properties) {
+          if (property == 'class') {
+            $(this).addClass(properties[property]);
+          }
+          else {
+            $(this).attr(property, properties[property]);
+          }
+        }
+      });
+    });
+    return $content.html();
+  },
+
+  insert: function(content) {
+    content = this.prepareContent(content);
+    tinyMCE.execInstanceCommand(this.field, 'mceInsertContent', false, content);
+  }
+};
+
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	25 Jan 2009 03:22:35 -0000
@@ -0,0 +1,22 @@
+<?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 is set by default.
+      // '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	25 Jan 2009 03:53:14 -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.instances[instanceId].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" />';
+  }
+};
Index: plugins/break/editor_plugin.js
===================================================================
RCS file: plugins/break/editor_plugin.js
diff -N plugins/break/editor_plugin.js
--- plugins/break/editor_plugin.js	10 Jun 2008 18:20:14 -0000	1.1
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,148 +0,0 @@
-// $Id: editor_plugin.js,v 1.1 2008/06/10 18:20:14 sun Exp $
-
-// Import plugin language.
-tinyMCE.importPluginLanguagePack('wysiwyg', 'en');
-
-var TinyMCE_wysiwygBreakPlugin = {
-  getInfo: function() {
-    return {
-      longname: 'Teaser break',
-      author: 'Nathan Haug',
-      authorurl: 'http://www.quicksketch.org',
-      infourl: 'http://drupal.org/project/wysiwyg'
-    };
-  },
-
-  initInstance: function(inst) {
-    tinyMCE.importCSS(inst.getDoc(), this.baseURL + '/break.css');
-  },
-
-  getControlHTML: function (control_name) {
-    switch (control_name) {
-      case 'break':
-        return tinyMCE.getButtonHTML(control_name, 'lang_break_desc', '{$pluginurl}/images/break.gif', 'break', false, 'null');
-    }
-    return '';
-  },
-
-  execCommand: function(editor_id, element, command, user_interface, value) {
-    switch (command) {
-      case 'break':
-        var classValue = '';
-        var template = new Array();
-        var inst = tinyMCE.getInstanceById(editor_id);
-        var focusElm = inst.getFocusElement();
-
-        // Check whether selection is an image and belongs to this plugin.
-        if (focusElm != null && focusElm.nodeName.toLowerCase() == 'img') {
-          classValue = this.getAttrib(focusElm, 'class');
-          if (classValue != 'wysiwyg-break') {
-            return true;
-          }
-        }
-
-        html = '<img src="' + tinyMCE.getParam('theme_href') + '/images/spacer.gif" alt="&lt;--break-&gt;" title="&lt;--break--&gt;" class="wysiwyg-break" />';
-        tinyMCE.execInstanceCommand(editor_id, 'mceInsertContent', false, html);
-        return true;
-    }
-    // Pass to next handler in chain.
-    return false;
-  },
-
-  cleanup: function(type, content) {
-    switch (type) {
-      case 'insert_to_editor':
-        // Parse all <!--break--> tags and replace them with images.
-        var startPos = 0;
-        while ((startPos = content.indexOf('<!--break-->', startPos)) != -1) {
-          // Insert image.
-          var contentAfter = content.substring(startPos + 12);
-          content = content.substring(0, startPos);
-          content += '<img src="' + tinyMCE.getParam('theme_href') + '/images/spacer.gif" alt="&lt;--break-&gt;" title="&lt;--break--&gt;" class="wysiwyg-break" />';
-          content += contentAfter;
-          startPos++;
-        }
-        break;
-
-      case 'get_from_editor':
-        // Parse all img tags and replace them with <!--break-->.
-        var startPos = -1;
-        while ((startPos = content.indexOf('<img', startPos + 1)) != -1) {
-          var endPos = content.indexOf('/>', startPos);
-          var attribs = parseAttributes(content.substring(startPos + 4, endPos));
-          if (attribs['class'] == 'wysiwyg-break') {
-            endPos += 2;
-            chunkBefore = content.substring(0, startPos);
-            chunkAfter = content.substring(endPos);
-            content = chunkBefore + '<!--break-->' + chunkAfter;
-          }
-        }
-        break;
-    }
-    // Pass through to next handler in chain
-    return content;
-
-    /**
-     * Local function that parses the break image in and out.
-     */
-    function parseAttributes (attribute_string) {
-      var attributeName = '', attributeValue = '', withInName, withInValue;
-      var attributes = new Array();
-      var whiteSpaceRegExp = new RegExp('^[ \n\r\t]+', 'g');
-
-      if (attribute_string == null || attribute_string.length < 2) {
-        return null;
-      }
-      withInName = withInValue = false;
-      for (var i = 0; i < attribute_string.length; i++) {
-        var chr = attribute_string.charAt(i);
-        if ((chr == '"' || chr == "'") && !withInValue) {
-          withInValue = true;
-        }
-        else if ((chr == '"' || chr == "'") && withInValue) {
-          withInValue = false;
-          var pos = attributeName.lastIndexOf(' ');
-          if (pos != -1) {
-            attributeName = attributeName.substring(pos+1);
-          }
-          attributes[attributeName.toLowerCase()] = attributeValue.substring(1).toLowerCase();
-          attributeName = '';
-          attributeValue = '';
-        }
-        else if (!whiteSpaceRegExp.test(chr) && !withInName && !withInValue) {
-          withInName = true;
-        }
-        if (chr == '=' && withInName) {
-          withInName = false;
-        }
-        if (withInName) {
-          attributeName += chr;
-        }
-        if (withInValue) {
-          attributeValue += chr;
-        }
-      }
-      return attributes;
-    }
-  },
-
-  handleNodeChange: function(editor_id, node, undo_index, undo_levels, visual_aid, any_selection) {
-    tinyMCE.switchClass(editor_id + '_wysiwyg_break', 'mceButtonNormal');
-    if (node == null) {
-      return;
-    }
-    do {
-      if (node.nodeName.toLowerCase() == 'img' && this.getAttrib(node, 'class').indexOf('wysiwyg-break') == 0) {
-        tinyMCE.switchClass(editor_id + '_wysiwyg_break', 'mceButtonSelected');
-      }
-    } while ((node = node.parentNode));
-    return true;
-  },
-
-  getAttrib: function(elm, name) {
-    return elm.getAttribute(name) ? elm.getAttribute(name) : '';
-  }
-};
-
-tinyMCE.addPlugin('wysiwyg', TinyMCE_wysiwygBreakPlugin);
-
Index: plugins/break/editor_plugin_src.js
===================================================================
RCS file: plugins/break/editor_plugin_src.js
diff -N plugins/break/editor_plugin_src.js
--- plugins/break/editor_plugin_src.js	10 Jun 2008 18:20:14 -0000	1.1
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,148 +0,0 @@
-// $Id: editor_plugin_src.js,v 1.1 2008/06/10 18:20:14 sun Exp $
-
-// Import plugin language.
-tinyMCE.importPluginLanguagePack('wysiwyg', 'en');
-
-var TinyMCE_wysiwygBreakPlugin = {
-  getInfo: function() {
-    return {
-      longname: 'Teaser break',
-      author: 'Nathan Haug',
-      authorurl: 'http://www.quicksketch.org',
-      infourl: 'http://drupal.org/project/wysiwyg'
-    };
-  },
-
-  initInstance: function(inst) {
-    tinyMCE.importCSS(inst.getDoc(), this.baseURL + '/break.css');
-  },
-
-  getControlHTML: function (control_name) {
-    switch (control_name) {
-      case 'break':
-        return tinyMCE.getButtonHTML(control_name, 'lang_break_desc', '{$pluginurl}/images/break.gif', 'break', false, 'null');
-    }
-    return '';
-  },
-
-  execCommand: function(editor_id, element, command, user_interface, value) {
-    switch (command) {
-      case 'break':
-        var classValue = '';
-        var template = new Array();
-        var inst = tinyMCE.getInstanceById(editor_id);
-        var focusElm = inst.getFocusElement();
-
-        // Check whether selection is an image and belongs to this plugin.
-        if (focusElm != null && focusElm.nodeName.toLowerCase() == 'img') {
-          classValue = this.getAttrib(focusElm, 'class');
-          if (classValue != 'wysiwyg-break') {
-            return true;
-          }
-        }
-
-        html = '<img src="' + tinyMCE.getParam('theme_href') + '/images/spacer.gif" alt="&lt;--break-&gt;" title="&lt;--break--&gt;" class="wysiwyg-break" />';
-        tinyMCE.execInstanceCommand(editor_id, 'mceInsertContent', false, html);
-        return true;
-    }
-    // Pass to next handler in chain.
-    return false;
-  },
-
-  cleanup: function(type, content) {
-    switch (type) {
-      case 'insert_to_editor':
-        // Parse all <!--break--> tags and replace them with images.
-        var startPos = 0;
-        while ((startPos = content.indexOf('<!--break-->', startPos)) != -1) {
-          // Insert image.
-          var contentAfter = content.substring(startPos + 12);
-          content = content.substring(0, startPos);
-          content += '<img src="' + tinyMCE.getParam('theme_href') + '/images/spacer.gif" alt="&lt;--break-&gt;" title="&lt;--break--&gt;" class="wysiwyg-break" />';
-          content += contentAfter;
-          startPos++;
-        }
-        break;
-
-      case 'get_from_editor':
-        // Parse all img tags and replace them with <!--break-->.
-        var startPos = -1;
-        while ((startPos = content.indexOf('<img', startPos + 1)) != -1) {
-          var endPos = content.indexOf('/>', startPos);
-          var attribs = parseAttributes(content.substring(startPos + 4, endPos));
-          if (attribs['class'] == 'wysiwyg-break') {
-            endPos += 2;
-            chunkBefore = content.substring(0, startPos);
-            chunkAfter = content.substring(endPos);
-            content = chunkBefore + '<!--break-->' + chunkAfter;
-          }
-        }
-        break;
-    }
-    // Pass through to next handler in chain
-    return content;
-
-    /**
-     * Local function that parses the break image in and out.
-     */
-    function parseAttributes (attribute_string) {
-      var attributeName = '', attributeValue = '', withInName, withInValue;
-      var attributes = new Array();
-      var whiteSpaceRegExp = new RegExp('^[ \n\r\t]+', 'g');
-
-      if (attribute_string == null || attribute_string.length < 2) {
-        return null;
-      }
-      withInName = withInValue = false;
-      for (var i = 0; i < attribute_string.length; i++) {
-        var chr = attribute_string.charAt(i);
-        if ((chr == '"' || chr == "'") && !withInValue) {
-          withInValue = true;
-        }
-        else if ((chr == '"' || chr == "'") && withInValue) {
-          withInValue = false;
-          var pos = attributeName.lastIndexOf(' ');
-          if (pos != -1) {
-            attributeName = attributeName.substring(pos+1);
-          }
-          attributes[attributeName.toLowerCase()] = attributeValue.substring(1).toLowerCase();
-          attributeName = '';
-          attributeValue = '';
-        }
-        else if (!whiteSpaceRegExp.test(chr) && !withInName && !withInValue) {
-          withInName = true;
-        }
-        if (chr == '=' && withInName) {
-          withInName = false;
-        }
-        if (withInName) {
-          attributeName += chr;
-        }
-        if (withInValue) {
-          attributeValue += chr;
-        }
-      }
-      return attributes;
-    }
-  },
-
-  handleNodeChange: function(editor_id, node, undo_index, undo_levels, visual_aid, any_selection) {
-    tinyMCE.switchClass(editor_id + '_wysiwyg_break', 'mceButtonNormal');
-    if (node == null) {
-      return;
-    }
-    do {
-      if (node.nodeName.toLowerCase() == 'img' && this.getAttrib(node, 'class').indexOf('wysiwyg-break') == 0) {
-        tinyMCE.switchClass(editor_id + '_wysiwyg_break', 'mceButtonSelected');
-      }
-    } while ((node = node.parentNode));
-    return true;
-  },
-
-  getAttrib: function(elm, name) {
-    return elm.getAttribute(name) ? elm.getAttribute(name) : '';
-  }
-};
-
-tinyMCE.addPlugin('wysiwyg', TinyMCE_wysiwygBreakPlugin);
-
