Commit 166a12f1 authored by sun's avatar sun

#316507 by sun: Added Drupal.wysiwyg function stacks to execute editor library...

#316507 by sun: Added Drupal.wysiwyg function stacks to execute editor library specific actions upon initializing, attaching, detaching, and toggling an editor. Editor specific JavaScript resides in separate files now, as specified and returned by implementations of hook_editor().

Wysiwyg is a real API finally, supporting multiple editors and editor versions.  Yay! 8)
parent 14759316
tinymce
fckeditor
\ No newline at end of file
tinymce*
fckeditor*
\ No newline at end of file
......@@ -6,6 +6,11 @@ Wysiwyg x.x-x.x, xxxx-xx-xx
Wysiwyg 6.x-x.x, xxxx-xx-xx
---------------------------
#316507 by sun: Added Drupal.wysiwyg function stacks to execute editor library
specific actions upon initializing, attaching, detaching, and toggling an
editor. Editor specific JavaScript resides in separate files now, as specified
and returned by implementations of hook_editor().
Wysiwyg is a real API finally, supporting multiple editors and editor versions.
#316507 by sun: Rewrote Wysiwyg API's internal architecture to support multiple
editors.
......
// $Id$
Drupal.wysiwyg = Drupal.wysiwyg || { 'init': {}, 'attach': {}, 'detach': {}, 'toggle': {} };
/**
* Initialize TinyMCE instances.
*
* 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 enabled editor theme.
*/
Drupal.wysiwyg.init.tinymce = function(editorSettings) {
// If JS compression is enabled, TinyMCE is unable to find its own base path
// and exec mode, hence we need to define it manually.
// @todo Move global library settings somewhere else.
tinyMCE.baseURL = Drupal.settings.wysiwygEditor.editorBasePath;
tinyMCE.srcMode = (Drupal.settings.wysiwygEditor.execMode == 'src' ? '_src' : '');
tinyMCE.gzipMode = (Drupal.settings.wysiwygEditor.execMode == 'gzip');
for (var theme in editorSettings) {
// Clone, so original settings are not overwritten.
var config = Drupal.wysiwyg.clone(editorSettings[theme]);
tinyMCE.init(config);
}
// @todo Move into global library settings.
for (var plugin in Drupal.settings.wysiwygEditor.plugins.tinymce) {
tinyMCE.loadPlugin(plugin, Drupal.settings.wysiwygEditor.plugins.tinymce[plugin]);
}
}
/**
* Attach TinyMCE to textareas, using the theme specified in CSS class names.
*
* @param editorSettings
* An object containing editor settings for each enabled editor theme.
*/
Drupal.wysiwyg.attach.tinymce = function(context, editorSettings) {
for (var theme in editorSettings) {
// Clone, so original settings are not overwritten.
var config = Drupal.wysiwyg.clone(editorSettings[theme]);
// Configure settings for this theme.
for (var setting in config) {
tinyMCE.settings[setting] = config[setting];
}
$('textarea.wysiwyg-' + theme + ':not(.wysiwyg-processed)', context).each(function() {
// Attach Wysiwyg Editor control if default is on.
if (Drupal.settings.wysiwygEditor.status) {
tinyMCE.execCommand('mceAddControl', true, this.id);
}
$(this).addClass('wysiwyg-processed');
});
}
}
/**
* Toggle editor and return new state.
*
* @param element
* The DOM element to toggle the editor for.
* @param theme
* The editor theme assigned to the element.
*
* @return
* A boolean value indicating whether the editor has been enabled.
*/
Drupal.wysiwyg.toggle.tinymce = function(element, theme) {
if (tinyMCE.getEditorId(element.id) == null) {
// Clone, so original settings are not overwritten.
var config = Drupal.wysiwyg.clone(Drupal.settings.wysiwygEditor.configs.tinymce[theme]);
// Set configuration options for this theme.
for (var setting in config) {
tinyMCE.settings[setting] = config[setting];
}
tinyMCE.addMCEControl(element, element.id);
return true;
}
else {
tinyMCE.removeMCEControl(tinyMCE.getEditorId(element.id));
return false;
}
}
// $Id$
Drupal.wysiwyg = Drupal.wysiwyg || { 'init': {}, 'attach': {}, 'detach': {}, 'toggle': {} };
/**
* Initialize TinyMCE 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 enabled editor theme.
*/
Drupal.wysiwyg.init.tinymce = function(editorSettings) {
// If JS compression is enabled, TinyMCE is unable to find its own base path
// and exec mode, hence we need to define it manually.
// @todo Move global library settings somewhere else.
tinyMCE.baseURL = Drupal.settings.wysiwygEditor.editorBasePath;
tinyMCE.srcMode = (Drupal.settings.wysiwygEditor.execMode == 'src' ? '_src' : '');
tinyMCE.gzipMode = (Drupal.settings.wysiwygEditor.execMode == 'gzip');
for (var theme in editorSettings) {
// Clone, so original settings are not overwritten.
var config = Drupal.wysiwyg.clone(editorSettings[theme]);
tinyMCE.init(config);
}
// @todo Move into global library settings.
// @todo Plugin architecture is completely different in 3.x.
// for (var plugin in Drupal.settings.wysiwygEditor.plugins.tinymce) {
// tinyMCE.loadPlugin(plugin, Drupal.settings.wysiwygEditor.plugins.tinymce[plugin]);
// }
}
/**
* Attach TinyMCE to textareas, using the theme specified in CSS class names.
*
* @param editorSettings
* An object containing editor settings for each enabled editor theme.
*/
Drupal.wysiwyg.attach.tinymce = function(context, editorSettings) {
for (var theme in editorSettings) {
// Clone, so original settings are not overwritten.
var config = Drupal.wysiwyg.clone(editorSettings[theme]);
// Configure settings for this theme.
for (var setting in config) {
tinyMCE.settings[setting] = config[setting];
}
$('textarea.wysiwyg-' + theme + ':not(.wysiwyg-processed)', context).each(function() {
// Attach Wysiwyg Editor control if default is on.
if (Drupal.settings.wysiwygEditor.status) {
tinyMCE.execCommand('mceAddControl', true, this.id);
}
$(this).addClass('wysiwyg-processed');
});
}
}
/**
* Detach all TinyMCE editors.
*
* @todo Context support required to remove only certain editors (think AHAH/AJAX).
*/
Drupal.wysiwyg.detach.tinymce = function(context) {
if (tinyMCE.activeEditor) {
tinyMCE.triggerSave();
tinyMCE.activeEditor.remove();
}
}
/**
* Toggle editor and return new state.
*
* @param element
* The DOM element to toggle the editor for.
* @param theme
* The editor theme assigned to the element.
*
* @return
* A boolean value indicating whether the editor has been enabled.
*/
Drupal.wysiwyg.toggle.tinymce = function(element, theme) {
tinyMCE.execCommand('mceToggleEditor', false, element.id);
return !(tinyMCE.get(element.id).isHidden());
}
......@@ -17,7 +17,6 @@ function wysiwyg_tinymce_editor() {
'title' => 'TinyMCE',
'vendor url' => 'http://tinymce.moxiecode.com',
'download url' => 'http://tinymce.moxiecode.com/download.php',
'editor path' => wysiwyg_get_path('tinymce'),
'library path' => wysiwyg_get_path('tinymce') . '/jscripts/tiny_mce',
'libraries' => array( // We cannot assume that all editors need just one js library.
'' => array( // Key may be used in wysiwyg_tinymce_settings() for exec mode.
......@@ -29,7 +28,6 @@ function wysiwyg_tinymce_editor() {
'files' => array('tiny_mce_src.js'),
),
),
'js path' => wysiwyg_get_path('js'),
'version callback' => 'wysiwyg_tinymce_version',
'themes callback' => 'wysiwyg_tinymce_themes',
'settings callback' => 'wysiwyg_tinymce_settings',
......@@ -61,8 +59,11 @@ function wysiwyg_tinymce_editor() {
),
),
// Optional properties
'css path' => wysiwyg_get_path('css'),
'css files' => array('tinymce.css'),
// 'editor path' => wysiwyg_get_path('tinymce'), // Assumed by default.
// 'js path' => wysiwyg_get_path('editors/js'), // Assumed by default.
// 'css path' => wysiwyg_get_path('editors/css'), // Assumed by default.
// @todo Not yet implemented.
// 'css files' => array('tinymce.css'),
);
return $editor;
}
......
// $Id$
Drupal.wysiwyg = Drupal.wysiwyg || { 'init': {}, 'attach': {}, 'detach': {}, 'toggle': {} };
/**
* Initialize each Wysiwyg Editor configuration.
*
* This function needs to be called before the page is fully loaded, as
* calling tinyMCE.init() after the page is loaded breaks in IE 6.
* Initialize all editor libraries.
*/
Drupal.wysiwygEditorInit = function () {
// If JS compression is enabled, TinyMCE is unable to find its own base path
// and exec mode, hence we need to define it manually.
tinyMCE.baseURL = Drupal.settings.wysiwygEditor.editorBasePath;
tinyMCE.srcMode = (Drupal.settings.wysiwygEditor.execMode == 'src' ? '_src' : '');
tinyMCE.gzipMode = (Drupal.settings.wysiwygEditor.execMode == 'gzip');
for (var theme in Drupal.settings.wysiwygEditor.configs.tinymce) {
// Clone so we are not passing by reference. Otherwise the settings will
// get overwritten.
var config = Drupal.wysiwygEditorCloneObject(Drupal.settings.wysiwygEditor.configs.tinymce[theme]);
tinyMCE.init(config);
}
for (var plugin in Drupal.settings.wysiwygEditor.plugins.tinymce) {
tinyMCE.loadPlugin(plugin, Drupal.settings.wysiwygEditor.plugins.tinymce[plugin]);
}
Drupal.wysiwygEditorInit = function() {
jQuery.each(Drupal.wysiwyg.init, function(editor) {
this(Drupal.settings.wysiwygEditor.configs[editor]);
});
}
/**
* Attach Wysiwyg Editor to textareas.
* Attach editors to fields.
*
* This function can be called to process AJAX-loaded content.
*/
Drupal.wysiwygEditorAttach = function () {
for (var theme in Drupal.settings.wysiwygEditor.configs.tinymce) {
var config = Drupal.wysiwygEditorCloneObject(Drupal.settings.wysiwygEditor.configs.tinymce[theme]);
// Set configuration options for this theme.
for (var setting in config) {
tinyMCE.settings[setting] = config[setting];
}
$('textarea.wysiwyg-' + theme + ':not(.wysiwyg-processed)').each(function () {
// Show toggle link if set.
if (Drupal.settings.wysiwygEditor.showToggle) {
Drupal.wysiwygEditorAttachToggleLink(this, theme);
}
// Attach Wysiwyg Editor control if default is on.
if (Drupal.settings.wysiwygEditor.status) {
tinyMCE.execCommand('mceAddControl', true, this.id);
Drupal.behaviors.attachWysiwyg = function(context) {
jQuery.each(Drupal.wysiwyg.attach, function(editor) {
// Show toggle link if set.
if (Drupal.settings.wysiwygEditor.showToggle) {
for (var theme in Drupal.settings.wysiwygEditor.configs[editor]) {
$('textarea.wysiwyg-' + theme + ':not(.wysiwyg-processed)', context).each(function() {
Drupal.wysiwygEditorAttachToggleLink(this, editor, theme);
});
}
$(this).addClass('wysiwyg-processed');
});
}
}
this(context, Drupal.settings.wysiwygEditor.configs[editor]);
});
}
/**
* Toggle the Wysiwyg Editor control and related link text for a textarea.
* Append a toggle link to an element.
*
* @param element
* The DOM element to toggle the editor for.
* @param editor
* The editor name assigned to the element.
* @param theme
* The editor theme assigned to the element.
*/
Drupal.wysiwygEditorToggle = function (id, theme) {
if (tinyMCE.getEditorId(id) == null) {
var config = Drupal.wysiwygEditorCloneObject(Drupal.settings.wysiwygEditor.configs.tinymce[theme]);
// Set configuration options for this theme.
for (var setting in config) {
tinyMCE.settings[setting] = config[setting];
}
tinyMCE.addMCEControl($('#' + id).get(0), id);
$('#wysiwyg4' + id).html(Drupal.settings.wysiwygEditor.disable).blur();
}
else {
tinyMCE.removeMCEControl(tinyMCE.getEditorId(id));
$('#wysiwyg4' + id).html(Drupal.settings.wysiwygEditor.enable).blur();
}
Drupal.wysiwygEditorAttachToggleLink = function(element, editor, theme) {
var text = document.createTextNode(Drupal.settings.wysiwygEditor.status ? Drupal.settings.wysiwygEditor.disable : Drupal.settings.wysiwygEditor.enable);
var a = document.createElement('a');
$(a)
.click(function() {
Drupal.wysiwygEditorToggle(element, editor, theme);
})
.attr('id', 'wysiwyg4' + element.id)
.css('cursor', 'pointer')
.append(text);
var div = document.createElement('div');
$(div).append(a);
$(element).after(div);
}
/**
* Append toggle link to textarea.
* Enable/disable the editor and change toggle link text accordingly.
*
* Toggle implementation functions are expected to return the new state of a
* toggled editor.
*
* @param element
* The DOM element to toggle the editor for.
* @param editor
* The editor name assigned to the element.
* @param theme
* The editor theme assigned to the element.
*/
Drupal.wysiwygEditorAttachToggleLink = function (elt, theme) {
if (typeof(document.execCommand) == 'undefined') {
$(elt).after('<div style="font-size:x-small">' + Drupal.settings.wysiwygEditor.noWysiwyg + '</div>');
Drupal.wysiwygEditorToggle = function(element, editor, theme) {
if (typeof Drupal.wysiwyg.toggle[editor] == 'function') {
var new_state = Drupal.wysiwyg.toggle[editor](element, theme);
}
if (new_state) {
$('#wysiwyg4' + element.id).html(Drupal.settings.wysiwygEditor.disable).blur();
}
else {
var text = document.createTextNode(Drupal.settings.wysiwygEditor.status ? Drupal.settings.wysiwygEditor.disable : Drupal.settings.wysiwygEditor.enable);
var a = document.createElement('a');
$(a)
.click(function() {
Drupal.wysiwygEditorToggle(elt.id, theme);
})
.attr('id', 'wysiwyg4' + elt.id)
.css('cursor', 'pointer')
.append(text);
var div = document.createElement('div');
$(div).append(a);
$(elt).after(div);
$('#wysiwyg4' + element.id).html(Drupal.settings.wysiwygEditor.enable).blur();
}
}
Drupal.wysiwygEditorCloneObject = function (obj) {
/**
* Clone a configuration object recursively; required for certain editors.
*
* @param obj
* The object to clone.
*
* @return
* A copy of the passed in object.
*/
Drupal.wysiwyg.clone = function(obj) {
var clone = {};
for (i in obj) {
if ((typeof obj[i] == 'object') || (typeof obj[i] == 'array')) {
clone[i] = Drupal.wysiwygEditorCloneObject(obj[i]);
clone[i] = Drupal.wysiwyg.clone(obj[i]);
}
else {
clone[i] = obj[i];
......@@ -106,13 +103,7 @@ Drupal.wysiwygEditorCloneObject = function (obj) {
}
/**
* Global killswitch.
*/
if (Drupal.jsEnabled) {
$(document).ready(Drupal.wysiwygEditorAttach);
}
/**
* Initialize Wysiwyg Editor.
* Initialize editor libraries.
*/
Drupal.wysiwygEditorInit();
......@@ -177,67 +177,77 @@ function wysiwyg_editor_load_editor($profile) {
static $settings_added;
static $loaded = array();
$name = $profile->settings['editor'];
// Library files must only be loaded once.
if (!isset($loaded[$name])) {
// Load editor
$editor = wysiwyg_get_editor($name);
if ($editor) {
// Determine library files to load.
if (isset($profile->settings['library']) && isset($editor['libraries'][$profile->settings['library']])) {
$library = $profile->settings['library'];
$files = $editor['libraries'][$profile->settings['library']]['files'];
}
else {
// Fallback to the first by default (external libraries can change).
$library = key($editor['libraries']);
$files = array_shift($editor['libraries']);
$files = $files['files'];
}
foreach ($files as $file) {
drupal_add_js($editor['library path'] . '/' . $file);
}
// Load JavaScript integration files for this editor.
if (isset($editor['js files'])) {
$files = $editor['js files'];
}
foreach ($files as $file) {
drupal_add_js($editor['js path'] . '/' . $file, 'module', 'footer');
}
$status = wysiwyg_editor_user_get_status($profile);
drupal_add_js(array('wysiwygEditor' => array(
'configs' => array($editor['name'] => array()),
'showToggle' => $profile->settings['show_toggle'],
'status' => $status,
// If JS compression is enabled, at least TinyMCE is unable to determine
// its own base path and exec mode since it can't find the script name.
'editorBasePath' => base_path() . $editor['library path'],
'execMode' => $library,
)), 'setting');
$loaded[$name] = TRUE;
}
else {
$loaded[$name] = FALSE;
}
}
// Add basic Wysiwyg settings.
if (!isset($settings_added)) {
if (!isset($settings_added) && $loaded[$name]) {
drupal_add_js(array('wysiwygEditor' => array(
'configs' => array(),
'disable' => t('Disable rich-text'),
'enable' => t('Enable rich-text'),
'noWysiwyg' => t('Your current web browser does not support WYSIWYG editing.'),
)), 'setting');
// Add wysiwyg_editor.js to the footer to ensure it's executed after the
// Drupal.settings array has been rendered and populated.
// Drupal.settings array has been rendered and populated. Also, since editor
// library initialization functions must be loaded first by the browser,
// Drupal.wysiwygEditorInit() must be executed AFTER editors registered
// their callbacks, and BEFORE Drupal.behaviors are applied, this must come
// last.
// @todo Separate into wysiwyg.init.js and wysiwyg.editor?.js, to ensure
// this logic/ordering, and do not force editor integration scripts to
// check and define Drupal.wysiwyg on its own.
drupal_add_js(wysiwyg_get_path('wysiwyg_editor.js'), 'module', 'footer');
// Add our stylesheet to stack editor buttons into one row.
drupal_add_css(wysiwyg_get_path('wysiwyg_editor.css'));
$settings_added = TRUE;
}
$name = $profile->settings['editor'];
// Library files must only be loaded once.
if (!isset($loaded[$name])) {
// Load editor
$editor = wysiwyg_get_editor($name);
if (!$editor) {
$loaded[$name] = FALSE;
return FALSE;
}
// Determine library files to load.
if (isset($profile->settings['library']) && isset($editor['libraries'][$profile->settings['library']])) {
$library = $profile->settings['library'];
$files = $editor['libraries'][$profile->settings['library']]['files'];
}
else {
// Fallback to the first by default (external libraries can change).
$library = key($editor['libraries']);
$files = array_shift($editor['libraries']);
$files = $files['files'];
}
foreach ($files as $file) {
drupal_add_js($editor['library path'] . '/' . $file);
}
// Load JavaScript integration files for this editor.
if (isset($editor['js files'])) {
$files = $editor['js files'];
}
foreach ($files as $file) {
drupal_add_js($editor['js path'] . '/' . $file, 'module', 'footer');
}
$status = wysiwyg_editor_user_get_status($profile);
drupal_add_js(array('wysiwygEditor' => array(
'configs' => array($editor['name'] => array()),
'showToggle' => $profile->settings['show_toggle'],
'status' => $status,
// If JS compression is enabled, at least TinyMCE is unable to determine
// its own base path and exec mode since it can't find the script name.
'editorBasePath' => base_path() . $editor['library path'],
'execMode' => $library,
)), 'setting');
$loaded[$name] = TRUE;
}
return $loaded[$name];
}
......@@ -546,12 +556,17 @@ function wysiwyg_get_all_editors() {
'title' => '',
'vendor url' => '',
'download url' => '',
'library path' => '',
'editor path' => wysiwyg_get_path($properties['name']),
'library path' => wysiwyg_get_path($properties['name']),
'libraries' => array(),
'settings callback' => '',
'plugin callback' => '',
'version callback' => '',
'version callback' => NULL,
'themes callback' => NULL,
'settings callback' => NULL,
'plugin callback' => NULL,
'plugin settings callback' => NULL,
'versions' => array(),
'js path' => $properties['path'] .'/js',
'css path' => $properties['path'] .'/css',
);
// Check whether library is present.
if (!($editors[$editor]['installed'] = file_exists($properties['library path']))) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment