Index: editors/jwysiwyg.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/editors/jwysiwyg.inc,v
retrieving revision 1.4
diff -u -p -r1.4 jwysiwyg.inc
--- editors/jwysiwyg.inc	9 Jun 2009 00:18:11 -0000	1.4
+++ editors/jwysiwyg.inc	18 Jun 2009 23:53:49 -0000
@@ -25,6 +25,16 @@ function wysiwyg_jwysiwyg_editor() {
       ),
     ),
     'version callback' => 'wysiwyg_jwysiwyg_version',
+    'settings callback' => 'wysiwyg_jwysiwyg_settings',
+    'plugin callback' => 'wysiwyg_jwysiwyg_plugins',
+    'plugin settings callback' => 'wysiwyg_jwysiwyg_plugin_settings',
+    'proxy plugin' => array(
+      'drupal' => array(
+        'load' => TRUE,
+        'proxy' => TRUE,
+      ),
+    ),
+    'proxy plugin settings callback' => 'wysiwyg_jwysiwyg_proxy_plugin_settings',
     // @todo Wrong property; add separate properties for editor requisites.
     'css path' => wysiwyg_get_path('jwysiwyg'),
     'versions' => array(
@@ -58,3 +68,142 @@ function wysiwyg_jwysiwyg_version($edito
   fclose($script);
 }
 
+/**
+ * Return runtime editor settings for a given wysiwyg profile.
+ *
+ * @param $editor
+ *   A processed hook_editor() array of editor properties.
+ * @param $config
+ *   An array containing wysiwyg editor profile settings.
+ * @param $theme
+ *   The name of a theme/GUI/skin to use.
+ *
+ * @return
+ *   A settings array to be populated in
+ *   Drupal.settings.wysiwyg.configs.{editor}
+ */
+function wysiwyg_jwysiwyg_settings($editor, $config, $theme) {
+  // Default settings, present so they can be overridden by plugins,
+  // except for the template and button controls which are a bit too complex.
+  $settings = array(
+    'debug' => FALSE,
+    'rmUnwantedBr' => TRUE,
+    'brIE' => TRUE,
+    'messages' => array(
+      'nonSelection' => FAlSE,
+    ),
+  );
+  if (!empty($config['buttons'])) {
+    // Disable all default buttons since we have a custom config.
+    $button_statuses = array(
+      'bold' => FALSE, 'italic' => FALSE,
+      'createLink' => FALSE, 'insertImage' => FALSE,
+      'increaseFontSize' => FALSE, 'decreaseFontSize' => FALSE,
+      'h1' => FALSE, 'h2' => FALSE, 'h3' => FALSE,
+      'h1mozilla' => FALSE, 'h2mozilla' => FALSE, 'h3mozilla' => FALSE,
+      'separator00' => FALSE, 'separator01' => FALSE, 'separator02' => FALSE, 'separator03' => FALSE,
+      'separator04' => FALSE, 'separator05' => FALSE, 'separator06' => FALSE, 'separator07' => FALSE,
+      'separator08' => FALSE, 'separator09' => FALSE,
+      'removeFormat' => FALSE,
+    );
+    $plugins = wysiwyg_get_plugins($editor['name']);
+    foreach ($config['buttons'] as $plugin => $buttons) {
+      foreach ($buttons as $button => $enabled) {
+        // Iterate separately over buttons and extensions properties.
+        foreach (array('buttons', 'extensions') as $type) {
+          // Skip unavailable plugins.
+          if (!isset($plugins[$plugin][$type][$button])) {
+            continue;
+          }
+          // Add button.
+          if ($type == 'buttons') {
+            $button_statuses[$button] = TRUE;
+          }
+          // Allow plugins to add or override global configuration settings.
+          if (!empty($plugins[$plugin]['options'])) {
+            $settings = array_merge($settings, $plugins[$plugin]['options']);
+          }
+        }
+      }
+    }
+    $settings['buttons'] = $button_statuses;
+  }
+  // Add editor content stylesheet.
+  if (isset($config['css_setting'])) {
+    if ($config['css_setting'] == 'theme') {
+      $settings['content_css'] = wysiwyg_get_css();
+    }
+    else if ($config['css_setting'] == 'self' && isset($config['css_path'])) {
+      $settings['content_css'] = strtr($config['css_path'], array('%b' => base_path(), '%t' => path_to_theme()));
+    }
+  }
+  return $settings;
+}
+
+/**
+ * Build a JS settings array of native external plugins that need to be loaded separately.
+ *
+ * @todo Required for jWYSIWYG?  If not, wysiwyg_add_plugin_settings() needs
+ *   an overhaul.
+ */
+function wysiwyg_jwysiwyg_plugin_settings($editor, $profile, $plugins) {
+  $settings = array();
+  return $settings;
+}
+
+/**
+ * Build a JS settings array for Drupal plugins loaded via the proxy plugin.
+ */
+
+function wysiwyg_jwysiwyg_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 Whizzywig; semi-implementation of hook_wysiwyg_plugin().
+ */
+function wysiwyg_jwysiwyg_plugins($editor) {
+  return array(
+    'default' => array(
+      'buttons' => array(
+        'bold' => t('Bold'), 'italic' => t('Italic'), 'underline' => t('Underline'), 'strikeThrough' => t('Strikethrough'),
+//      'separator00' => t('Separator 1'),
+        'justifyLeft' => t('Align left'), 'justifyCenter' => t('Align center'), 'justifyRight' => t('Align right'), 'justifyFull' => t('Justify'),
+//      'separator01' => t('Separator 2'),
+        'indent' => t('Indent'), 'outdent' => t('Outdent'),
+//      'separator02' => t('Separator 3'),
+        'subscript' => t('Subscript'), 'superscript' => t('Superscript'),
+//      'separator03' => t('Separator 4'),
+        'undo' => t('Undo'), 'redo' => t('Redo'),
+//      'separator04' => t('Separator 5'),
+        'insertUnorderedList' => t('Bullet list'), 'insertOrderedList' => t('Numbered list'),
+        'insertHorizontalRule' => t('Horizontal rule'),
+//      'separator05' => t('Separator 6'),
+        'createLink' => t('Link'),
+        'insertImage' => t('Image'),
+//      'separator06' => t('Separator 7'),
+        'h1' => t('Header 1'), 'h2' => t('Header 2'), 'h3' => t('Header 3'),
+        'h1mozilla' => t('Header 1 Mozilla'), 'h2mozilla' => t('Header 2 Mozilla'), 'h3mozilla' => t('Header 3 Mozilla'),
+//      'separator07' => t('Separator 8'),
+        'cut' => t('Cut'), 'copy' => t('Copy'), 'paste' => t('Paste'),
+//      'separator08' => t('Separator 9'),
+        'increaseFontSize' => t('Increase font size'), 'decreaseFontSize' => t('Decrease font size'),
+//      'separator09' => t('Separator 10'),
+        'html' => t('Source code'),
+        'removeFormat' => t('Remove formatting'),
+      ),
+      'internal' => TRUE,
+    ),
+  );
+}
Index: editors/js/jwysiwyg.js
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/editors/js/jwysiwyg.js,v
retrieving revision 1.3
diff -u -p -r1.3 jwysiwyg.js
--- editors/js/jwysiwyg.js	1 Dec 2008 14:14:41 -0000	1.3
+++ editors/js/jwysiwyg.js	18 Jun 2009 23:53:49 -0000
@@ -1,11 +1,137 @@
 // $Id: jwysiwyg.js,v 1.3 2008/12/01 14:14:41 sun Exp $
 
 /**
+ * Initialize editor instances.
+ *
+ * @param editorSettings
+ *   An object containing editor settings for each input format.
+ */
+Drupal.wysiwyg.editor.init.jwysiwyg = function(editorSettings) {
+  // Add Drupal plugin button styles to the document header.
+  var editorStyle = '';
+  var checkedPlugins = [];
+  for (var format in editorSettings) {
+    for (var buttonName in editorSettings[format].buttons) {
+      if (Drupal.settings.wysiwyg.plugins.drupal[buttonName] && !checkedPlugins[buttonName]) {
+        editorStyle += "div.wysiwyg ul.panel li a." + buttonName + " { background: url('" + Drupal.settings.wysiwyg.plugins[format].drupal[buttonName].icon + "') no-repeat 0px 0px; }\n";
+        checkedPlugins[buttonName] = true;
+      }
+    }
+  }
+  $('head').append('<style type="text/css">' + editorStyle + '</style>');
+};
+
+/**
  * Attach this editor to a target element.
  */
 Drupal.wysiwyg.editor.attach.jwysiwyg = function(context, params, settings) {
+  var $field = $('#' + params.field);
+  var css = [];
+
+  // Setup editor buttons for Drupal plugins.
+  // @todo: Make sure buttons are always named same as the Drupal plugin.
+  // @todo: Update if Drupal plugins can provide multiple buttons.
+  var buttonSettings = {};
+  for (var buttonName in settings.buttons) {
+    buttonSettings[buttonName] = {
+      visible: settings.buttons[buttonName]
+    }
+    if (Drupal.settings.wysiwyg.plugins.drupal[buttonName]) {
+      // This is a Drupal plugin and needs a custom function.
+      var pluginSettings = Drupal.settings.wysiwyg.plugins[params.format].drupal[buttonName];
+      if (pluginSettings.css) {
+        css.push(pluginSettings.css);
+      }
+      buttonSettings[buttonName].exec = function(fieldName, buttonName) {
+        // Use a closure to store the value of fieldName and buttonName.
+        return function() {
+          var $field = $('#' + fieldName);
+          if (typeof Drupal.wysiwyg.plugins[buttonName].invoke == 'function') {
+            var element = $field.data('wysiwyg').editor[0];
+            var selectedRange = Drupal.wysiwyg.editor.instance.jwysiwyg.getSelectedRange(element.contentWindow);
+            var selectedNode = (selectedRange.commonAncestorContainer ? selectedRange.commonAncestorContainer : selectedRange.parentElement());
+            var data = { format: 'html', node: selectedNode, content : $field.data('wysiwyg').getContent() };
+            Drupal.wysiwyg.plugins[buttonName].invoke(data, Drupal.settings.wysiwyg.plugins.drupal[buttonName], params.field);
+          }
+        };
+      }(params.field, buttonName);
+    }
+  }
+  delete settings.buttons;
+  settings.controls = buttonSettings;
+
+  if (settings.content_css) { 
+    css.push(settings.content_css);
+  }
+  delete settings.content_css;
+ 
+  // jWysiwyg only supports a single css file, so we'll change the template.
+  var htmlTemplate = '<' + '?xml version="1.0" encoding="UTF-8"?' + '>'
+  + '<!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="en">'
+  + '<head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">'
+  + '<link rel="stylesheet" type="text/css" href="' + css.join('" /><link rel="stylesheet" type="text/css" href="') + '" />'
+  + 'STYLE_SHEET</head><body>INITIAL_CONTENT</body></html>';
+  settings.html = htmlTemplate;
+
   // Attach editor.
-  $('#' + params.field).wysiwyg();
+  $field.wysiwyg(settings);
+
+  var editor = $field.data('wysiwyg');
+
+  // Inject proxy methods to handle plugins attaching/detaching plugins and
+  // activating their buttons.
+  var pluginSettings = Drupal.settings.wysiwyg.plugins;
+  var pluginInstances = Drupal.wysiwyg.plugins;
+
+  // Use the jWysiwyg editor area document as key for the field name.
+  $(editor.editorDoc).data('wysiwyg_field', params.field);
+  $(editor.editorDoc).focus(function (){
+    Drupal.wysiwyg.activeId = $(this).data('wysiwyg_field');
+  });
+
+  var oldGetContent = editor.getContent;
+  editor.getContent = function() {
+    var content = oldGetContent.call(this);
+    for (var plugin in pluginSettings[params.format].drupal) if (typeof pluginInstances[plugin].detach == 'function') {
+      content = pluginInstances[plugin].detach(content, pluginSettings[plugin], params.field);
+    }
+    return content;
+  };
+
+  var oldSetContent = editor.setContent;
+  editor.setContent = function(newContent) {
+    for (var plugin in Drupal.settings.wysiwyg.plugins[params.format].drupal) if (typeof pluginInstances[plugin].attach == 'function') {
+      newContent = pluginInstances[plugin].attach(newContent, pluginSettings.drupal[plugin], params.field);
+    }
+    oldSetContent.call(this, newContent);
+  };
+
+  var oldCheckTargets = editor.checkTargets;
+  editor.checkTargets = function(element) {
+    var foundTarget = false;
+    for (var plugin in pluginSettings[params.format].drupal) {
+      if (typeof pluginInstances[plugin].isNode == 'function') {
+        if (pluginInstances[plugin].isNode(element)) {
+          foundTarget = true;
+          break;
+        }
+      }
+    }
+
+    if (!foundTarget) {
+      oldCheckTargets.call(this, element);
+    }
+    else {
+      // Deactivate all buttons by passing the body element.
+      oldCheckTargets.call(this, element.ownerDocument.body);
+      // Activate the Drupal plugin button.
+      $('.' + plugin, this.panel).addClass('active');
+    }
+  };
+  // Because we could not proxy the setContent method before it was defined
+  // we need to run it again to allow Drupal plugins to attach themselves.
+  editor.setContent(editor.getContent());
 };
 
 /**
@@ -22,3 +148,59 @@ Drupal.wysiwyg.editor.detach.jwysiwyg = 
   $field.show();
 };
 
+Drupal.wysiwyg.editor.instance.jwysiwyg = {
+  openDialog: function(dialog, params) {
+    alert("Drupal plugin dialogs are not yet supported by jWysiwyg.");
+  },
+
+  closeDialog: function(dialog) {
+  },
+
+  getSelection: function(targetWindow) {
+    return targetWindow.getSelection ? targetWindow.getSelection() : targetWindow.document.selection.createRange();
+  },
+
+  getSelectedRange: function(targetWindow) {
+    var selection = this.getSelection(targetWindow);
+    if (selection.getRangeAt) {
+      return selection.getRangeAt(0);
+    }
+    // Safari.
+    else {
+      var range = document.createRange();
+      range.setStart(selection.anchorNode,selection.anchorOffset);
+      range.setEnd(selection.focusNode,selection.focusOffset);
+      return range;
+    }
+  },
+
+  /**
+   * Insert content at the current caret position.
+   */
+  insert: function(content) {
+    var $field = $('#' + this.field);
+    var editor = $field.data('wysiwyg');
+    var editorWindow = editor.editor[0].contentWindow;
+
+    // jWysiwyg does not provide an insert method.
+    // Using simplified code from QuirksMode.org
+    var selectionRange = this.getSelectedRange(editorWindow);
+    if (selectionRange.insertNode) {
+      // Delete and insert new node.
+      selectionRange.deleteContents();
+      selectionRange.insertNode(selectionRange.createContextualFragment(content));
+    }
+    else if (editor.selectionStart || editor.selectionStart == '0') {
+      if (selectionRange.item) {
+        // Delete content and get caret text selection.
+        editor.editorDoc.execCommand('Delete', false, null);
+        selectionRange = this.getSelectedRange(editorWindow);
+      }
+      selectionRange.pasteHTML(content);
+    }
+    else {
+      editor.setContent(editor.getContent() + content);
+    }
+  }
+};
+
