Index: wysiwyg.init.js =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/wysiwyg.init.js,v retrieving revision 1.1 diff -u -p -r1.1 wysiwyg.init.js --- wysiwyg.init.js 11 Oct 2008 19:54:24 -0000 1.1 +++ wysiwyg.init.js 12 Oct 2008 02:58:19 -0000 @@ -1,8 +1,8 @@ // $Id: wysiwyg.init.js,v 1.1 2008/10/11 19:54:24 sun Exp $ -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.admin.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/wysiwyg_editor.admin.inc,v retrieving revision 1.18 diff -u -p -r1.18 wysiwyg_editor.admin.inc --- wysiwyg_editor.admin.inc 10 Oct 2008 21:57:09 -0000 1.18 +++ wysiwyg_editor.admin.inc 12 Oct 2008 17:56:43 -0000 @@ -180,14 +180,16 @@ function wysiwyg_editor_profile_form($fo foreach ($plugins as $name => $meta) { if (isset($meta['buttons']) && is_array($meta['buttons'])) { foreach ($meta['buttons'] as $button => $title) { - // @todo Button icon locations are different in editors, editor versions, - // and contrib/custom plugins (like Image Assist, f.e.). - $img_src = $meta['path'] ."/images/$name.gif"; - // Handle plugins that have more than one button. - if (!file_exists($img_src)) { - $img_src = $meta['path'] ."/images/$button.gif"; + if (!empty($meta['path'])) { + // @todo Button icon locations are different in editors, editor versions, + // and contrib/custom plugins (like Image Assist, f.e.). + $img_src = $meta['path'] ."/images/$name.gif"; + // Handle plugins that have more than one button. + if (!file_exists($img_src)) { + $img_src = $meta['path'] ."/images/$button.gif"; + } + $icon = file_exists($img_src) ? '' : ''; } - $icon = file_exists($img_src) ? '' : ''; $title = (isset($meta['url']) ? l($title, $meta['url'], array('target' => '_blank')) : $title); $title = (!empty($icon) ? $icon .' '. $title : $title); $form['buttons'][$name][$button] = array( Index: wysiwyg_editor.js =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/wysiwyg/wysiwyg_editor.js,v retrieving revision 1.9 diff -u -p -r1.9 wysiwyg_editor.js --- wysiwyg_editor.js 11 Oct 2008 19:54:24 -0000 1.9 +++ wysiwyg_editor.js 12 Oct 2008 17:42:00 -0000 @@ -81,6 +81,13 @@ 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. + Drupal.wysiwyg.invoke = {}; + 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 +132,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.29 diff -u -p -r1.29 wysiwyg_editor.module --- wysiwyg_editor.module 11 Oct 2008 19:54:24 -0000 1.29 +++ wysiwyg_editor.module 12 Oct 2008 02:58:19 -0000 @@ -160,10 +160,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.5 diff -u -p -r1.5 tinymce.inc --- editors/tinymce.inc 11 Oct 2008 22:48:00 -0000 1.5 +++ editors/tinymce.inc 12 Oct 2008 19:53:57 -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,21 @@ 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'], + 'iconTitle' => $meta['icon title'], + '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.2 diff -u -p -r1.2 none.js --- editors/js/none.js 11 Oct 2008 19:54:24 -0000 1.2 +++ editors/js/none.js 12 Oct 2008 03:12:53 -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.3 diff -u -p -r1.3 tinymce-2.js --- editors/js/tinymce-2.js 11 Oct 2008 19:54:24 -0000 1.3 +++ editors/js/tinymce-2.js 12 Oct 2008 21:35:35 -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,125 @@ Drupal.wysiwyg.editor.detach.tinymce = f // } }; +Drupal.wysiwyg.editor.invoke.tinymce = { + addPlugin: function(plugin) { + var settings = Drupal.settings.wysiwygEditor.plugins.tinymce[plugin]; + + // Register plugin. + 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, 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.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, settings, tinyMCE.selectedInstance.editorId); + content = Drupal.wysiwyg.editor.invoke.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, 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(); + }, + + 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 = $('
' + content + '
'); // 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(instanceId, content) { + content = this.prepareContent(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.4 diff -u -p -r1.4 tinymce-3.js --- editors/js/tinymce-3.js 11 Oct 2008 19:54:24 -0000 1.4 +++ editors/js/tinymce-3.js 12 Oct 2008 21:23:51 -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.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); + } } }; @@ -64,3 +69,123 @@ Drupal.wysiwyg.editor.detach.tinymce = f } }; +Drupal.wysiwyg.editor.invoke.tinymce = { + addPlugin: function(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 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, 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.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, settings, ed.id); + data.content = Drupal.wysiwyg.editor.invoke.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, 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(); + }, + + 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 = $('
' + content + '
'); // 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(instanceId, content) { + content = this.prepareContent(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 12 Oct 2008 19:53:14 -0000 @@ -0,0 +1,21 @@ + t('Teaser break') . ' NEW!', + 'vendor url' => 'http://drupal.org/project/wysiwyg', + 'icon file' => 'break.gif', + 'icon title' => t('Separate the teaser and body of this content'), + // 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 12 Oct 2008 21:12:28 -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(//)) { + return; + } + var content = ''; + } + if (typeof content != 'undefined') { + Drupal.wysiwyg.invoke.insert(instanceId, content); + } + }, + + // Replace all tags with images. + attach: function(content, settings, instanceId) { + content = content.replace(//g, this._getPlaceholder(settings)); + return content; + }, + + // Replace images with tags in editor contents upon data.save. + detach: function(content, settings, instanceId) { + $content = $('
' + content + '
'); // No .outerHTML() in jQuery :( + $('img.wysiwyg-break', $content).replaceWith(''); + return $content.html(); + }, + + _getPlaceholder: function (settings) { + return '<--break->'; + } +};