? LICENSE.txt
? wip_fckeditor_patch_api.patch
? wysiwyg-DRUPAL-6--1.fckeditor.toolbar.patch
? wysiwyg-HEAD.fckeditor-drupal-plugins-2.patch
? wysiwyg-HEAD.fckeditor-drupal-plugins-3.patch
? wysiwyg-HEAD.plugin-loading-part-II.patch
? wysiwyg-HEAD.plugin-loading.patch
Index: editors/fckeditor.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/editors/fckeditor.inc,v
retrieving revision 1.11
diff -u -p -r1.11 fckeditor.inc
--- editors/fckeditor.inc	13 Feb 2009 02:22:19 -0000	1.11
+++ editors/fckeditor.inc	7 Mar 2009 14:28:10 -0000
@@ -27,6 +27,14 @@ function wysiwyg_fckeditor_editor() {
     'themes callback' => 'wysiwyg_fckeditor_themes',
     'settings callback' => 'wysiwyg_fckeditor_settings',
     'plugin callback' => 'wysiwyg_fckeditor_plugins',
+    'plugin settings callback' => 'wysiwyg_fckeditor_plugin_settings',
+    'proxy plugin' => array(
+      'drupal' => array(
+        'load' => TRUE,
+        'proxy' => TRUE,
+      ),
+    ),
+    'proxy plugin settings callback' => 'wysiwyg_fckeditor_proxy_plugin_settings',
     'versions' => array(
       2.6 => array(
         'js files' => array('fckeditor-2.6.js'),
@@ -157,6 +165,35 @@ function wysiwyg_fckeditor_themes($edito
 }
 
 /**
+ * Build a JS settings array of native external plugins that need to be loaded separately.
+ *
+ * @todo Required for FCKeditor?  If not, wysiwyg_add_plugin_settings() needs
+ *   an overhaul.
+ */
+function wysiwyg_fckeditor_plugin_settings($editor, $profile, $plugins) {
+  $settings = array();
+  return $settings;
+}
+
+/**
+ * Build a JS settings array for Drupal plugins loaded via the proxy plugin.
+ */
+function wysiwyg_fckeditor_proxy_plugin_settings($editor, $profile, $plugins) {
+  $settings = array();
+  foreach ($plugins as $name => $plugin) {
+    // Populate required plugin settings.
+    $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;
+}
+
+/**
  * Return internal plugins for FCKeditor; semi-implementation of hook_wysiwyg_plugin().
  */
 function wysiwyg_fckeditor_plugins($editor) {
Index: editors/js/fckeditor-2.6.js
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/editors/js/fckeditor-2.6.js,v
retrieving revision 1.13
diff -u -p -r1.13 fckeditor-2.6.js
--- editors/js/fckeditor-2.6.js	5 Feb 2009 01:34:35 -0000	1.13
+++ editors/js/fckeditor-2.6.js	7 Mar 2009 14:28:10 -0000
@@ -12,12 +12,14 @@ Drupal.wysiwyg.editor.attach.fckeditor =
     FCKinstance.ToolbarSet = settings.ToolbarSet;
   }
 
-  // Apply input format configuration.
-  FCKinstance.Config.format = params.format;
-  delete settings.buttons;
-  for (var setting in settings) {
-    FCKinstance.Config[setting] = settings[setting];
+  // Point to custom settings file, loaded during editor init.
+  if(settings.CustomConfigurationsPath) {
+   FCKinstance.Config.CustomConfigurationsPath = settings.CustomConfigurationsPath;
   }
+ 
+  // Load Drupal plugins and apply format specific settings.
+  // @see fckeditor.config.js
+
   // Attach editor.
   FCKinstance.ReplaceTextarea();
 };
@@ -26,22 +28,179 @@ Drupal.wysiwyg.editor.attach.fckeditor =
  * Detach a single or all editors.
  */
 Drupal.wysiwyg.editor.detach.fckeditor = function(context, params) {
+  var instances = [];
   if (typeof params != 'undefined' && typeof FCKeditorAPI != 'undefined') {
     var instance = FCKeditorAPI.GetInstance(params.field);
     if (instance) {
-      $('#' + params.field).val(instance.GetXHTML()).show();
-      $('#' + params.field + '___Config').remove();
-      $('#' + params.field + '___Frame').remove();
-      delete FCKeditorAPI.__Instances[params.field];
+      instances[params.field] = instance;
     }
   }
   else {
-    for (var instance in FCKeditorAPI.__Instances) {
-      $('#' + instance).val(instance.GetXHTML()).show();
-      $('#' + instance + '___Config').remove();
-      $('#' + instance + '___Frame').remove();
-      delete FCKeditorAPI.__Instances[instance];
+    instances = FCKeditorAPI.__Instances;
+  } 
+
+  for (var instanceName in instances) {
+    // Shut down the instance and give plugins a chance to detatch by changing status first.
+    instances[instanceName].SetStatus(FCK_STATUS_NOTLOADED);
+    instances[instanceName].UpdateLinkedField();
+    $('#' + instanceName).show();
+    $('#' + instanceName + '___Config').remove();
+    $('#' + instanceName + '___Frame').remove();
+    delete FCKeditorAPI.__Instances[instanceName];
+  }
+};
+
+Drupal.wysiwyg.editor.instance.fckeditor = {
+  addPlugin: function(plugin, settings, pluginSettings, instance) {
+    if (typeof Drupal.wysiwyg.plugins[plugin] != 'object') {
+      return;
     }
+    // DEBUG.
+    console.log(instance);
+    this.ed = instance;
+
+    if (Drupal.settings.wysiwyg.plugins[instance.wysiwygInputFormat].drupal[plugin].css) {
+      instance.FCKConfig.EditorAreaCSS += ',' + Drupal.settings.wysiwyg.plugins[instance.wysiwygInputFormat].drupal[plugin].css;
+    }
+
+    // Register event listerners for attaching/detaching the plugin
+    instance.FCK.Events.AttachEvent('OnStatusChange', function(editorInstance, newStatus) {
+      switch (newStatus) {
+        case FCK_STATUS_COMPLETE:
+          if (typeof Drupal.wysiwyg.plugins[plugin].attach == 'function') {
+            var contentXHTML = editorInstance.GetXHTML();
+            var oldContent = contentXHTML;
+            contentXHTML = Drupal.wysiwyg.plugins[plugin].attach(contentXHTML, pluginSettings, editorInstance.Name);
+            contentXHTML = Drupal.wysiwyg.editor.instance.fckeditor.prepareContent(contentXHTML)
+            /*
+             * Should use SetHTML so FCKeditor can preserve comments, scripts etc
+             * But doing so rapidly leaves the original contents unaltered, except for when stepping
+             * through it using a debugger. Timing issue because of SetHTML rebuilding the iframe?
+             * SetHTML also causes the content to update/flicker once for each plugin.
+             * Checking if anything was actually modified to minimize this for now.
+             * It is also possible to attach the plugins at the same time they are added, and modify
+             * instance.FCK.LinkedField.value instead. That eliminates the flickering but might cause other problems
+             * if the plugin expects the editor to be completely loaded.
+             */
+             if(contentXHTML != oldContent) {
+              editorInstance.EditorDocument.body.innerHTML=contentXHTML;
+            }
+          }
+          break;
+        case FCK_STATUS_NOTLOADED:
+          // Nothing in FCKeditor triggers this after it has been loaded, so we know what to do
+          if (typeof Drupal.wysiwyg.plugins[plugin].detach == 'function') {
+            var contentXHTML = editorInstance.GetXHTML();
+            var oldContent = contentXHTML;
+            contentXHTML = Drupal.wysiwyg.plugins[plugin].detach(contentXHTML, pluginSettings, editorInstance.Name);
+            /*
+             * Should use SetHTML here too. Using .innerHTML here *should* be fine since everything will
+             * be moved to the textare just after this.
+             */
+            if (contentXHTML != oldContent) {
+              editorInstance.EditorDocument.body.innerHTML=contentXHTML;
+            }
+          }
+          break;
+      }
+    });
+
+    // @see fckcommands.js, fck_othercommands.js, fckpastewordcommand.js
+    instance.FCKCommands.RegisterCommand(plugin, {
+      // Invoke the plugin's button.
+      Execute: function () {
+        if (typeof Drupal.wysiwyg.plugins[plugin].invoke == 'function') {
+          var data = { format: 'html', node: instance.FCKSelection.GetParentElement() };
+          // @todo This is NOT the same as data.node.
+          data.content = data.node.innerHTML;
+          Drupal.wysiwyg.plugins[plugin].invoke(data, pluginSettings, instance.FCK.Name);
+        }
+      },
+
+      // isNode: Return whether the plugin button should be enabled for the
+      // current selection.
+      // @see FCKUnlinkCommand.prototype.GetState()
+      GetState: function () {
+        // Disabled if not in WYSIWYG mode.
+        if (instance.FCK.EditMode != FCK_EDITMODE_WYSIWYG) {
+          return FCK_TRISTATE_DISABLED;
+        }
+
+        var state = instance.FCK.GetNamedCommandState(this.Name);
+        if (state == FCK_TRISTATE_OFF && instance.FCK.EditMode == FCK_EDITMODE_WYSIWYG) {
+          if (typeof Drupal.wysiwyg.plugins[plugin].isNode == 'function') {
+            var node = instance.FCKSelection.GetSelectedElement();
+            state = Drupal.wysiwyg.plugins[plugin].isNode(node) ? FCK_TRISTATE_ON : FCK_TRISTATE_OFF;
+          }
+        }
+        return state;
+      },
+
+      /**
+       * Return information about the plugin as a name/value array.
+       */
+      Name: plugin
+    });
+
+    // Register the plugin button.
+    // var FCKToolbarButton = function(commandName, label, tooltip, style, sourceView, contextSensitive, icon)
+    instance.FCKToolbarItems.RegisterItem(plugin, new instance.FCKToolbarButton(plugin, settings.iconTitle, settings.iconTitle, null, false, true, settings.icon));
+
+    return;
+
+    // @todo Functionality from TinyMCE that needs to be covered in here as well.
+    tinymce.create('tinymce.plugins.' + plugin, {
+      init: function(ed, url) {
+        // Load custom CSS for editor contents on startup. DONE
+        ed.onInit.add(function() {
+          if (settings.css) {
+            ed.dom.loadCSS(settings.css);
+          }
+        });
+
+        // Attach: Replace plain text with HTML representations. DONE, hackish
+        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. DONE, hackish
+        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. DONE
+        ed.onNodeChange.add(function(ed, command, node) {
+          if (typeof Drupal.wysiwyg.plugins[plugin].isNode == 'function') {
+            command.setActive(plugin, Drupal.wysiwyg.plugins[plugin].isNode(node));
+          }
+        });
+      },
+    });
+  },
+
+  openDialog: function(dialog, params) {
+    // @todo implement open dialog
+  },
+
+  closeDialog: function(dialog) {
+    // @todo implement close dialog
+  },
+
+  prepareContent: function(content) {
+    // @todo not needed for FCKeditor?
+    return content;
+  },
+
+  insert: function(content) {
+    var instance = FCKeditorAPI.GetInstance(this.field);
+    // @see FCK.InsertHtml(), FCK.InsertElement()
+    instance.InsertHtml(content);
   }
 };
 
Index: editors/js/fckeditor.config.js
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/editors/js/fckeditor.config.js,v
retrieving revision 1.2
diff -u -p -r1.2 fckeditor.config.js
--- editors/js/fckeditor.config.js	1 Feb 2009 05:58:10 -0000	1.2
+++ editors/js/fckeditor.config.js	7 Mar 2009 14:28:10 -0000
@@ -1,17 +1,44 @@
 // $Id: fckeditor.config.js,v 1.2 2009/02/01 05:58:10 sun Exp $
 
+/*
+ * This file is loaded from within FCKeditor's frame
+ */
+Drupal = window.parent.Drupal;
+
 /**
  * Fetch and provide original editor settings as local variable.
  *
  * FCKeditor does not support to pass complex variable types to the editor.
- *
- * For whatever reason, our custom 'format' property is not available in
- * FCKConfig.format, but in FCKConfig.PageConfig.format instead.
+ * 
+ * 
  */
-var wysiwygSettings = window.parent.Drupal.settings.wysiwyg.configs.fckeditor[FCKConfig.PageConfig.format];
+var wysiwygInputFormat = Drupal.wysiwyg.instances[Drupal.wysiwyg.activeId].format;
+var wysiwygSettings = Drupal.settings.wysiwyg.configs.fckeditor[wysiwygInputFormat];
 
 /**
  * Apply custom Wysiwyg API toolbar for input format.
  */
 FCKConfig.ToolbarSets['Wysiwyg'] = wysiwygSettings.buttons;
 
+/**
+ * Load format-specific settings into FCKConfig so plugins can append stylesheets etc.
+ */
+
+for (var setting in wysiwygSettings) {
+  if (setting == 'buttons') {
+    continue;
+  }
+  FCKConfig[setting] = wysiwygSettings[setting];
+}
+ 
+/**
+ * Register Drupal plugins for this input format.
+ * Parameters to addPlugin are:
+ * - Plugin name
+ * - Format specific plugin settings
+ * - General plugin settings
+ * - A reference to this window so the plugins can access FCKConfig etc 
+ */
+for (var plugin in Drupal.settings.wysiwyg.plugins[wysiwygInputFormat].drupal) {
+  Drupal.wysiwyg.editor.instance.fckeditor.addPlugin(plugin, Drupal.settings.wysiwyg.plugins[wysiwygInputFormat].drupal[plugin], Drupal.settings.wysiwyg.plugins.drupal[plugin], window);
+}
