Index: wysiwyg.init.js
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/wysiwyg.init.js,v
retrieving revision 1.1.2.3
diff -u -p -r1.1.2.3 wysiwyg.init.js
--- wysiwyg.init.js	11 Oct 2008 20:00:20 -0000	1.1.2.3
+++ wysiwyg.init.js	11 Oct 2008 20:01:29 -0000
@@ -2,9 +2,9 @@
 
 Drupal.behaviors = Drupal.behaviors || {}; // D5 only.
 
-Drupal.wysiwyg = Drupal.wysiwyg || {};
+Drupal.wysiwyg = Drupal.wysiwyg || { 'invoke': {} };
 
-Drupal.wysiwyg.editor = Drupal.wysiwyg.editor || { 'init': {}, 'attach': {}, 'detach': {} };
+Drupal.wysiwyg.editor = Drupal.wysiwyg.editor || { 'init': {}, 'attach': {}, 'detach': {}, 'invoke': {} };
 
 Drupal.wysiwyg.plugins = Drupal.wysiwyg.plugins || {};
 
Index: wysiwyg_editor.js
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/wysiwyg_editor.js,v
retrieving revision 1.1.2.10
diff -u -p -r1.1.2.10 wysiwyg_editor.js
--- wysiwyg_editor.js	11 Oct 2008 19:54:46 -0000	1.1.2.10
+++ wysiwyg_editor.js	12 Oct 2008 03:13:23 -0000
@@ -81,6 +81,12 @@ Drupal.wysiwygAttach = function(context,
   if (typeof Drupal.wysiwyg.editor.attach[params.editor] == 'function') {
     // Attach editor.
     Drupal.wysiwyg.editor.attach[params.editor](context, params, Drupal.wysiwyg.clone(Drupal.settings.wysiwygEditor.configs[params.editor]));
+    // Provide editor callbacks for plugins.
+    if (typeof Drupal.wysiwyg.editor.invoke[params.editor] == 'object') {
+      Drupal.wysiwyg.invoke = Drupal.wysiwyg.editor.invoke[params.editor];
+    }
+    // Store this editor id, so (external) plugins can use it.
+    Drupal.wysiwyg.activeId = params.field;
     // Display toggle link.
     $('#wysiwyg-toggle-' + params.field).show();
   }
@@ -125,6 +131,7 @@ Drupal.wysiwygEditorAttachToggleLink = f
       $('#wysiwyg-toggle-' + params.field).html(Drupal.settings.wysiwygEditor.enable).blur();
       // After disabling the editor, re-attach default behaviors.
       Drupal.wysiwyg.editor.attach.none(context, params);
+      Drupal.wysiwyg.invoke = Drupal.wysiwyg.editor.invoke.none;
     },
     function() {
       // Before enabling the editor, detach default behaviors.
Index: wysiwyg_editor.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/wysiwyg_editor.module,v
retrieving revision 1.1.2.29
diff -u -p -r1.1.2.29 wysiwyg_editor.module
--- wysiwyg_editor.module	11 Oct 2008 19:54:46 -0000	1.1.2.29
+++ wysiwyg_editor.module	11 Oct 2008 19:56:52 -0000
@@ -163,10 +163,10 @@ function wysiwyg_process_form(&$form) {
               // Check editor theme (and reset it if not/no longer available).
               $theme = wysiwyg_editor_get_themes($profile, $field['#wysiwyg_style']);
 
+              // Add plugin settings (first) for this input format.
+              wysiwyg_editor_add_plugin_settings($profile);
               // 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;
             }
@@ -362,6 +362,13 @@ function wysiwyg_editor_add_plugin_setti
     $plugins = array();
     $editor = wysiwyg_get_editor($profile->settings['editor']);
     $info = module_invoke_all('wysiwyg_plugin', $editor['name'], $editor['installed version']);
+    if (isset($editor['proxy plugin'])) {
+      $info += $editor['proxy plugin'];
+      $contrib_plugins = wysiwyg_get_all_plugins();
+      foreach ($contrib_plugins as $plugin_name => $meta) {
+        $info['drupal']['buttons'][$plugin_name] = $meta;
+      }
+    }
     // Only keep enabled plugins in this profile.
     foreach ($info as $plugin => $meta) {
       if (!isset($profile->settings['buttons'][$plugin])) {
@@ -436,9 +443,16 @@ function wysiwyg_editor_get_plugins($edi
       $plugins = $editor['plugin callback']($editor);
     }
     // Load our own plugins.
-    include_once drupal_get_path('module', 'wysiwyg_editor') .'/wysiwyg_editor.plugins.inc';
+    #include_once drupal_get_path('module', 'wysiwyg_editor') .'/wysiwyg_editor.plugins.inc';
   
-    $plugins = array_merge($plugins, module_invoke_all('wysiwyg_plugin', $editor['name'], $editor['installed version']));
+    #$plugins = array_merge($plugins, module_invoke_all('wysiwyg_plugin', $editor['name'], $editor['installed version']));
+    if (isset($editor['proxy plugin'])) {
+      $plugins += $editor['proxy plugin'];
+      $contrib_plugins = wysiwyg_get_all_plugins();
+      foreach ($contrib_plugins as $plugin_name => $info) {
+        $plugins['drupal']['buttons'][$plugin_name] = $info['title'];
+      }
+    }
   }
   return $plugins;
 }
@@ -640,6 +654,42 @@ 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 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,
+    );
+    // 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: editors/tinymce.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/editors/tinymce.inc,v
retrieving revision 1.1.2.4
diff -u -p -r1.1.2.4 tinymce.inc
--- editors/tinymce.inc	11 Oct 2008 22:48:13 -0000	1.1.2.4
+++ editors/tinymce.inc	12 Oct 2008 02:59:54 -0000
@@ -38,6 +38,12 @@ function wysiwyg_tinymce_editor() {
         // 'include files' => array('tinymce-2.inc'),
         'js files' => array('tinymce-2.js'),
         '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'),
