(function($) { /** * Initialize editor instances. * * @todo Is the following note still valid for 3.x? * This function needs to be called before the page is fully loaded, as * calling tinyMCE.init() after the page is loaded breaks IE6. * * @param editorSettings * An object containing editor settings for each input format. */ Drupal.wysiwyg.editor.init.tinymce = function(settings) { // @see #454992: drupal_get_js() must not use 'q' as query string. if (tinymce.query == 'q') { tinymce.query = ''; } // If JS compression is enabled, TinyMCE is unable to autodetect its global // settinge, hence we need to define them manually. // @todo Move global library settings somewhere else. tinyMCE.baseURL = settings.global.editorBasePath; tinyMCE.srcMode = (settings.global.execMode == 'src' ? '_src' : ''); tinyMCE.gzipMode = (settings.global.execMode == 'gzip'); // Initialize editor configurations. for (var format in settings) { if (format == 'global') { continue; }; tinyMCE.init(settings[format]); if (Drupal.settings.wysiwyg.plugins[format]) { // Load native external plugins. // Array syntax required; 'native' is a predefined token in JavaScript. for (var plugin in Drupal.settings.wysiwyg.plugins[format]['native']) { tinymce.PluginManager.load(plugin, Drupal.settings.wysiwyg.plugins[format]['native'][plugin]); } // Load Drupal plugins. for (var plugin in Drupal.settings.wysiwyg.plugins[format].drupal) { Drupal.wysiwyg.editor.instance.tinymce.addPlugin(plugin, Drupal.settings.wysiwyg.plugins[format].drupal[plugin], Drupal.settings.wysiwyg.plugins.drupal[plugin]); } } } }; /** * Attach this editor to a target element. * * See Drupal.wysiwyg.editor.attach.none() for a full desciption of this hook. */ Drupal.wysiwyg.editor.attach.tinymce = function(context, params, settings) { // Configure editor settings for this input format. var ed = new tinymce.Editor(params.field, settings); // Reset active instance id on any event. ed.onEvent.add(function(ed, e) { Drupal.wysiwyg.activeId = ed.id; }); // Make toolbar buttons wrappable (required for IE). ed.onPostRender.add(function (ed) { var $toolbar = $('
'); $('#' + ed.editorContainer + ' table.mceToolbar > tbody > tr > td').each(function () { $('
').addClass(this.className).append($(this).children()).appendTo($toolbar); }); $('#' + ed.editorContainer + ' table.mceLayout td.mceToolbar').append($toolbar); $('#' + ed.editorContainer + ' table.mceToolbar').remove(); }); // Remove TinyMCE's internal mceItem class, which was incorrectly added to // submitted content by Wysiwyg <2.1. TinyMCE only temporarily adds the class // for placeholder elements. If preemptively set, the class prevents (native) // editor plugins from gaining an active state, so we have to manually remove // it prior to attaching the editor. This is done on the client-side instead // of the server-side, as Wysiwyg has no way to figure out where content is // stored, and the class only affects editing. $field = $('#' + params.field); $field.val($field.val().replace(/(<.+?\s+class=['"][\w\s]*?)\bmceItem\b([\w\s]*?['"].*?>)/ig, '$1$2')); // Attach editor. ed.render(); }; /** * Detach a single or all editors. * * See Drupal.wysiwyg.editor.detach.none() for a full desciption of this hook. */ Drupal.wysiwyg.editor.detach.tinymce = function(context, params) { if (typeof params != 'undefined') { var instance = tinyMCE.get(params.field); if (instance) { instance.save(); instance.remove(); } } else { // Save contents of all editors back into textareas. tinyMCE.triggerSave(); // Remove all editor instances. for (var instance in tinyMCE.editors) { tinyMCE.editors[instance].remove(); } } }; Drupal.wysiwyg.editor.instance.tinymce = { addPlugin: function(plugin, settings, pluginSettings) { if (typeof Drupal.wysiwyg.plugins[plugin] != 'object') { return; } 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() }; // TinyMCE creates a completely new instance for fullscreen mode. var instanceId = ed.id == 'mce_fullscreen' ? ed.getParam('fullscreen_editor_id') : ed.id; Drupal.wysiwyg.plugins[plugin].invoke(data, pluginSettings, instanceId); } }); // 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, pluginSettings, ed.id); data.content = Drupal.wysiwyg.editor.instance.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, pluginSettings, 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(dialog, params) { var instanceId = this.isFullscreen() ? 'mce_fullscreen' : this.field; var editor = tinyMCE.get(instanceId); editor.windowManager.open({ file: dialog.url + '/' + instanceId, width: dialog.width, height: dialog.height, inline: 1 }, params); }, closeDialog: function(dialog) { var instanceId = this.isFullscreen() ? 'mce_fullscreen' : this.field; var editor = tinyMCE.get(instanceId); editor.windowManager.close(dialog); }, 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' } }; var $content = $('
' + content + '
'); // No .outerHTML() in jQuery :( // Find all placeholder/replacement content of Drupal plugins. $content.find('.drupal-content').each(function() { // Recursively process DOM elements below this element to apply special // properties. var $drupalContent = $(this); $.each(specialProperties, function(element, properties) { $drupalContent.find(element).andSelf().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(content) { content = this.prepareContent(content); var instanceId = this.isFullscreen() ? 'mce_fullscreen' : this.field; tinyMCE.execInstanceCommand(instanceId, 'mceInsertContent', false, content); }, isFullscreen: function() { // TinyMCE creates a completely new instance for fullscreen mode. return tinyMCE.activeEditor.id == 'mce_fullscreen' && tinyMCE.activeEditor.getParam('fullscreen_editor_id') == this.field; } }; })(jQuery);