Index: editors/jwysiwyg.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/editors/jwysiwyg.inc,v retrieving revision 1.3 diff -u -p -r1.3 jwysiwyg.inc --- editors/jwysiwyg.inc 18 May 2009 01:08:13 -0000 1.3 +++ editors/jwysiwyg.inc 7 Jun 2009 01:06:46 -0000 @@ -22,6 +22,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( @@ -54,3 +64,142 @@ function wysiwyg_jwysiwyg_version($edito } } +/** + * 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 7 Jun 2009 01:06:46 -0000 @@ -1,11 +1,129 @@ // $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 = ''); +} /** * Attach this editor to a target element. */ Drupal.wysiwyg.editor.attach.jwysiwyg = function(context, params, settings) { + var $field = $('#' + params.field); + var includeInHeader = []; + + var buttonSettings = {}; + // 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. + 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) { + includeInHeader.push(''); + } + 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); // Pass parameters into closure. + } + } + delete settings.buttons; + settings.controls = buttonSettings; + + if (settings.content_css) { + // Join the array of stylesheet urls to link tags and push them to header. + includeInHeader.push(''); + } + delete settings.content_css; + + // jWWYSIWYG only supports a single css file, so we'll change the template. + var htmlTemplate = '<'+'?xml version="1.0" encoding="UTF-8"?'+'>' + + '' + + '' + + '' + + includeInHeader.join('') + + 'STYLE_SHEETINITIAL_CONTENT'; + 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; + + 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') { + $('.' + plugin, this.panel).removeClass('active'); + if (pluginInstances[plugin].isNode(element)) { + foundTarget = true; + $('.' + plugin, this.panel).addClass('active'); + break; + } + } + // Stop the native image button from activating on placeholder images etc. + if (!foundTarget) { + oldCheckTargets.call(this, element); + } + }; + /** + * 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 +140,52 @@ Drupal.wysiwyg.editor.detach.jwysiwyg = $field.show(); }; +/** + * Inserts content at the caret position + */ +Drupal.wysiwyg.editor.instance.jwysiwyg = { + insert : function(content) { + var $field = $('#'+this.field); + var editor = $field.data('wysiwyg'); + var editorWindow = editor.editor[0].contentWindow; + // Could not find any jWYSIWYG API "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); + } + }, + openDialog : function() { + alert("Dialogs are not yet supported."); + }, + 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); + } + else { // Safari! + var range = document.createRange(); + range.setStart(selection.anchorNode,selection.anchorOffset); + range.setEnd(selection.focusNode,selection.focusOffset); + return range; + } + } +}; +