@@ -57,6 +63,12 @@ function wysiwyg_tinymce_editor() {
             'files' => array('tiny_mce_src.js'),
           ),
         ),
+        'proxy plugin' => array(
+          'drupal' => array(
+            'load' => TRUE,
+            'internal' => TRUE,
+          ),
+        ),
       ),
     ),
     // Optional properties
@@ -172,7 +184,12 @@ function wysiwyg_tinymce_settings($edito
           }
           // Add internal buttons that also need to be loaded as extension.
           else if ($type == 'buttons' && isset($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') {
@@ -266,7 +283,20 @@ function wysiwyg_tinymce_themes($editor,
 function wysiwyg_tinymce_plugin_settings($editor, $profile, $info) {
   $plugins = array();
   foreach ($info as $name => $plugin) {
-    if (!isset($plugin['internal'])) {
+    // New API plugins.
+    if ($name == 'drupal') {
+      foreach ($plugin['buttons'] as $plugin_name => $meta) {
+        drupal_add_js($meta['js path'] .'/'. $meta['js file']);
+        $plugins[$plugin_name] = $meta['dialog settings'] + array(
+          'title' => $meta['title'],
+          'path' => base_path() . $meta['js path'],
+          'dialogPath' => base_path() . $meta['dialog path'],
+          'icon' => base_path() . $meta['icon path'] .'/'. $meta['icon file'],
+          'css' => base_path() . $meta['css path'] .'/'. $meta['css file'],
+        );
+      }
+    }
+    else if (!isset($plugin['internal'])) {
       $plugins[$name] = base_path() . $plugin['path'];
     }
   }
Index: editors/js/none.js
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/editors/js/none.js,v
retrieving revision 1.1.2.3
diff -u -p -r1.1.2.3 none.js
--- editors/js/none.js	11 Oct 2008 19:54:46 -0000	1.1.2.3
+++ editors/js/none.js	12 Oct 2008 03:12:46 -0000
@@ -42,3 +42,30 @@ Drupal.wysiwyg.editor.detach.none = func
   }
 };
 
+/**
+ * Simple invoke methods for plain text areas.
+ */
+Drupal.wysiwyg.editor.invoke.none = {
+  insert: function(instanceId, content) {
+    var editor = document.getElementById(instanceId);
+
+    // 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.1.2.4
diff -u -p -r1.1.2.4 tinymce-2.js
--- editors/js/tinymce-2.js	11 Oct 2008 19:54:46 -0000	1.1.2.4
+++ editors/js/tinymce-2.js	12 Oct 2008 02:33:07 -0000
@@ -25,7 +25,12 @@ Drupal.wysiwyg.editor.init.tinymce = fun
   }
   // @todo Move into global library settings.
   for (var plugin in Drupal.settings.wysiwygEditor.plugins.tinymce) {
-    tinyMCE.loadPlugin(plugin, Drupal.settings.wysiwygEditor.plugins.tinymce[plugin]);
+    if (typeof Drupal.settings.wysiwygEditor.plugins.tinymce[plugin] == 'string') {
+      tinyMCE.loadPlugin(plugin, Drupal.settings.wysiwygEditor.plugins.tinymce[plugin]);
+    }
+    else if (plugin != 'drupal') {
+      Drupal.wysiwyg.editor.invoke.tinymce.addPlugin(plugin);
+    }
   }
 };
 
@@ -61,3 +66,100 @@ Drupal.wysiwyg.editor.detach.tinymce = f
 //  }
 };
 
+Drupal.wysiwyg.editor.invoke.tinymce = {
+  addPlugin: function(plugin) {
+    // Load plugin specific language pack.
+    // @todo
+    // tinyMCE.importPluginLanguagePack(plugin, 'en');
+
+    var settings = Drupal.settings.wysiwygEditor.plugins.tinymce[plugin];
+
+    // Register plugin.
+    tinyMCE.addPlugin(plugin, {
+
+      // Register the ImageAssist execCommand.
+      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);
+              Drupal.wysiwyg.plugins[plugin].invoke(ed.getFocusElement(), settings, editor_id);
+              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.title, 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, settings, tinyMCE.selectedInstance.editorId);
+            }
+            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, settings, 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() {
+  },
+  closeDialog: function() {
+    tinyMCEPopup.close();
+  },
+  insert: function(instanceId, content) {
+    var editor = tinyMCE.getInstanceById(instanceId);
+    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.1.2.5
diff -u -p -r1.1.2.5 tinymce-3.js
--- editors/js/tinymce-3.js	11 Oct 2008 19:54:46 -0000	1.1.2.5
+++ editors/js/tinymce-3.js	12 Oct 2008 00:38:34 -0000
@@ -1,7 +1,5 @@
 // $Id: tinymce-3.js,v 1.1.2.5 2008/10/11 19:54:46 sun Exp $
 
-Drupal.wysiwyg = Drupal.wysiwyg || { 'init': {}, 'attach': {}, 'detach': {} };
-
 /**
  * Initialize editor instances.
  *
@@ -12,7 +10,7 @@ Drupal.wysiwyg = Drupal.wysiwyg || { 'in
  * @param editorSettings
  *   An object containing editor settings for each enabled editor theme.
  */
-Drupal.wysiwyg.init.tinymce = function(editorSettings) {
+Drupal.wysiwyg.editor.init.tinymce = function(editorSettings) {
   // If JS compression is enabled, TinyMCE is unable to find its own base path
   // and exec mode, hence we need to define it manually.
   // @todo Move global library settings somewhere else.
@@ -27,7 +25,12 @@ Drupal.wysiwyg.init.tinymce = function(e
   }
   // @todo Move into global library settings.
   for (var plugin in Drupal.settings.wysiwygEditor.plugins.tinymce) {
-    tinymce.PluginManager.load(plugin, Drupal.settings.wysiwygEditor.plugins.tinymce[plugin] + '/editor_plugin.js');
+    if (typeof Drupal.settings.wysiwygEditor.plugins.tinymce[plugin] == 'string') {
+      tinymce.PluginManager.load(plugin, Drupal.settings.wysiwygEditor.plugins.tinymce[plugin]);
+    }
+    else if (plugin != 'drupal') {
+      Drupal.wysiwyg.editor.invoke.tinymce.addPlugin(plugin);
+    }
   }
 };
 
@@ -66,3 +69,98 @@ Drupal.wysiwyg.editor.detach.tinymce = f
   }
 };
 
+Drupal.wysiwyg.editor.invoke.tinymce = {
+  addPlugin: function(plugin) {
+    // Load plugin specific language pack.
+    // @todo
+    //tinymce.PluginManager.requireLangPack(plugin);
+
+    var settings = Drupal.settings.wysiwygEditor.plugins.tinymce[plugin];
+
+    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 the ImageAssist execCommand.
+        ed.addCommand(plugin, function() {
+          if (typeof Drupal.wysiwyg.plugins[plugin].invoke == 'function') {
+            Drupal.wysiwyg.plugins[plugin].invoke(ed.selection.getNode(), settings, 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.title,
+          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, settings, ed.id);
+          }
+        });
+
+        // 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, settings, 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() {
+  },
+  closeDialog: function() {
+    tinyMCEPopup.close();
+  },
+  insert: function(instanceId, content) {
+    var editor = tinyMCE.get(instanceId);
+    editor.execCommand('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	10 Oct 2008 17:42:05 -0000
@@ -0,0 +1,20 @@
+<?php
+// $Id: wysiwyg_editor.plugins.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') . ' NEW!',
+    'vendor url' => 'http://drupal.org/project/wysiwyg',
+    'icon file' => 'break.gif',
+    // TEMP 10/10/2008 sun
+    'buttons' => array('break' => t('Teaser break') . ' NEW!'),
+    'internal' => 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	11 Oct 2008 22:56:28 -0000
@@ -0,0 +1,36 @@
+// $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') && $(node).attr('class') == 'wysiwyg-break');
+  },
+
+  // Execute the button.
+  invoke: function(node, settings, instanceId) {
+    // Prevent duplicating a teaser break.
+    if ($(node).is('img') && $(node).attr('class') != 'wysiwyg-break') {
+      return;
+    }
+    var content = this._getPlaceholder(settings);
+    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>');
+    $('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/images/spacer.gif
===================================================================
RCS file: plugins/break/images/spacer.gif
diff -N plugins/break/images/spacer.gif
Binary files /dev/null and spacer.gif differ
