' . t('Do NOT download the "CKEditor for Drupal" edition.') . '
'; +} + +/** + * Detect editor version. + * + * @param $editor + * An array containing editor properties as returned from hook_editor(). + * + * @return + * The installed editor version. + */ +function wysiwyg_ckeditor_version($editor) { + $library = $editor['library path'] . '/ckeditor.js'; + if (!file_exists($library)) { + return; + } + $library = fopen($library, 'r'); + $max_lines = 8; + while ($max_lines && $line = fgets($library, 500)) { + // version:'CKEditor 3.0 SVN',revision:'3665' + // version:'3.0 RC',revision:'3753' + // version:'3.0.1',revision:'4391' + if (preg_match('@version:\'(?:CKEditor )?([\d\.]+)(?:.+revision:\'([\d]+))?@', $line, $version)) { + fclose($library); + // Version numbers need to have three parts since 3.0.1. + $version[1] = preg_replace('/^(\d+)\.(\d+)$/', '${1}.${2}.0', $version[1]); + return $version[1] . '.' . $version[2]; + } + $max_lines--; + } + fclose($library); +} + +/** + * Determine available editor themes or check/reset a given one. + * + * @param $editor + * A processed hook_editor() array of editor properties. + * @param $profile + * A wysiwyg editor profile. + * + * @return + * An array of theme names. The first returned name should be the default + * theme name. + */ +function wysiwyg_ckeditor_themes($editor, $profile) { + // @todo Skins are not themes but this will do for now. + $path = $editor['library path'] . '/skins/'; + if (file_exists($path) && ($dir_handle = opendir($path))) { + $themes = array(); + while ($file = readdir($dir_handle)) { + if (is_dir($path . $file) && substr($file, 0, 1) != '.' && $file != 'CVS') { + $themes[] = $file; + } + } + closedir($dir_handle); + natcasesort($themes); + $themes = array_values($themes); + return !empty($themes) ? $themes : array('default'); + } + else { + return array('default'); + } +} + +/** + * Enhances the editor profile settings form for CKEditor. + * + * Adds support for CKEditor's advanced stylesSets, which are a more advanced + * implementation and combination of block formats and font styles that allow + * to adjust the HTML element, attributes, and CSS styles at once. + * + * @see http://docs.cksource.com/CKEditor_3.x/Developers_Guide/Styles + * @see http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.config.html#.stylesSet + */ +function wysiwyg_ckeditor_settings_form(&$form, &$form_state) { + if (version_compare($form_state['wysiwyg']['editor']['installed version'], '3.2.1', '>=')) { + // Replace CSS classes element description to explain the advanced syntax. + $form['css']['css_classes']['#description'] = t('Optionally define CSS classes for the "Font style" dropdown list.[label]=[element].[class]',
+ '!example' => 'Title=h1.title',
+ ));
+ $form['css']['css_classes']['#element_validate'][] = 'wysiwyg_ckeditor_settings_form_validate_css_classes';
+ }
+ else {
+ // Versions below 3.2.1 do not support Font styles at all.
+ $form['css']['css_classes']['#access'] = FALSE;
+ }
+}
+
+/**
+ * #element_validate handler for CSS classes element altered by wysiwyg_ckeditor_settings_form().
+ */
+function wysiwyg_ckeditor_settings_form_validate_css_classes($element, &$form_state) {
+ if (wysiwyg_ckeditor_settings_parse_styles($element['#value']) === FALSE) {
+ form_error($element, t('The specified CSS classes are syntactically incorrect.'));
+ }
+}
+
+/**
+ * Returns an initialization JavaScript for this editor library.
+ *
+ * @param array $editor
+ * The editor library definition.
+ * @param string $library
+ * The library variant key from $editor['libraries'].
+ * @param object $profile
+ * The (first) wysiwyg editor profile.
+ *
+ * @return string
+ * A string containing inline JavaScript to execute before the editor library
+ * script is loaded.
+ */
+function wysiwyg_ckeditor_init($editor) {
+ // CKEditor unconditionally searches for its library filename in SCRIPT tags
+ // on the page upon loading the library in order to determine the base path to
+ // itself. When JavaScript aggregation is enabled, this search fails and all
+ // relative constructed paths within CKEditor are broken. The library has a
+ // CKEditor.basePath property, but it is not publicly documented and thus not
+ // reliable. The official documentation suggests to solve the issue through
+ // the global window variable.
+ // @see http://docs.cksource.com/CKEditor_3.x/Developers_Guide/Specifying_the_Editor_Path
+ $library_path = base_path() . $editor['library path'] . '/';
+ return <<.
+ // Linebreaks can be inserted before or after opening and closing tags.
+ if (settings.apply_source_formatting) {
+ // Mimic FCKeditor output, by breaking lines between tags.
+ for (var tag in tags) {
+ if (tag == 'pre') {
+ continue;
+ }
+ this.dataProcessor.writer.setRules(tag, {
+ indent: true,
+ breakBeforeOpen: true,
+ breakAfterOpen: false,
+ breakBeforeClose: false,
+ breakAfterClose: true
+ });
+ }
+ }
+ else {
+ // CKEditor adds default formatting to
, so we want to remove that
+ // here too.
+ tags.br = 1;
+ // No indents or linebreaks;
+ for (var tag in tags) {
+ if (tag == 'pre') {
+ continue;
+ }
+ this.dataProcessor.writer.setRules(tag, {
+ indent: false,
+ breakBeforeOpen: false,
+ breakAfterOpen: false,
+ breakBeforeClose: false,
+ breakAfterClose: false
+ });
+ }
+ }
+ },
+
+ pluginsLoaded: function(ev) {
+ // Override the conversion methods to let Drupal plugins modify the data.
+ var editor = ev.editor;
+ if (editor.dataProcessor && Drupal.settings.wysiwyg.plugins[params.format]) {
+ editor.dataProcessor.toHtml = CKEDITOR.tools.override(editor.dataProcessor.toHtml, function(originalToHtml) {
+ // Convert raw data for display in WYSIWYG mode.
+ return function(data, fixForBody) {
+ for (var plugin in Drupal.settings.wysiwyg.plugins[params.format].drupal) {
+ if (typeof Drupal.wysiwyg.plugins[plugin].attach == 'function') {
+ data = Drupal.wysiwyg.plugins[plugin].attach(data, Drupal.settings.wysiwyg.plugins.drupal[plugin], editor.name);
+ data = Drupal.wysiwyg.instances[params.field].prepareContent(data);
+ }
+ }
+ return originalToHtml.call(this, data, fixForBody);
+ };
+ });
+ editor.dataProcessor.toDataFormat = CKEDITOR.tools.override(editor.dataProcessor.toDataFormat, function(originalToDataFormat) {
+ // Convert WYSIWYG mode content to raw data.
+ return function(data, fixForBody) {
+ data = originalToDataFormat.call(this, data, fixForBody);
+ for (var plugin in Drupal.settings.wysiwyg.plugins[params.format].drupal) {
+ if (typeof Drupal.wysiwyg.plugins[plugin].detach == 'function') {
+ data = Drupal.wysiwyg.plugins[plugin].detach(data, Drupal.settings.wysiwyg.plugins.drupal[plugin], editor.name);
+ }
+ }
+ return data;
+ };
+ });
+ }
+ },
+
+ selectionChange: function (event) {
+ var pluginSettings = Drupal.settings.wysiwyg.plugins[params.format];
+ if (pluginSettings && pluginSettings.drupal) {
+ $.each(pluginSettings.drupal, function (name) {
+ var plugin = Drupal.wysiwyg.plugins[name];
+ if ($.isFunction(plugin.isNode)) {
+ var node = event.data.selection.getSelectedElement();
+ var state = plugin.isNode(node ? node.$ : null) ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF;
+ event.editor.getCommand(name).setState(state);
+ }
+ });
+ }
+ },
+
+ focus: function(ev) {
+ Drupal.wysiwyg.activeId = ev.editor.name;
+ },
+
+ afterCommandExec: function(ev) {
+ // Fix Drupal toolbar obscuring editor toolbar in fullscreen mode.
+ if (ev.data.name != 'maximize') {
+ return;
+ }
+ if (ev.data.command.state == CKEDITOR.TRISTATE_ON) {
+ $drupalToolbar.hide();
+ }
+ else {
+ $drupalToolbar.show();
+ }
+ }
+ };
+
+ // Attach editor.
+ CKEDITOR.replace(params.field, settings);
+};
+
+/**
+ * Detach a single or all editors.
+ *
+ * @todo 3.x: editor.prototype.getInstances() should always return an array
+ * containing all instances or the passed in params.field instance, but
+ * always return an array to simplify all detach functions.
+ */
+Drupal.wysiwyg.editor.detach.ckeditor = function (context, params, trigger) {
+ var method = (trigger == 'serialize') ? 'updateElement' : 'destroy';
+ if (typeof params != 'undefined') {
+ var instance = CKEDITOR.instances[params.field];
+ if (instance) {
+ instance[method]();
+ }
+ }
+ else {
+ for (var instanceName in CKEDITOR.instances) {
+ if (CKEDITOR.instances.hasOwnProperty(instanceName)) {
+ CKEDITOR.instances[instanceName][method]();
+ }
+ }
+ }
+};
+
+Drupal.wysiwyg.editor.instance.ckeditor = {
+ addPlugin: function(pluginName, settings, pluginSettings) {
+ CKEDITOR.plugins.add(pluginName, {
+ // Wrap Drupal plugin in a proxy pluygin.
+ init: function(editor) {
+ if (settings.css) {
+ editor.on('mode', function(ev) {
+ if (ev.editor.mode == 'wysiwyg') {
+ // Inject CSS files directly into the editing area head tag.
+ $('head', $('#cke_contents_' + ev.editor.name + ' iframe').eq(0).contents()).append('');
+ }
+ });
+ }
+ if (typeof Drupal.wysiwyg.plugins[pluginName].invoke == 'function') {
+ var pluginCommand = {
+ exec: function (editor) {
+ var data = { format: 'html', node: null, content: '' };
+ var selection = editor.getSelection();
+ if (selection) {
+ data.node = selection.getSelectedElement();
+ if (data.node) {
+ data.node = data.node.$;
+ }
+ if (selection.getType() == CKEDITOR.SELECTION_TEXT) {
+ if (CKEDITOR.env.ie) {
+ data.content = selection.getNative().createRange().text;
+ }
+ else {
+ data.content = selection.getNative().toString();
+ }
+ }
+ else if (data.node) {
+ // content is supposed to contain the "outerHTML".
+ data.content = data.node.parentNode.innerHTML;
+ }
+ }
+ Drupal.wysiwyg.plugins[pluginName].invoke(data, pluginSettings, editor.name);
+ }
+ };
+ editor.addCommand(pluginName, pluginCommand);
+ }
+ editor.ui.addButton(pluginName, {
+ label: settings.iconTitle,
+ command: pluginName,
+ icon: settings.icon
+ });
+
+ // @todo Add button state handling.
+ }
+ });
+ },
+ prepareContent: function(content) {
+ // @todo Don't know if we need this yet.
+ return content;
+ },
+
+ insert: function(content) {
+ content = this.prepareContent(content);
+ CKEDITOR.instances[this.field].insertHtml(content);
+ },
+
+ setContent: function (content) {
+ CKEDITOR.instances[this.field].setData(content);
+ },
+
+ getContent: function () {
+ return CKEDITOR.instances[this.field].getData();
+ }
+};
+
+})(jQuery);
diff --git a/sites/all/modules/contrib/editor/wysiwyg/editors/js/epiceditor.js b/sites/all/modules/contrib/editor/wysiwyg/editors/js/epiceditor.js
new file mode 100644
index 00000000..03c48017
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/editors/js/epiceditor.js
@@ -0,0 +1,38 @@
+(function($) {
+
+/**
+ * Attach this editor to a target element.
+ */
+Drupal.wysiwyg.editor.attach.epiceditor = function (context, params, settings) {
+ var $target = $('#' + params.field);
+ var containerId = params.field + '-epiceditor';
+ var defaultContent = $target.val();
+ $target.hide().after('');
+
+ settings.container = containerId;
+ settings.file = {
+ defaultContent: defaultContent
+ };
+ settings.theme = {
+ preview: '/themes/preview/preview-dark.css',
+ editor: '/themes/editor/' + settings.theme + '.css'
+ }
+ var editor = new EpicEditor(settings).load();
+ $target.data('epiceditor', editor);
+};
+
+/**
+ * Detach a single or all editors.
+ */
+Drupal.wysiwyg.editor.detach.epiceditor = function (context, params, trigger) {
+ var $target = $('#' + params.field);
+ var editor = $target.data('epiceditor');
+
+ $target.val(editor.exportFile());
+
+ editor.unload(function () {
+ $target.show();
+ });
+};
+
+})(jQuery);
diff --git a/sites/all/modules/contrib/editor/wysiwyg/editors/js/fckeditor-2.6.js b/sites/all/modules/contrib/editor/wysiwyg/editors/js/fckeditor-2.6.js
new file mode 100644
index 00000000..fd915e3f
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/editors/js/fckeditor-2.6.js
@@ -0,0 +1,196 @@
+(function($) {
+
+/**
+ * Attach this editor to a target element.
+ */
+Drupal.wysiwyg.editor.attach.fckeditor = function(context, params, settings) {
+ var FCKinstance = new FCKeditor(params.field, settings.Width, settings.Height, settings.ToolbarSet);
+ // Apply editor instance settings.
+ FCKinstance.BasePath = settings.EditorPath;
+ FCKinstance.Config.wysiwygFormat = params.format;
+ FCKinstance.Config.CustomConfigurationsPath = settings.CustomConfigurationsPath;
+
+ // Load Drupal plugins and apply format specific settings.
+ // @see fckeditor.config.js
+ // @see Drupal.wysiwyg.editor.instance.fckeditor.init()
+
+ // Attach editor.
+ FCKinstance.ReplaceTextarea();
+};
+
+/**
+ * Detach a single or all editors.
+ */
+Drupal.wysiwyg.editor.detach.fckeditor = function (context, params, trigger) {
+ var instances = [];
+ if (typeof params != 'undefined' && typeof FCKeditorAPI != 'undefined') {
+ var instance = FCKeditorAPI.GetInstance(params.field);
+ if (instance) {
+ instances[params.field] = instance;
+ }
+ }
+ else {
+ instances = FCKeditorAPI.__Instances;
+ }
+
+ for (var instanceName in instances) {
+ var instance = instances[instanceName];
+ instance.UpdateLinkedField();
+ if (trigger == 'serialize') {
+ // The editor is not being removed from the DOM, so updating the linked
+ // field is the only action necessary.
+ continue;
+ }
+ // Since we already detach the editor and update the textarea, the submit
+ // event handler needs to be removed to prevent data loss (in IE).
+ // FCKeditor uses 2 nested iFrames; instance.EditingArea.Window is the
+ // deepest. Its parent is the iFrame containing the editor.
+ var instanceScope = instance.EditingArea.Window.parent;
+ instanceScope.FCKTools.RemoveEventListener(instance.GetParentForm(), 'submit', instance.UpdateLinkedField);
+ // Run cleanups before forcing an unload of the iFrames or IE crashes.
+ // This also deletes the instance from the FCKeditorAPI.__Instances array.
+ instanceScope.FCKTools.RemoveEventListener(instanceScope, 'unload', instanceScope.FCKeditorAPI_Cleanup);
+ instanceScope.FCKTools.RemoveEventListener(instanceScope, 'beforeunload', instanceScope.FCKeditorAPI_ConfirmCleanup);
+ if (jQuery.isFunction(instanceScope.FCKIECleanup_Cleanup)) {
+ instanceScope.FCKIECleanup_Cleanup();
+ }
+ instanceScope.FCKeditorAPI_ConfirmCleanup();
+ instanceScope.FCKeditorAPI_Cleanup();
+ // Remove the editor elements.
+ $('#' + instanceName + '___Config').remove();
+ $('#' + instanceName + '___Frame').remove();
+ $('#' + instanceName).show();
+ }
+};
+
+Drupal.wysiwyg.editor.instance.fckeditor = {
+ init: function(instance) {
+ // Track which editor instance is active.
+ instance.FCK.Events.AttachEvent('OnFocus', function(editorInstance) {
+ Drupal.wysiwyg.activeId = editorInstance.Name;
+ });
+
+ // Create a custom data processor to wrap the default one and allow Drupal
+ // plugins modify the editor contents.
+ var wysiwygDataProcessor = function() {};
+ wysiwygDataProcessor.prototype = new instance.FCKDataProcessor();
+ // Attach: Convert text into HTML.
+ wysiwygDataProcessor.prototype.ConvertToHtml = function(data) {
+ // Called from SetData() with stripped comments/scripts, revert those
+ // manipulations and attach Drupal plugins.
+ var data = instance.FCKConfig.ProtectedSource.Revert(data);
+ if (Drupal.settings.wysiwyg.plugins[instance.wysiwygFormat] && Drupal.settings.wysiwyg.plugins[instance.wysiwygFormat].drupal) {
+ for (var plugin in Drupal.settings.wysiwyg.plugins[instance.wysiwygFormat].drupal) {
+ if (typeof Drupal.wysiwyg.plugins[plugin].attach == 'function') {
+ data = Drupal.wysiwyg.plugins[plugin].attach(data, Drupal.settings.wysiwyg.plugins.drupal[plugin], instance.FCK.Name);
+ data = Drupal.wysiwyg.editor.instance.fckeditor.prepareContent(data);
+ }
+ }
+ }
+ // Re-protect the source and use the original data processor to convert it
+ // into XHTML.
+ data = instance.FCKConfig.ProtectedSource.Protect(data);
+ return instance.FCKDataProcessor.prototype.ConvertToHtml.call(this, data);
+ };
+ // Detach: Convert HTML into text.
+ wysiwygDataProcessor.prototype.ConvertToDataFormat = function(rootNode, excludeRoot, ignoreIfEmptyParagraph, format) {
+ // Called from GetData(), convert the content's DOM into a XHTML string
+ // using the original data processor and detach Drupal plugins.
+ var data = instance.FCKDataProcessor.prototype.ConvertToDataFormat.call(this, rootNode, excludeRoot, ignoreIfEmptyParagraph, format);
+ if (Drupal.settings.wysiwyg.plugins[instance.wysiwygFormat] && Drupal.settings.wysiwyg.plugins[instance.wysiwygFormat].drupal) {
+ for (var plugin in Drupal.settings.wysiwyg.plugins[instance.wysiwygFormat].drupal) {
+ if (typeof Drupal.wysiwyg.plugins[plugin].detach == 'function') {
+ data = Drupal.wysiwyg.plugins[plugin].detach(data, Drupal.settings.wysiwyg.plugins.drupal[plugin], instance.FCK.Name);
+ }
+ }
+ }
+ return data;
+ };
+ instance.FCK.DataProcessor = new wysiwygDataProcessor();
+ },
+
+ addPlugin: function(plugin, settings, pluginSettings, instance) {
+ if (typeof Drupal.wysiwyg.plugins[plugin] != 'object') {
+ return;
+ }
+
+ if (Drupal.settings.wysiwyg.plugins[instance.wysiwygFormat].drupal[plugin].css) {
+ instance.FCKConfig.EditorAreaCSS += ',' + Drupal.settings.wysiwyg.plugins[instance.wysiwygFormat].drupal[plugin].css;
+ }
+
+ // @see fckcommands.js, fck_othercommands.js, fckpastewordcommand.js
+ instance.FCKCommands.RegisterCommand(plugin, {
+ // Invoke the plugin's button.
+ Execute: function () {
+ if (typeof Drupal.wysiwyg.plugins[plugin].invoke == 'function') {
+ var data = { format: 'html', node: instance.FCKSelection.GetParentElement() };
+ // @todo This is NOT the same as data.node.
+ data.content = data.node.innerHTML;
+ Drupal.wysiwyg.plugins[plugin].invoke(data, pluginSettings, instance.FCK.Name);
+ }
+ },
+
+ // isNode: Return whether the plugin button should be enabled for the
+ // current selection.
+ // @see FCKUnlinkCommand.prototype.GetState()
+ GetState: function () {
+ // Always disabled if not in WYSIWYG mode.
+ if (instance.FCK.EditMode != FCK_EDITMODE_WYSIWYG) {
+ return FCK_TRISTATE_DISABLED;
+ }
+ var state = instance.FCK.GetNamedCommandState(this.Name);
+ // FCKeditor sets the wrong state in WebKit browsers.
+ if (!$.support.queryCommandEnabled && state == FCK_TRISTATE_DISABLED) {
+ state = FCK_TRISTATE_OFF;
+ }
+ if (state == FCK_TRISTATE_OFF && instance.FCK.EditMode == FCK_EDITMODE_WYSIWYG) {
+ if (typeof Drupal.wysiwyg.plugins[plugin].isNode == 'function') {
+ var node = instance.FCKSelection.GetSelectedElement();
+ state = Drupal.wysiwyg.plugins[plugin].isNode(node) ? FCK_TRISTATE_ON : FCK_TRISTATE_OFF;
+ }
+ }
+ return state;
+ },
+
+ /**
+ * Return information about the plugin as a name/value array.
+ */
+ Name: plugin
+ });
+
+ // Register the plugin button.
+ // Arguments: commandName, label, tooltip, style, sourceView, contextSensitive, icon.
+ instance.FCKToolbarItems.RegisterItem(plugin, new instance.FCKToolbarButton(plugin, settings.iconTitle, settings.iconTitle, null, false, true, settings.icon));
+ },
+
+ openDialog: function(dialog, params) {
+ // @todo Implement open dialog.
+ },
+
+ closeDialog: function(dialog) {
+ // @todo Implement close dialog.
+ },
+
+ prepareContent: function(content) {
+ // @todo Not needed for FCKeditor?
+ return content;
+ },
+
+ insert: function(content) {
+ var instance = FCKeditorAPI.GetInstance(this.field);
+ // @see FCK.InsertHtml(), FCK.InsertElement()
+ instance.InsertHtml(content);
+ },
+
+ getContent: function () {
+ var instance = FCKeditorAPI.GetInstance(this.field);
+ return instance.GetData();
+ },
+
+ setContent: function (content) {
+ var instance = FCKeditorAPI.GetInstance(this.field);
+ instance.SetHTML(content);
+ }
+};
+
+})(jQuery);
diff --git a/sites/all/modules/contrib/editor/wysiwyg/editors/js/fckeditor.config.js b/sites/all/modules/contrib/editor/wysiwyg/editors/js/fckeditor.config.js
new file mode 100644
index 00000000..42efc322
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/editors/js/fckeditor.config.js
@@ -0,0 +1,86 @@
+
+Drupal = window.parent.Drupal;
+
+/**
+ * Fetch and provide original editor settings as local variable.
+ *
+ * FCKeditor does not support to pass complex variable types to the editor.
+ * Instance settings passed to FCKinstance.Config are temporarily stored in
+ * FCKConfig.PageConfig.
+ */
+var wysiwygFormat = FCKConfig.PageConfig.wysiwygFormat;
+var wysiwygSettings = Drupal.settings.wysiwyg.configs.fckeditor[wysiwygFormat];
+var pluginSettings = (Drupal.settings.wysiwyg.plugins[wysiwygFormat] ? Drupal.settings.wysiwyg.plugins[wysiwygFormat] : { 'native': {}, 'drupal': {} });
+
+/**
+ * Apply format-specific settings.
+ */
+for (var setting in wysiwygSettings) {
+ if (setting == 'buttons') {
+ // Apply custom Wysiwyg toolbar for this format.
+ // FCKConfig.ToolbarSets['Wysiwyg'] = wysiwygSettings.buttons;
+
+ // Temporarily stack buttons into multiple button groups and remove
+ // separators until #277954 is solved.
+ FCKConfig.ToolbarSets['Wysiwyg'] = [];
+ for (var i = 0; i < wysiwygSettings.buttons[0].length; i++) {
+ FCKConfig.ToolbarSets['Wysiwyg'].push([wysiwygSettings.buttons[0][i]]);
+ }
+ FCKTools.AppendStyleSheet(document, '#xToolbar .TB_Start { display:none; }');
+ // Set valid height of select element in silver and office2003 skins.
+ if (FCKConfig.SkinPath.match(/\/office2003\/$/)) {
+ FCKTools.AppendStyleSheet(document, '#xToolbar .SC_FieldCaption { height: 24px; } #xToolbar .TB_End { display: none; }');
+ }
+ else if (FCKConfig.SkinPath.match(/\/silver\/$/)) {
+ FCKTools.AppendStyleSheet(document, '#xToolbar .SC_FieldCaption { height: 27px; }');
+ }
+ }
+ else {
+ FCKConfig[setting] = wysiwygSettings[setting];
+ }
+}
+
+// Fix Drupal toolbar obscuring editor toolbar in fullscreen mode.
+var oldFitWindowExecute = FCKFitWindow.prototype.Execute;
+var $drupalToolbar = window.parent.jQuery('#toolbar', Drupal.overlayChild ? window.parent.window.parent.document : window.parent.document);
+FCKFitWindow.prototype.Execute = function() {
+ oldFitWindowExecute.apply(this, arguments);
+ if (this.IsMaximized) {
+ $drupalToolbar.hide();
+ }
+ else {
+ $drupalToolbar.show();
+ }
+}
+
+/**
+ * Initialize this editor instance.
+ */
+Drupal.wysiwyg.editor.instance.fckeditor.init(window);
+
+/**
+ * Register native plugins for this input format.
+ *
+ * Parameters to Plugins.Add are:
+ * - Plugin name.
+ * - Languages the plugin is available in.
+ * - Location of the plugin folder; /fckplugin.js is appended.
+ */
+for (var plugin in pluginSettings['native']) {
+ // Languages and path may be undefined for internal plugins.
+ FCKConfig.Plugins.Add(plugin, pluginSettings['native'][plugin].languages, pluginSettings['native'][plugin].path);
+}
+
+/**
+ * Register Drupal plugins for this input format.
+ *
+ * Parameters to addPlugin() are:
+ * - Plugin name.
+ * - Format specific plugin settings.
+ * - General plugin settings.
+ * - A reference to this window so the plugin setup can access FCKConfig.
+ */
+for (var plugin in pluginSettings.drupal) {
+ Drupal.wysiwyg.editor.instance.fckeditor.addPlugin(plugin, pluginSettings.drupal[plugin], Drupal.settings.wysiwyg.plugins.drupal[plugin], window);
+}
+
diff --git a/sites/all/modules/contrib/editor/wysiwyg/editors/js/jwysiwyg.js b/sites/all/modules/contrib/editor/wysiwyg/editors/js/jwysiwyg.js
new file mode 100644
index 00000000..d3e7490f
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/editors/js/jwysiwyg.js
@@ -0,0 +1,43 @@
+(function($) {
+
+/**
+ * Attach this editor to a target element.
+ */
+Drupal.wysiwyg.editor.attach.jwysiwyg = function(context, params, settings) {
+ // Attach editor.
+ $('#' + params.field).wysiwyg();
+};
+
+/**
+ * Detach a single or all editors.
+ */
+Drupal.wysiwyg.editor.detach.jwysiwyg = function (context, params, trigger) {
+ var $field = $('#' + params.field);
+ var editor = $field.data('wysiwyg');
+ if (typeof editor != 'undefined') {
+ editor.saveContent();
+ if (trigger != 'serialize') {
+ editor.element.remove();
+ }
+ }
+ $field.removeData('wysiwyg');
+ if (trigger != 'serialize') {
+ $field.show();
+ }
+};
+
+Drupal.wysiwyg.editor.instance.jwysiwyg = {
+ insert: function (content) {
+ $('#' + this.field).wysiwyg('insertHtml', content);
+ },
+
+ setContent: function (content) {
+ $('#' + this.field).wysiwyg('setContent', content);
+ },
+
+ getContent: function () {
+ return $('#' + this.field).wysiwyg('getContent');
+ }
+};
+
+})(jQuery);
diff --git a/sites/all/modules/contrib/editor/wysiwyg/editors/js/markitup.js b/sites/all/modules/contrib/editor/wysiwyg/editors/js/markitup.js
new file mode 100644
index 00000000..00e10b97
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/editors/js/markitup.js
@@ -0,0 +1,46 @@
+(function($) {
+
+/**
+ * Attach this editor to a target element.
+ */
+Drupal.wysiwyg.editor.attach.markitup = function(context, params, settings) {
+ $('#' + params.field, context).markItUp(settings);
+
+ // Adjust CSS for editor buttons.
+ $.each(settings.markupSet, function (button) {
+ $('.' + settings.nameSpace + ' .' + this.className + ' a')
+ .css({ backgroundImage: 'url(' + settings.root + 'sets/default/images/' + button + '.png' + ')' })
+ .parents('li').css({ backgroundImage: 'none' });
+ });
+};
+
+/**
+ * Detach a single or all editors.
+ */
+Drupal.wysiwyg.editor.detach.markitup = function (context, params, trigger) {
+ if (trigger == 'serialize') {
+ return;
+ }
+ if (typeof params != 'undefined') {
+ $('#' + params.field, context).markItUpRemove();
+ }
+ else {
+ $('.markItUpEditor', context).markItUpRemove();
+ }
+};
+
+Drupal.wysiwyg.editor.instance.markitup = {
+ insert: function (content) {
+ $.markItUp({ replaceWith: content });
+ },
+
+ setContent: function (content) {
+ $('#' + this.field).val(content);
+ },
+
+ getContent: function () {
+ return $('#' + this.field).val();
+ }
+};
+
+})(jQuery);
diff --git a/sites/all/modules/contrib/editor/wysiwyg/editors/js/nicedit.js b/sites/all/modules/contrib/editor/wysiwyg/editors/js/nicedit.js
new file mode 100644
index 00000000..10b7809c
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/editors/js/nicedit.js
@@ -0,0 +1,115 @@
+(function($) {
+
+/**
+ * Attach this editor to a target element.
+ */
+Drupal.wysiwyg.editor.attach.nicedit = function(context, params, settings) {
+ // Intercept and ignore submit handlers or they will revert changes made
+ // since the instance was removed. The handlers are anonymous and hidden out
+ // of scope in a closure so we can't unbind them. The same operations are
+ // performed when the instance is detached anyway.
+ var oldAddEvent = bkLib.addEvent;
+ bkLib.addEvent = function(obj, type, fn) {
+ if (type != 'submit') {
+ oldAddEvent(obj, type, fn);
+ }
+ }
+ // Attach editor.
+ var editor = new nicEditor(settings);
+ editor.panelInstance(params.field);
+ // The old addEvent() must be restored after creating a new instance, as
+ // plugins with dialogs use it to bind submit handlers to their forms.
+ bkLib.addEvent = oldAddEvent;
+ editor.addEvent('focus', function () {
+ Drupal.wysiwyg.activeId = params.field;
+ });
+};
+
+/**
+ * Detach a single or all editors.
+ *
+ * See Drupal.wysiwyg.editor.detach.none() for a full description of this hook.
+ */
+Drupal.wysiwyg.editor.detach.nicedit = function (context, params, trigger) {
+ if (typeof params != 'undefined') {
+ var instance = nicEditors.findEditor(params.field);
+ if (instance) {
+ if (trigger == 'serialize') {
+ instance.saveContent();
+ }
+ else {
+ instance.ne.removeInstance(params.field);
+ instance.ne.removePanel();
+ }
+ }
+ }
+ else {
+ for (var e in nicEditors.editors) {
+ // Save contents of all editors back into textareas.
+ var instances = nicEditors.editors[e].nicInstances;
+ for (var i = 0; i < instances.length; i++) {
+ if (trigger == 'serialize') {
+ instances[i].saveContent();
+ }
+ else {
+ instances[i].remove();
+ }
+ }
+ // Remove all editor instances.
+ if (trigger != 'serialize') {
+ nicEditors.editors[e].nicInstances = [];
+ }
+ }
+ }
+};
+
+/**
+ * Instance methods for nicEdit.
+ */
+Drupal.wysiwyg.editor.instance.nicedit = {
+ insert: function (content) {
+ var instance = nicEditors.findEditor(this.field);
+ var editingArea = instance.getElm();
+ var sel = instance.getSel();
+ // IE.
+ if (document.selection) {
+ editingArea.focus();
+ sel.createRange().pasteHTML(content);
+ }
+ else {
+ // Convert selection to a range.
+ var range;
+ // W3C compatible.
+ if (sel.getRangeAt) {
+ range = sel.getRangeAt(0);
+ }
+ // Safari.
+ else {
+ range = editingArea.ownerDocument.createRange();
+ range.setStart(sel.anchorNode, sel.anchorOffset);
+ range.setEnd(sel.focusNode, userSeletion.focusOffset);
+ }
+ // The code below doesn't work in IE, but it never gets here.
+ var fragment = editingArea.ownerDocument.createDocumentFragment();
+ // Fragments don't support innerHTML.
+ var wrapper = editingArea.ownerDocument.createElement('div');
+ wrapper.innerHTML = content;
+ while (wrapper.firstChild) {
+ fragment.appendChild(wrapper.firstChild);
+ }
+ range.deleteContents();
+ // Only fragment children are inserted.
+ range.insertNode(fragment);
+ }
+ },
+
+ setContent: function (content) {
+ nicEditors.findEditor(this.field).setContent(content);
+ },
+
+ getContent: function () {
+ return nicEditors.findEditor(this.field).getContent();
+ }
+};
+
+})(jQuery);
diff --git a/sites/all/modules/contrib/editor/wysiwyg/editors/js/none.js b/sites/all/modules/contrib/editor/wysiwyg/editors/js/none.js
new file mode 100644
index 00000000..762f7fb2
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/editors/js/none.js
@@ -0,0 +1,91 @@
+(function($) {
+
+/**
+ * Attach this editor to a target element.
+ *
+ * @param context
+ * A DOM element, supplied by Drupal.attachBehaviors().
+ * @param params
+ * An object containing input format parameters. Default parameters are:
+ * - editor: The internal editor name.
+ * - theme: The name/key of the editor theme/profile to use.
+ * - field: The CSS id of the target element.
+ * @param settings
+ * An object containing editor settings for all enabled editor themes.
+ */
+Drupal.wysiwyg.editor.attach.none = function(context, params, settings) {
+ if (params.resizable) {
+ var $wrapper = $('#' + params.field).parents('.form-textarea-wrapper:first');
+ $wrapper.addClass('resizable');
+ if (Drupal.behaviors.textarea) {
+ Drupal.behaviors.textarea.attach();
+ }
+ }
+};
+
+/**
+ * Detach a single or all editors.
+ *
+ * The editor syncs its contents back to the original field before its instance
+ * is removed.
+ *
+ * @param context
+ * A DOM element, supplied by Drupal.attachBehaviors().
+ * @param params
+ * (optional) An object containing input format parameters. If defined,
+ * only the editor instance in params.field should be detached. Otherwise,
+ * all editors should be detached and saved, so they can be submitted in
+ * AJAX/AHAH applications.
+ * @param trigger
+ * A string describing why the editor is being detached.
+ * Possible triggers are:
+ * - unload: (default) Another or no editor is about to take its place.
+ * - move: Currently expected to produce the same result as unload.
+ * - serialize: The form is about to be serialized before an AJAX request or
+ * a normal form submission. If possible, perform a quick detach and leave
+ * the editor's GUI elements in place to avoid flashes or scrolling issues.
+ * @see Drupal.detachBehaviors
+ */
+Drupal.wysiwyg.editor.detach.none = function (context, params, trigger) {
+ if (typeof params != 'undefined' && (trigger != 'serialize')) {
+ var $wrapper = $('#' + params.field).parents('.form-textarea-wrapper:first');
+ $wrapper.removeOnce('textarea').removeClass('.resizable-textarea')
+ .find('.grippie').remove();
+ }
+};
+
+/**
+ * Instance methods for plain text areas.
+ */
+Drupal.wysiwyg.editor.instance.none = {
+ insert: function(content) {
+ var editor = document.getElementById(this.field);
+
+ // IE support.
+ if (document.selection) {
+ editor.focus();
+ var 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;
+ }
+ },
+
+ setContent: function (content) {
+ $('#' + this.field).val(content);
+ },
+
+ getContent: function () {
+ return $('#' + this.field).val();
+ }
+};
+
+})(jQuery);
diff --git a/sites/all/modules/contrib/editor/wysiwyg/editors/js/openwysiwyg.js b/sites/all/modules/contrib/editor/wysiwyg/editors/js/openwysiwyg.js
new file mode 100644
index 00000000..a01e8a0b
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/editors/js/openwysiwyg.js
@@ -0,0 +1,141 @@
+
+// Backup $ and reset it to jQuery.
+Drupal.wysiwyg._openwysiwyg = $;
+$ = jQuery;
+
+// Wrap openWYSIWYG's methods to temporarily use its version of $.
+jQuery.each(WYSIWYG, function (key, value) {
+ if (jQuery.isFunction(value)) {
+ WYSIWYG[key] = function () {
+ var old$ = $;
+ $ = Drupal.wysiwyg._openwysiwyg;
+ var result = value.apply(this, arguments);
+ $ = old$;
+ return result;
+ };
+ }
+});
+
+// Override editor functions.
+WYSIWYG.getEditor = function (n) {
+ return Drupal.wysiwyg._openwysiwyg("wysiwyg" + n);
+};
+
+(function($) {
+
+// Fix Drupal toolbar obscuring editor toolbar in fullscreen mode.
+var oldMaximize = WYSIWYG.maximize;
+WYSIWYG.maximize = function (n) {
+var $drupalToolbar = $('#toolbar', Drupal.overlayChild ? window.parent.document : document);
+ oldMaximize.apply(this, arguments);
+ if (this.maximized[n]) {
+ $drupalToolbar.hide();
+ }
+ else {
+ $drupalToolbar.show();
+ }
+}
+
+/**
+ * Attach this editor to a target element.
+ */
+Drupal.wysiwyg.editor.attach.openwysiwyg = function(context, params, settings) {
+ // Initialize settings.
+ settings.ImagesDir = settings.path + 'images/';
+ settings.PopupsDir = settings.path + 'popups/';
+ settings.CSSFile = settings.path + 'styles/wysiwyg.css';
+ //settings.DropDowns = [];
+ var config = new WYSIWYG.Settings();
+ for (var setting in settings) {
+ config[setting] = settings[setting];
+ }
+ // Attach editor.
+ WYSIWYG.setSettings(params.field, config);
+ WYSIWYG_Core.includeCSS(WYSIWYG.config[params.field].CSSFile);
+ WYSIWYG._generate(params.field, config);
+};
+
+/**
+ * Detach a single or all editors.
+ */
+Drupal.wysiwyg.editor.detach.openwysiwyg = function (context, params, trigger) {
+ if (typeof params != 'undefined') {
+ var instance = WYSIWYG.config[params.field];
+ if (typeof instance != 'undefined') {
+ WYSIWYG.updateTextArea(params.field);
+ if (trigger != 'serialize') {
+ jQuery('#wysiwyg_div_' + params.field).remove();
+ delete instance;
+ }
+ }
+ if (trigger != 'serialize') {
+ jQuery('#' + params.field).show();
+ }
+ }
+ else {
+ jQuery.each(WYSIWYG.config, function(field) {
+ WYSIWYG.updateTextArea(field);
+ if (trigger != 'serialize') {
+ jQuery('#wysiwyg_div_' + field).remove();
+ delete this;
+ jQuery('#' + field).show();
+ }
+ });
+ }
+};
+
+/**
+ * Instance methods for openWYSIWYG.
+ */
+Drupal.wysiwyg.editor.instance.openwysiwyg = {
+ insert: function (content) {
+ // If IE has dropped focus content will be inserted at the top of the page.
+ $('#wysiwyg' + this.field).contents().find('body').focus();
+ WYSIWYG.insertHTML(content, this.field);
+ },
+
+ setContent: function (content) {
+ // Based on openWYSIWYG's _generate() method.
+ var doc = WYSIWYG.getEditorWindow(this.field).document;
+ if (WYSIWYG.config[this.field].ReplaceLineBreaks) {
+ content = content.replace(/\n\r|\n/ig, '
');
+ }
+ if (WYSIWYG.viewTextMode[this.field]) {
+ var html = document.createTextNode(content);
+ doc.body.innerHTML = '';
+ doc.body.appendChild(html);
+ }
+ else {
+ doc.open();
+ doc.write(content);
+ doc.close();
+ }
+ },
+
+ getContent: function () {
+ // Based on openWYSIWYG's updateTextarea() method.
+ var content = '';
+ var doc = WYSIWYG.getEditorWindow(this.field).document;
+ if (WYSIWYG.viewTextMode[this.field]) {
+ if (WYSIWYG_Core.isMSIE) {
+ content = doc.body.innerText;
+ }
+ else {
+ var range = doc.body.ownerDocument.createRange();
+ range.selectNodeContents(doc.body);
+ content = range.toString();
+ }
+ }
+ else {
+ content = doc.body.innerHTML;
+ }
+ content = WYSIWYG.stripURLPath(this.field, content);
+ content = WYSIWYG_Core.replaceRGBWithHexColor(content);
+ if (WYSIWYG.config[this.field].ReplaceLineBreaks) {
+ content = content.replace(/(\r\n)|(\n)/ig, '');
+ }
+ return content;
+ }
+};
+
+})(jQuery);
diff --git a/sites/all/modules/contrib/editor/wysiwyg/editors/js/tinymce-2.js b/sites/all/modules/contrib/editor/wysiwyg/editors/js/tinymce-2.js
new file mode 100644
index 00000000..61a60ade
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/editors/js/tinymce-2.js
@@ -0,0 +1,203 @@
+(function($) {
+
+/**
+ * Initialize editor 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 input format.
+ */
+Drupal.wysiwyg.editor.init.tinymce = function(settings) {
+ // Initialize editor configurations.
+ for (var format in settings) {
+ 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.loadPlugin(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.
+ for (var setting in settings) {
+ tinyMCE.settings[setting] = settings[setting];
+ }
+
+ // 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.
+ tinyMCE.execCommand('mceAddControl', true, params.field);
+};
+
+/**
+ * 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, trigger) {
+ if (typeof params != 'undefined') {
+ tinyMCE.removeMCEControl(tinyMCE.getEditorId(params.field));
+ $('#' + params.field).removeAttr('style');
+ }
+// else if (tinyMCE.activeEditor) {
+// tinyMCE.triggerSave();
+// tinyMCE.activeEditor.remove();
+// }
+};
+
+Drupal.wysiwyg.editor.instance.tinymce = {
+ addPlugin: function(plugin, settings, pluginSettings) {
+ if (typeof Drupal.wysiwyg.plugins[plugin] != 'object') {
+ return;
+ }
+ 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, pluginSettings, ed.formTargetElementId);
+ 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, pluginSettings, tinyMCE.selectedInstance.editorId);
+ content = Drupal.wysiwyg.editor.instance.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, pluginSettings, 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(dialog, params) {
+ var editor = tinyMCE.getInstanceById(this.field);
+ tinyMCE.openWindow({
+ file: dialog.url + '/' + this.field,
+ width: dialog.width,
+ height: dialog.height,
+ inline: 1
+ }, params);
+ },
+
+ closeDialog: function(dialog) {
+ var editor = tinyMCE.getInstanceById(this.field);
+ 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' }
+ };
+ var $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(content) {
+ content = this.prepareContent(content);
+ var editor = tinyMCE.getInstanceById(this.field);
+ editor.execCommand('mceInsertContent', false, content);
+ editor.repaint();
+ }
+};
+
+})(jQuery);
diff --git a/sites/all/modules/contrib/editor/wysiwyg/editors/js/tinymce-3.js b/sites/all/modules/contrib/editor/wysiwyg/editors/js/tinymce-3.js
new file mode 100644
index 00000000..83bae13a
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/editors/js/tinymce-3.js
@@ -0,0 +1,256 @@
+(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) {
+ // Fix Drupal toolbar obscuring editor toolbar in fullscreen mode.
+ var $drupalToolbar = $('#toolbar', Drupal.overlayChild ? window.parent.document : document);
+ tinyMCE.onAddEditor.add(function (mgr, ed) {
+ if (ed.id == 'mce_fullscreen') {
+ $drupalToolbar.hide();
+ }
+ });
+ tinyMCE.onRemoveEditor.add(function (mgr, ed) {
+ if (ed.id == 'mce_fullscreen') {
+ $drupalToolbar.show();
+ }
+ });
+
+ // Initialize editor configurations.
+ for (var format in settings) {
+ 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;
+ });
+ // Indicate that the DOM has been loaded (in case of Ajax).
+ tinymce.dom.Event.domLoaded = true;
+ // 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, trigger) {
+ if (typeof params != 'undefined') {
+ var instance = tinyMCE.get(params.field);
+ if (instance) {
+ instance.save();
+ if (trigger != 'serialize') {
+ instance.remove();
+ }
+ }
+ }
+ else {
+ // Save contents of all editors back into textareas.
+ tinyMCE.triggerSave();
+ if (trigger != 'serialize') {
+ // 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) {
+ var editorId = (ed.id == 'mce_fullscreen' ? ed.getParam('fullscreen_editor_id') : ed.id);
+ if (typeof Drupal.wysiwyg.plugins[plugin].attach == 'function') {
+ data.content = Drupal.wysiwyg.plugins[plugin].attach(data.content, pluginSettings, editorId);
+ data.content = Drupal.wysiwyg.editor.instance.tinymce.prepareContent(data.content);
+ }
+ });
+
+ // Detach: Replace HTML representations with plain text.
+ ed.onGetContent.add(function(ed, data) {
+ var editorId = (ed.id == 'mce_fullscreen' ? ed.getParam('fullscreen_editor_id') : ed.id);
+ if (typeof Drupal.wysiwyg.plugins[plugin].detach == 'function') {
+ data.content = Drupal.wysiwyg.plugins[plugin].detach(data.content, pluginSettings, editorId);
+ }
+ });
+
+ // 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.getInstanceId();
+ 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 editor = tinyMCE.get(this.getInstanceId());
+ 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);
+ tinyMCE.execInstanceCommand(this.getInstanceId(), 'mceInsertContent', false, content);
+ },
+
+ setContent: function (content) {
+ content = this.prepareContent(content);
+ tinyMCE.execInstanceCommand(this.getInstanceId(), 'mceSetContent', false, content);
+ },
+
+ getContent: function () {
+ return tinyMCE.get(this.getInstanceId()).getContent();
+ },
+
+ 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;
+ },
+
+ getInstanceId: function () {
+ return this.isFullscreen() ? 'mce_fullscreen' : this.field;
+ }
+};
+
+})(jQuery);
diff --git a/sites/all/modules/contrib/editor/wysiwyg/editors/js/whizzywig-56.js b/sites/all/modules/contrib/editor/wysiwyg/editors/js/whizzywig-56.js
new file mode 100644
index 00000000..3fc2fe57
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/editors/js/whizzywig-56.js
@@ -0,0 +1,155 @@
+
+var wysiwygWhizzywig = { currentField: null, fields: {} };
+var buttonPath = null;
+
+/**
+ * Override Whizzywig's document.write() function.
+ *
+ * Whizzywig uses document.write() by default, which leads to a blank page when
+ * invoked in jQuery.ready(). Luckily, Whizzywig developers implemented a
+ * shorthand w() substitute function that we can override to redirect the output
+ * into the global wysiwygWhizzywig variable.
+ *
+ * @see o()
+ */
+var w = function (string) {
+ if (string) {
+ wysiwygWhizzywig.fields[wysiwygWhizzywig.currentField] += string;
+ }
+ return wysiwygWhizzywig.fields[wysiwygWhizzywig.currentField];
+};
+
+/**
+ * Override Whizzywig's document.getElementById() function.
+ *
+ * Since we redirect the output of w() into a temporary string upon attaching
+ * an editor, we also have to override the o() shorthand substitute function
+ * for document.getElementById() to search in the document or our container.
+ * This override function also inserts the editor instance when Whizzywig
+ * tries to access its IFRAME, so it has access to the full/regular window
+ * object.
+ *
+ * @see w()
+ */
+var o = function (id) {
+ // Upon first access to "whizzy" + id, Whizzywig tries to access its IFRAME,
+ // so we need to insert the editor into the DOM.
+ if (id == 'whizzy' + wysiwygWhizzywig.currentField && wysiwygWhizzywig.fields[wysiwygWhizzywig.currentField]) {
+ jQuery('#' + wysiwygWhizzywig.currentField).after('');
+ // Iframe's .contentWindow becomes null in Webkit if inserted via .after().
+ jQuery('#' + wysiwygWhizzywig.currentField + '-whizzywig').html(w());
+ // Prevent subsequent invocations from inserting the editor multiple times.
+ wysiwygWhizzywig.fields[wysiwygWhizzywig.currentField] = '';
+ }
+ // If id exists in the regular window.document, return it.
+ if (jQuery('#' + id).size()) {
+ return jQuery('#' + id).get(0);
+ }
+ // Otherwise return id from our container.
+ return jQuery('#' + id, w()).get(0);
+};
+
+(function($) {
+
+/**
+ * Attach this editor to a target element.
+ */
+Drupal.wysiwyg.editor.attach.whizzywig = function(context, params, settings) {
+ // Previous versions used per-button images found in this location,
+ // now it is only used for custom buttons.
+ if (settings.buttonPath) {
+ window.buttonPath = settings.buttonPath;
+ }
+ // Assign the toolbar image path used for native buttons, if available.
+ if (settings.toolbarImagePath) {
+ btn._f = settings.toolbarImagePath;
+ }
+ // Fall back to text labels for all buttons.
+ else {
+ window.buttonPath = 'textbuttons';
+ }
+ // Create Whizzywig container.
+ wysiwygWhizzywig.currentField = params.field;
+ wysiwygWhizzywig.fields[wysiwygWhizzywig.currentField] = '';
+ // Whizzywig needs to have the width set 'inline'.
+ $field = $('#' + params.field);
+ var originalValues = Drupal.wysiwyg.instances[params.field];
+ originalValues.originalStyle = $field.attr('style');
+ $field.css('width', $field.width() + 'px');
+
+ // Attach editor.
+ makeWhizzyWig(params.field, (settings.buttons ? settings.buttons : 'all'));
+ // Whizzywig fails to detect and set initial textarea contents.
+ $('#whizzy' + params.field).contents().find('body').html(tidyD($field.val()));
+};
+
+/**
+ * Detach a single or all editors.
+ */
+Drupal.wysiwyg.editor.detach.whizzywig = function (context, params, trigger) {
+ var detach = function (index) {
+ var id = whizzies[index], $field = $('#' + id), instance = Drupal.wysiwyg.instances[id];
+
+ // Save contents of editor back into textarea.
+ $field.val(instance.getContent());
+ // If the editor is just being serialized (not detached), our work is done.
+ if (trigger == 'serialize') {
+ return;
+ }
+ // Remove editor instance.
+ $('#' + id + '-whizzywig').remove();
+ whizzies.splice(index, 1);
+
+ // Restore original textarea styling.
+ $field.removeAttr('style').attr('style', instance.originalStyle);
+ };
+
+ if (typeof params != 'undefined') {
+ for (var i = 0; i < whizzies.length; i++) {
+ if (whizzies[i] == params.field) {
+ detach(i);
+ break;
+ }
+ }
+ }
+ else {
+ while (whizzies.length > 0) {
+ detach(0);
+ }
+ }
+};
+
+/**
+ * Instance methods for Whizzywig.
+ */
+Drupal.wysiwyg.editor.instance.whizzywig = {
+ insert: function (content) {
+ // Whizzywig executes any string beginning with 'js:'.
+ insHTML(content.replace(/^js:/, 'js:'));
+ },
+
+ setContent: function (content) {
+ // Whizzywig shows the original textarea in source mode.
+ if ($field.css('display') == 'block') {
+ $('#' + this.field).val(content);
+ }
+ else {
+ var doc = $('#whizzy' + this.field).contents()[0];
+ doc.open();
+ doc.write(content);
+ doc.close();
+ }
+ },
+
+ getContent: function () {
+ // Whizzywig's tidyH() expects a document node. Clone the editing iframe's
+ // document so tidyH() won't mess with it if this gets called while editing.
+ var clone = $($('#whizzy' + this.field).contents()[0].documentElement).clone()[0].ownerDocument;
+ // Whizzywig shows the original textarea in source mode so update the body.
+ if ($field.css('display') == 'block') {
+ clone.body.innerHTML = $('#' + this.field).val();
+ }
+ return tidyH(clone);
+ }
+};
+})(jQuery);
diff --git a/sites/all/modules/contrib/editor/wysiwyg/editors/js/whizzywig-60.js b/sites/all/modules/contrib/editor/wysiwyg/editors/js/whizzywig-60.js
new file mode 100644
index 00000000..bbc6e649
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/editors/js/whizzywig-60.js
@@ -0,0 +1,107 @@
+
+var buttonPath = null;
+
+(function($) {
+
+/**
+ * Attach this editor to a target element.
+ */
+Drupal.wysiwyg.editor.attach.whizzywig = function(context, params, settings) {
+ // Previous versions used per-button images found in this location,
+ // now it is only used for custom buttons.
+ if (settings.buttonPath) {
+ window.buttonPath = settings.buttonPath;
+ }
+ // Assign the toolbar image path used for native buttons, if available.
+ if (settings.toolbarImagePath) {
+ btn._f = settings.toolbarImagePath;
+ }
+ // Fall back to text labels for all buttons.
+ else {
+ window.buttonPath = 'textbuttons';
+ }
+ // Whizzywig needs to have the width set 'inline'.
+ $field = $('#' + params.field);
+ var originalValues = Drupal.wysiwyg.instances[params.field];
+ originalValues.originalStyle = $field.attr('style');
+ $field.css('width', $field.width() + 'px');
+
+ // Attach editor.
+ makeWhizzyWig(params.field, (settings.buttons ? settings.buttons : 'all'));
+ // Whizzywig fails to detect and set initial textarea contents.
+ $('#whizzy' + params.field).contents().find('body').html(tidyD($field.val()));
+};
+
+/**
+ * Detach a single or all editors.
+ */
+Drupal.wysiwyg.editor.detach.whizzywig = function (context, params, trigger) {
+ var detach = function (index) {
+ var id = whizzies[index], $field = $('#' + id), instance = Drupal.wysiwyg.instances[id];
+
+ // Save contents of editor back into textarea.
+ $field.val(instance.getContent());
+ // If the editor is just being serialized (not detached), our work is done.
+ if (trigger == 'serialize') {
+ return;
+ }
+ // Move original textarea back to its previous location.
+ var $container = $('#CONTAINER' + id);
+ $field.insertBefore($container);
+ // Remove editor instance.
+ $container.remove();
+ whizzies.splice(index, 1);
+
+ // Restore original textarea styling.
+ $field.removeAttr('style').attr('style', instance.originalStyle);
+ }
+
+ if (typeof params != 'undefined') {
+ for (var i = 0; i < whizzies.length; i++) {
+ if (whizzies[i] == params.field) {
+ detach(i);
+ break;
+ }
+ }
+ }
+ else {
+ while (whizzies.length > 0) {
+ detach(0);
+ }
+ }
+};
+
+/**
+ * Instance methods for Whizzywig.
+ */
+Drupal.wysiwyg.editor.instance.whizzywig = {
+ insert: function (content) {
+ // Whizzywig executes any string beginning with 'js:'.
+ insHTML(content.replace(/^js:/, 'js:'));
+ },
+
+ setContent: function (content) {
+ // Whizzywig shows the original textarea in source mode.
+ if ($field.css('display') == 'block') {
+ $('#' + this.field).val(content);
+ }
+ else {
+ var doc = $('#whizzy' + this.field).contents()[0];
+ doc.open();
+ doc.write(content);
+ doc.close();
+ }
+ },
+
+ getContent: function () {
+ // Whizzywig's tidyH() expects a document node. Clone the editing iframe's
+ // document so tidyH() won't mess with it if this gets called while editing.
+ var clone = $($('#whizzy' + this.field).contents()[0].documentElement).clone()[0].ownerDocument;
+ // Whizzywig shows the original textarea in source mode so update the body.
+ if ($field.css('display') == 'block') {
+ clone.body.innerHTML = $('#' + this.field).val();
+ }
+ return tidyH(clone);
+ }
+};
+})(jQuery);
diff --git a/sites/all/modules/contrib/editor/wysiwyg/editors/js/whizzywig.js b/sites/all/modules/contrib/editor/wysiwyg/editors/js/whizzywig.js
new file mode 100644
index 00000000..e89ac5f0
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/editors/js/whizzywig.js
@@ -0,0 +1,154 @@
+
+var wysiwygWhizzywig = { currentField: null, fields: {} };
+var buttonPath = null;
+
+/**
+ * Override Whizzywig's document.write() function.
+ *
+ * Whizzywig uses document.write() by default, which leads to a blank page when
+ * invoked in jQuery.ready(). Luckily, Whizzywig developers implemented a
+ * shorthand w() substitute function that we can override to redirect the output
+ * into the global wysiwygWhizzywig variable.
+ *
+ * @see o()
+ */
+var w = function (string) {
+ if (string) {
+ wysiwygWhizzywig.fields[wysiwygWhizzywig.currentField] += string;
+ }
+ return wysiwygWhizzywig.fields[wysiwygWhizzywig.currentField];
+};
+
+/**
+ * Override Whizzywig's document.getElementById() function.
+ *
+ * Since we redirect the output of w() into a temporary string upon attaching
+ * an editor, we also have to override the o() shorthand substitute function
+ * for document.getElementById() to search in the document or our container.
+ * This override function also inserts the editor instance when Whizzywig
+ * tries to access its IFRAME, so it has access to the full/regular window
+ * object.
+ *
+ * @see w()
+ */
+var o = function (id) {
+ // Upon first access to "whizzy" + id, Whizzywig tries to access its IFRAME,
+ // so we need to insert the editor into the DOM.
+ if (id == 'whizzy' + wysiwygWhizzywig.currentField && wysiwygWhizzywig.fields[wysiwygWhizzywig.currentField]) {
+ jQuery('#' + wysiwygWhizzywig.currentField).after('');
+ // Iframe's .contentWindow becomes null in Webkit if inserted via .after().
+ jQuery('#' + wysiwygWhizzywig.currentField + '-whizzywig').html(w());
+ // Prevent subsequent invocations from inserting the editor multiple times.
+ wysiwygWhizzywig.fields[wysiwygWhizzywig.currentField] = '';
+ }
+ // If id exists in the regular window.document, return it.
+ if (jQuery('#' + id).size()) {
+ return jQuery('#' + id).get(0);
+ }
+ // Otherwise return id from our container.
+ return jQuery('#' + id, w()).get(0);
+};
+
+(function($) {
+
+/**
+ * Attach this editor to a target element.
+ */
+Drupal.wysiwyg.editor.attach.whizzywig = function(context, params, settings) {
+ // Assign button images path, if available.
+ if (settings.buttonPath) {
+ window.buttonPath = settings.buttonPath;
+ }
+ // Create Whizzywig container.
+ wysiwygWhizzywig.currentField = params.field;
+ wysiwygWhizzywig.fields[wysiwygWhizzywig.currentField] = '';
+ // Whizzywig needs to have the width set 'inline'.
+ $field = $('#' + params.field);
+ var originalValues = Drupal.wysiwyg.instances[params.field];
+ originalValues.originalStyle = $field.attr('style');
+ $field.css('width', $field.width() + 'px');
+
+ // Attach editor.
+ makeWhizzyWig(params.field, (settings.buttons ? settings.buttons : 'all'));
+ // Whizzywig fails to detect and set initial textarea contents.
+ $('#whizzy' + params.field).contents().find('body').html(tidyD($field.val()));
+};
+
+/**
+ * Detach a single or all editors.
+ */
+Drupal.wysiwyg.editor.detach.whizzywig = function (context, params, trigger) {
+ var detach = function (index) {
+ var id = whizzies[index], $field = $('#' + id), instance = Drupal.wysiwyg.instances[id];
+
+ // Save contents of editor back into textarea.
+ $field.val(instance.getContent());
+ // If the editor is just being serialized (not detached), our work is done.
+ if (trigger == 'serialize') {
+ return;
+ }
+ // Remove editor instance.
+ $('#' + id + '-whizzywig').remove();
+ whizzies.splice(index, 1);
+
+ // Restore original textarea styling.
+ $field.removeAttr('style').attr('style', instance.originalStyle);
+ };
+
+ if (typeof params != 'undefined') {
+ for (var i = 0; i < whizzies.length; i++) {
+ if (whizzies[i] == params.field) {
+ detach(i);
+ break;
+ }
+ }
+ }
+ else {
+ while (whizzies.length > 0) {
+ detach(0);
+ }
+ }
+};
+
+/**
+ * Instance methods for Whizzywig.
+ */
+Drupal.wysiwyg.editor.instance.whizzywig = {
+ insert: function (content) {
+ // Whizzywig executes any string beginning with 'js:'.
+ insHTML(content.replace(/^js:/, 'js:'));
+ },
+
+ setContent: function (content) {
+ var $field = $('#' + this.field);
+ // Whizzywig shows the original textarea in source mode.
+ if ($field.css('display') == 'block') {
+ $field.val(content);
+ }
+ else {
+ var doc = $('#whizzy' + this.field).contents()[0];
+ doc.open();
+ doc.write(content);
+ doc.close();
+ }
+ },
+
+ getContent: function () {
+ var $field = $('#' + this.field),
+ // Whizzywig shows the original textarea in source mode.
+ content = ($field.css('display') == 'block' ?
+ $field.val() : $('#whizzy' + this.field).contents().find('body').html()
+ );
+
+ content = tidyH(content);
+ // Whizzywig's get_xhtml() addon, if defined, expects a DOM node.
+ if ($.isFunction(window.get_xhtml)) {
+ var pre = document.createElement('pre');
+ pre.innerHTML = content;
+ content = get_xhtml(pre);
+ }
+ return content.replace(location.href + '#', '#');
+ }
+};
+
+})(jQuery);
diff --git a/sites/all/modules/contrib/editor/wysiwyg/editors/js/wymeditor.js b/sites/all/modules/contrib/editor/wysiwyg/editors/js/wymeditor.js
new file mode 100644
index 00000000..4989dc60
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/editors/js/wymeditor.js
@@ -0,0 +1,74 @@
+(function($) {
+
+/**
+ * Attach this editor to a target element.
+ */
+Drupal.wysiwyg.editor.attach.wymeditor = function (context, params, settings) {
+ // Prepend basePath to wymPath.
+ settings.wymPath = settings.basePath + settings.wymPath;
+ // Update activeId on focus.
+ settings.postInit = function (instance) {
+ $(instance._doc).focus(function () {
+ Drupal.wysiwyg.activeId = params.field;
+ });
+ };
+ // Attach editor.
+ $('#' + params.field).wymeditor(settings);
+};
+
+/**
+ * Detach a single or all editors.
+ */
+Drupal.wysiwyg.editor.detach.wymeditor = function (context, params, trigger) {
+ if (typeof params != 'undefined') {
+ var $field = $('#' + params.field);
+ var index = $field.data(WYMeditor.WYM_INDEX);
+ if (typeof index != 'undefined') {
+ var instance = WYMeditor.INSTANCES[index];
+ instance.update();
+ if (trigger != 'serialize') {
+ $(instance._box).remove();
+ $(instance._element).show();
+ delete instance;
+ }
+ }
+ if (trigger != 'serialize') {
+ $field.show();
+ }
+ }
+ else {
+ jQuery.each(WYMeditor.INSTANCES, function () {
+ this.update();
+ if (trigger != 'serialize') {
+ $(this._box).remove();
+ $(this._element).show();
+ delete this;
+ }
+ });
+ }
+};
+
+Drupal.wysiwyg.editor.instance.wymeditor = {
+ insert: function (content) {
+ this.getInstance().insert(content);
+ },
+
+ setContent: function (content) {
+ this.getInstance().html(content);
+ },
+
+ getContent: function () {
+ return this.getInstance().xhtml();
+ },
+
+ getInstance: function () {
+ var $field = $('#' + this.field);
+ var index = $field.data(WYMeditor.WYM_INDEX);
+ if (typeof index != 'undefined') {
+ return WYMeditor.INSTANCES[index];
+ }
+ return null;
+ }
+};
+
+})(jQuery);
diff --git a/sites/all/modules/contrib/editor/wysiwyg/editors/js/yui.js b/sites/all/modules/contrib/editor/wysiwyg/editors/js/yui.js
new file mode 100644
index 00000000..3f4e7c63
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/editors/js/yui.js
@@ -0,0 +1,145 @@
+(function($) {
+
+/**
+ * Attach this editor to a target element.
+ *
+ * Since buttons must be added before the editor is rendered, we add plugins
+ * buttons on attach event rather than in init.
+ */
+Drupal.wysiwyg.editor.attach.yui = function(context, params, settings) {
+ // Apply theme.
+ $('#' + params.field).parent().addClass('yui-skin-' + settings.theme);
+
+ // Load plugins stylesheet.
+ for (var plugin in Drupal.settings.wysiwyg.plugins[params.format].drupal) {
+ settings.extracss += settings.extracss+' @import "'+Drupal.settings.wysiwyg.plugins[params.format].drupal[plugin].css+'"; ';
+ }
+
+ // Attach editor.
+ var editor = new YAHOO.widget.Editor(params.field, settings);
+
+ editor.on('toolbarLoaded', function() {
+ // Load Drupal plugins.
+ for (var plugin in Drupal.settings.wysiwyg.plugins[params.format].drupal) {
+ Drupal.wysiwyg.instances[params.field].addPlugin(plugin, Drupal.settings.wysiwyg.plugins[params.format].drupal[plugin], Drupal.settings.wysiwyg.plugins.drupal[plugin]);
+ }
+ });
+
+ // Allow plugins to act on setEditorHTML.
+ var oldSetEditorHTML = editor.setEditorHTML;
+ editor.setEditorHTML = function (content) {
+ for (var plugin in Drupal.settings.wysiwyg.plugins[params.format].drupal) {
+ var pluginSettings = Drupal.settings.wysiwyg.plugins.drupal[plugin];
+ if (typeof Drupal.wysiwyg.plugins[plugin].attach == 'function') {
+ content = Drupal.wysiwyg.plugins[plugin].attach(content, pluginSettings, params.field);
+ content = Drupal.wysiwyg.instances[params.field].prepareContent(content);
+ }
+ }
+ oldSetEditorHTML.call(this, content);
+ };
+
+ // Allow plugins to act on getEditorHTML.
+ var oldGetEditorHTML = editor.getEditorHTML;
+ editor.getEditorHTML = function () {
+ var content = oldGetEditorHTML.call(this);
+ for (var plugin in Drupal.settings.wysiwyg.plugins[params.format].drupal) {
+ var pluginSettings = Drupal.settings.wysiwyg.plugins.drupal[plugin];
+ if (typeof Drupal.wysiwyg.plugins[plugin].detach == 'function') {
+ content = Drupal.wysiwyg.plugins[plugin].detach(content, pluginSettings, params.field);
+ }
+ }
+ return content;
+ }
+
+ // Reload the editor contents to give Drupal plugins a chance to act.
+ editor.on('editorContentLoaded', function (e) {
+ e.target.setEditorHTML(oldGetEditorHTML.call(e.target));
+ });
+
+ editor.on('afterNodeChange', function (e) {
+ for (var plugin in Drupal.settings.wysiwyg.plugins[params.format].drupal) {
+ if (typeof Drupal.wysiwyg.plugins[plugin].isNode == 'function') {
+ if (Drupal.wysiwyg.plugins[plugin].isNode(e.target._getSelectedElement())) {
+ this.toolbar.selectButton(plugin);
+ }
+ }
+ }
+ });
+
+ editor.render();
+};
+
+/**
+ * Detach a single or all editors.
+ *
+ * See Drupal.wysiwyg.editor.detach.none() for a full desciption of this hook.
+ */
+Drupal.wysiwyg.editor.detach.yui = function (context, params, trigger) {
+ var method = (trigger && trigger == 'serialize') ? 'saveHTML' : 'destroy';
+ if (typeof params != 'undefined') {
+ var instance = YAHOO.widget.EditorInfo._instances[params.field];
+ if (instance) {
+ instance[method]();
+ if (method == 'destroy') {
+ delete YAHOO.widget.EditorInfo._instances[params.field];
+ }
+ }
+ }
+ else {
+ for (var e in YAHOO.widget.EditorInfo._instances) {
+ // Save contents of all editors back into textareas.
+ var instance = YAHOO.widget.EditorInfo._instances[e];
+ instance[method]();
+ if (method == 'destroy') {
+ delete YAHOO.widget.EditorInfo._instances[e];
+ }
+ }
+ }
+};
+
+/**
+ * Instance methods for YUI Editor.
+ */
+Drupal.wysiwyg.editor.instance.yui = {
+ addPlugin: function (plugin, settings, pluginSettings) {
+ if (typeof Drupal.wysiwyg.plugins[plugin] != 'object') {
+ return;
+ }
+ var editor = YAHOO.widget.EditorInfo.getEditorById(this.field);
+ var button = editor.toolbar.getButtonByValue(plugin);
+ $(button._button).parent().css('background', 'transparent url(' + settings.icon + ') no-repeat center');
+ // 'this' will reference the toolbar while inside the event handler.
+ var instanceId = this.field;
+ editor.toolbar.on(plugin + 'Click', function (e) {
+ var selectedElement = editor._getSelectedElement();
+ // @todo Using .html() will cause XTHML vs HTML conflicts.
+ var data = {
+ format: 'html',
+ node: selectedElement,
+ content: $(selectedElement).html()
+ };
+ Drupal.wysiwyg.plugins[plugin].invoke(data, pluginSettings, instanceId);
+ });
+ },
+
+ prepareContent: function (content) {
+ var editor = YAHOO.widget.EditorInfo.getEditorById(this.field);
+ content = editor.cleanHTML(content);
+ return content;
+ },
+
+ insert: function (content) {
+ YAHOO.widget.EditorInfo.getEditorById(this.field).cmd_inserthtml(content);
+ },
+
+ setContent: function (content) {
+ YAHOO.widget.EditorInfo.getEditorById(this.field).setEditorHTML(content);
+ },
+
+ getContent: function () {
+ var instance = YAHOO.widget.EditorInfo.getEditorById(this.field);
+ return instance.cleanHTML(instance.getEditorHTML(content));
+ }
+};
+
+})(jQuery);
diff --git a/sites/all/modules/contrib/editor/wysiwyg/editors/jwysiwyg.inc b/sites/all/modules/contrib/editor/wysiwyg/editors/jwysiwyg.inc
new file mode 100644
index 00000000..fa65b741
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/editors/jwysiwyg.inc
@@ -0,0 +1,62 @@
+ 'jWYSIWYG',
+ 'vendor url' => 'http://code.google.com/p/jwysiwyg/',
+ 'download url' => 'http://code.google.com/p/jwysiwyg/downloads/list',
+ 'libraries' => array(
+ '' => array(
+ 'title' => 'Source',
+ 'files' => array('jquery.wysiwyg.js'),
+ ),
+ 'pack' => array(
+ 'title' => 'Packed',
+ 'files' => array('jquery.wysiwyg.pack.js'),
+ ),
+ ),
+ 'version callback' => 'wysiwyg_jwysiwyg_version',
+ // @todo Wrong property; add separate properties for editor requisites.
+ 'css path' => wysiwyg_get_path('jwysiwyg'),
+ 'versions' => array(
+ '0.5' => array(
+ 'js files' => array('jwysiwyg.js'),
+ 'css files' => array('jquery.wysiwyg.css'),
+ ),
+ ),
+ );
+ return $editor;
+}
+
+/**
+ * Detect editor version.
+ *
+ * @param $editor
+ * An array containing editor properties as returned from hook_editor().
+ *
+ * @return
+ * The installed editor version.
+ */
+function wysiwyg_jwysiwyg_version($editor) {
+ $script = $editor['library path'] . '/jquery.wysiwyg.js';
+ if (!file_exists($script)) {
+ return;
+ }
+ $script = fopen($script, 'r');
+ fgets($script);
+ $line = fgets($script);
+ if (preg_match('@([0-9\.]+)$@', $line, $version)) {
+ fclose($script);
+ return $version[1];
+ }
+ fclose($script);
+}
+
diff --git a/sites/all/modules/contrib/editor/wysiwyg/editors/markitup.inc b/sites/all/modules/contrib/editor/wysiwyg/editors/markitup.inc
new file mode 100644
index 00000000..57a37e83
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/editors/markitup.inc
@@ -0,0 +1,189 @@
+ 'markItUp',
+ 'vendor url' => 'http://markitup.jaysalvat.com',
+ 'download url' => 'http://markitup.jaysalvat.com/downloads',
+ 'library path' => wysiwyg_get_path('markitup'),
+ 'libraries' => array(
+ '' => array(
+ 'title' => 'Source',
+ 'files' => array('markitup/jquery.markitup.js'),
+ ),
+ 'pack' => array(
+ 'title' => 'Packed',
+ 'files' => array('markitup/jquery.markitup.pack.js'),
+ ),
+ ),
+ 'version callback' => 'wysiwyg_markitup_version',
+ 'themes callback' => 'wysiwyg_markitup_themes',
+ 'settings callback' => 'wysiwyg_markitup_settings',
+ 'plugin callback' => 'wysiwyg_markitup_plugins',
+ 'versions' => array(
+ '1.1.5' => array(
+ 'js files' => array('markitup.js'),
+ ),
+ ),
+ );
+ return $editor;
+}
+
+/**
+ * Detect editor version.
+ *
+ * @param $editor
+ * An array containing editor properties as returned from hook_editor().
+ *
+ * @return
+ * The installed editor version.
+ */
+function wysiwyg_markitup_version($editor) {
+ // Changelog was in markitup/markitup/readme.txt <= 1.1.5.
+ $changelog = $editor['library path'] . '/markitup/readme.txt';
+ if (!file_exists($changelog)) {
+ // Changelog was moved up to markitup/CHANGELOG.md after 1.1.5.
+ $changelog = $editor['library path'] . '/CHANGELOG.md';
+ if (!file_exists($changelog)) {
+ return;
+ }
+ }
+ $changelog = fopen($changelog, 'r');
+ $line = fgets($changelog);
+ if (preg_match('@([0-9\.]+)@', $line, $version)) {
+ fclose($changelog);
+ return $version[1];
+ }
+ fclose($changelog);
+}
+
+/**
+ * Determine available editor themes or check/reset a given one.
+ *
+ * @param $editor
+ * A processed hook_editor() array of editor properties.
+ * @param $profile
+ * A wysiwyg editor profile.
+ *
+ * @return
+ * An array of theme names. The first returned name should be the default
+ * theme name.
+ */
+function wysiwyg_markitup_themes($editor, $profile) {
+ return array('simple', 'markitup');
+}
+
+/**
+ * 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_markitup_settings($editor, $config, $theme) {
+ drupal_add_css($editor['library path'] . '/markitup/skins/' . $theme . '/style.css', array(
+ // Specify an alternate basename; otherwise, style.css would override a
+ // commonly used style.css file of the theme.
+ 'basename' => 'markitup.' . $theme . '.style.css',
+ 'group' => CSS_THEME,
+ ));
+
+ $settings = array(
+ 'root' => base_path() . $editor['library path'] . '/markitup/',
+ 'nameSpace' => $theme,
+ 'markupSet' => array(),
+ );
+
+ // Add configured buttons or all available.
+ $default_buttons = array(
+ 'bold' => array(
+ 'name' => t('Bold'),
+ 'className' => 'markitup-bold',
+ 'key' => 'B',
+ 'openWith' => '(!(|!|)!)',
+ 'closeWith' => '(!(|!|)!)',
+ ),
+ 'italic' => array(
+ 'name' => t('Italic'),
+ 'className' => 'markitup-italic',
+ 'key' => 'I',
+ 'openWith' => '(!(|!|)!)',
+ 'closeWith' => '(!(|!|)!)',
+ ),
+ 'stroke' => array(
+ 'name' => t('Strike-through'),
+ 'className' => 'markitup-stroke',
+ 'key' => 'S',
+ 'openWith' => '',
+ 'closeWith' => '',
+ ),
+ 'image' => array(
+ 'name' => t('Image'),
+ 'className' => 'markitup-image',
+ 'key' => 'P',
+ 'replaceWith' => '
',
+ ),
+ 'link' => array(
+ 'name' => t('Link'),
+ 'className' => 'markitup-link',
+ 'key' => 'K',
+ 'openWith' => '',
+ 'closeWith' => '',
+ 'placeHolder' => 'Your text to link...',
+ ),
+ // @todo
+ // 'cleanup' => array('name' => t('Clean-up'), 'className' => 'markitup-cleanup', 'replaceWith' => 'function(markitup) { return markitup.selection.replace(/<(.*?)>/g, "") }'),
+ 'preview' => array(
+ 'name' => t('Preview'),
+ 'className' => 'markitup-preview',
+ 'call' => 'preview',
+ ),
+ );
+ $settings['markupSet'] = array();
+ if (!empty($config['buttons'])) {
+ foreach ($config['buttons'] as $plugin) {
+ foreach ($plugin as $button => $enabled) {
+ if (isset($default_buttons[$button])) {
+ $settings['markupSet'][$button] = $default_buttons[$button];
+ }
+ }
+ }
+ }
+
+ return $settings;
+}
+
+/**
+ * Return internal plugins for this editor; semi-implementation of hook_wysiwyg_plugin().
+ */
+function wysiwyg_markitup_plugins($editor) {
+ return array(
+ 'default' => array(
+ 'buttons' => array(
+ 'bold' => t('Bold'), 'italic' => t('Italic'),
+ 'stroke' => t('Strike-through'),
+ 'link' => t('Link'),
+ 'image' => t('Image'),
+ // 'cleanup' => t('Clean-up'),
+ 'preview' => t('Preview'),
+ ),
+ 'internal' => TRUE,
+ ),
+ );
+}
+
diff --git a/sites/all/modules/contrib/editor/wysiwyg/editors/nicedit.inc b/sites/all/modules/contrib/editor/wysiwyg/editors/nicedit.inc
new file mode 100644
index 00000000..6acc800d
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/editors/nicedit.inc
@@ -0,0 +1,119 @@
+ 'NicEdit',
+ 'vendor url' => 'http://nicedit.com',
+ 'download url' => 'http://nicedit.com/download.php',
+ 'libraries' => array(
+ '' => array(
+ 'title' => 'Source',
+ 'files' => array('nicEdit.js'),
+ ),
+ ),
+ 'version callback' => 'wysiwyg_nicedit_version',
+ 'settings callback' => 'wysiwyg_nicedit_settings',
+ 'plugin callback' => 'wysiwyg_nicedit_plugins',
+ 'versions' => array(
+ '0.9' => array(
+ 'js files' => array('nicedit.js'),
+ ),
+ ),
+ );
+ return $editor;
+}
+
+/**
+ * Detect editor version.
+ *
+ * @param $editor
+ * An array containing editor properties as returned from hook_editor().
+ *
+ * @return
+ * The installed editor version.
+ */
+function wysiwyg_nicedit_version($editor) {
+ // @see http://nicedit.com/forums/viewtopic.php?t=425
+ return '0.9';
+}
+
+/**
+ * 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_nicedit_settings($editor, $config, $theme) {
+ $settings = array(
+ 'iconsPath' => base_path() . $editor['library path'] . '/nicEditorIcons.gif',
+ );
+
+ // Add configured buttons or all available.
+ $settings['buttonList'] = array();
+ if (!empty($config['buttons'])) {
+ $buttons = array();
+ foreach ($config['buttons'] as $plugin) {
+ $buttons = array_merge($buttons, $plugin);
+ }
+ $settings['buttonList'] = array_keys($buttons);
+ }
+
+ // Add editor content stylesheet.
+ if (isset($config['css_setting'])) {
+ if ($config['css_setting'] == 'theme') {
+ $css = drupal_get_path('theme', variable_get('theme_default', NULL)) . '/style.css';
+ if (file_exists($css)) {
+ $settings['externalCSS'] = base_path() . $css;
+ }
+ }
+ elseif ($config['css_setting'] == 'self' && isset($config['css_path'])) {
+ $settings['externalCSS'] = strtr($config['css_path'], array('%b' => base_path(), '%t' => drupal_get_path('theme', variable_get('theme_default', NULL))));
+ }
+ }
+
+ return $settings;
+}
+
+/**
+ * Return internal plugins for this editor; semi-implementation of hook_wysiwyg_plugin().
+ */
+function wysiwyg_nicedit_plugins($editor) {
+ return array(
+ 'default' => array(
+ 'buttons' => array(
+ 'bold' => t('Bold'), 'italic' => t('Italic'), 'underline' => t('Underline'),
+ 'strikethrough' => t('Strike-through'),
+ 'left' => t('Align left'), 'center' => t('Align center'), 'right' => t('Align right'),
+ 'ul' => t('Bullet list'), 'ol' => t('Numbered list'),
+ 'outdent' => t('Outdent'), 'indent' => t('Indent'),
+ 'image' => t('Image'),
+ 'forecolor' => t('Forecolor'), 'bgcolor' => t('Backcolor'),
+ 'superscript' => t('Superscript'), 'subscript' => t('Subscript'),
+ 'hr' => t('Horizontal rule'),
+ // @todo New challenge: Optional internal plugins packaged into editor
+ // library.
+ 'link' => t('Link'), 'unlink' => t('Unlink'),
+ 'fontFormat' => t('HTML block format'), 'fontFamily' => t('Font'), 'fontSize' => t('Font size'),
+ 'xhtml' => t('Source code'),
+ ),
+ 'internal' => TRUE,
+ ),
+ );
+}
+
diff --git a/sites/all/modules/contrib/editor/wysiwyg/editors/openwysiwyg.inc b/sites/all/modules/contrib/editor/wysiwyg/editors/openwysiwyg.inc
new file mode 100644
index 00000000..b3ad84dd
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/editors/openwysiwyg.inc
@@ -0,0 +1,173 @@
+ 'openWYSIWYG',
+ 'vendor url' => 'http://www.openwebware.com',
+ 'download url' => 'http://www.openwebware.com/download.shtml',
+ 'library path' => wysiwyg_get_path('openwysiwyg') . '/scripts',
+ 'libraries' => array(
+ 'src' => array(
+ 'title' => 'Source',
+ 'files' => array('wysiwyg.js'),
+ ),
+ ),
+ 'version callback' => 'wysiwyg_openwysiwyg_version',
+ 'themes callback' => 'wysiwyg_openwysiwyg_themes',
+ 'settings callback' => 'wysiwyg_openwysiwyg_settings',
+ 'plugin callback' => 'wysiwyg_openwysiwyg_plugins',
+ 'versions' => array(
+ '1.4.7' => array(
+ 'js files' => array('openwysiwyg.js'),
+ 'css files' => array('openwysiwyg.css'),
+ ),
+ ),
+ );
+ return $editor;
+}
+
+/**
+ * Detect editor version.
+ *
+ * @param $editor
+ * An array containing editor properties as returned from hook_editor().
+ *
+ * @return
+ * The installed editor version.
+ */
+function wysiwyg_openwysiwyg_version($editor) {
+ // 'library path' has '/scripts' appended already.
+ $changelog = $editor['editor path'] . '/changelog';
+ if (!file_exists($changelog)) {
+ return;
+ }
+ $changelog = fopen($changelog, 'r');
+ $line = fgets($changelog, 20);
+ if (preg_match('@v([\d\.]+)@', $line, $version)) {
+ fclose($changelog);
+ return $version[1];
+ }
+ fclose($changelog);
+}
+
+/**
+ * Determine available editor themes or check/reset a given one.
+ *
+ * @param $editor
+ * A processed hook_editor() array of editor properties.
+ * @param $profile
+ * A wysiwyg editor profile.
+ *
+ * @return
+ * An array of theme names. The first returned name should be the default
+ * theme name.
+ */
+function wysiwyg_openwysiwyg_themes($editor, $profile) {
+ return array('default');
+}
+
+/**
+ * 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_openwysiwyg_settings($editor, $config, $theme) {
+ $settings = array(
+ 'path' => base_path() . $editor['editor path'] . '/',
+ 'Width' => '100%',
+ );
+
+ if (isset($config['path_loc']) && $config['path_loc'] == 'none') {
+ $settings['StatusBarEnabled'] = FALSE;
+ }
+
+ if (isset($config['css_setting'])) {
+ if ($config['css_setting'] == 'theme') {
+ $settings['CSSFile'] = reset(wysiwyg_get_css());
+ }
+ elseif ($config['css_setting'] == 'self' && isset($config['css_path'])) {
+ $settings['CSSFile'] = strtr($config['css_path'], array('%b' => base_path(), '%t' => drupal_get_path('theme', variable_get('theme_default', NULL))));
+ }
+ }
+
+ $settings['Toolbar'] = array();
+ if (!empty($config['buttons'])) {
+ $plugins = wysiwyg_get_plugins($editor['name']);
+ foreach ($config['buttons'] as $plugin => $buttons) {
+ foreach ($buttons as $button => $enabled) {
+ foreach (array('buttons', 'extensions') as $type) {
+ // Skip unavailable plugins.
+ if (!isset($plugins[$plugin][$type][$button])) {
+ continue;
+ }
+ // Add buttons.
+ if ($type == 'buttons') {
+ $settings['Toolbar'][0][] = $button;
+ }
+ }
+ }
+ }
+ }
+
+ // @todo
+// if (isset($config['block_formats'])) {
+// $settings['DropDowns']['headings']['elements'] = explode(',', $config['block_formats']);
+// }
+
+ return $settings;
+}
+
+/**
+ * Return internal plugins for this editor; semi-implementation of hook_wysiwyg_plugin().
+ */
+function wysiwyg_openwysiwyg_plugins($editor) {
+ $plugins = array(
+ 'default' => array(
+ 'buttons' => array(
+ 'bold' => t('Bold'), 'italic' => t('Italic'), 'underline' => t('Underline'),
+ 'strikethrough' => t('Strike-through'),
+ 'justifyleft' => t('Align left'), 'justifycenter' => t('Align center'), 'justifyright' => t('Align right'), 'justifyfull' => t('Justify'),
+ 'unorderedlist' => t('Bullet list'), 'orderedlist' => t('Numbered list'),
+ 'outdent' => t('Outdent'), 'indent' => t('Indent'),
+ 'undo' => t('Undo'), 'redo' => t('Redo'),
+ 'createlink' => t('Link'),
+ 'insertimage' => t('Image'),
+ 'cleanup' => t('Clean-up'),
+ 'forecolor' => t('Forecolor'), 'backcolor' => t('Backcolor'),
+ 'superscript' => t('Sup'), 'subscript' => t('Sub'),
+ 'blockquote' => t('Blockquote'), 'viewSource' => t('Source code'),
+ 'hr' => t('Horizontal rule'),
+ 'cut' => t('Cut'), 'copy' => t('Copy'), 'paste' => t('Paste'),
+ 'visualaid' => t('Visual aid'),
+ 'removeformat' => t('Remove format'),
+ 'charmap' => t('Character map'),
+ 'headings' => t('HTML block format'), 'font' => t('Font'), 'fontsize' => t('Font size'),
+ 'maximize' => t('Fullscreen'),
+ 'preview' => t('Preview'),
+ 'print' => t('Print'),
+ 'inserttable' => t('Table'),
+ 'help' => t('Help'),
+ ),
+ 'internal' => TRUE,
+ ),
+ );
+ return $plugins;
+}
+
diff --git a/sites/all/modules/contrib/editor/wysiwyg/editors/tinymce.inc b/sites/all/modules/contrib/editor/wysiwyg/editors/tinymce.inc
new file mode 100644
index 00000000..1dbd594c
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/editors/tinymce.inc
@@ -0,0 +1,653 @@
+_alter() to add/inject optional libraries like gzip.
+ */
+function wysiwyg_tinymce_editor() {
+ $editor['tinymce'] = array(
+ 'title' => 'TinyMCE',
+ 'vendor url' => 'http://tinymce.moxiecode.com',
+ 'download url' => 'http://tinymce.moxiecode.com/download.php',
+ 'library path' => wysiwyg_get_path('tinymce') . '/jscripts/tiny_mce',
+ 'libraries' => array(
+ '' => array(
+ 'title' => 'Minified',
+ 'files' => array('tiny_mce.js'),
+ ),
+ 'src' => array(
+ 'title' => 'Source',
+ 'files' => array('tiny_mce_src.js'),
+ ),
+ ),
+ 'version callback' => 'wysiwyg_tinymce_version',
+ 'themes callback' => 'wysiwyg_tinymce_themes',
+ 'init callback' => 'wysiwyg_tinymce_init',
+ 'settings callback' => 'wysiwyg_tinymce_settings',
+ 'plugin callback' => 'wysiwyg_tinymce_plugins',
+ 'plugin settings callback' => 'wysiwyg_tinymce_plugin_settings',
+ 'proxy plugin' => array(
+ 'drupal' => array(
+ 'load' => TRUE,
+ 'proxy' => TRUE,
+ ),
+ ),
+ 'proxy plugin settings callback' => 'wysiwyg_tinymce_proxy_plugin_settings',
+ 'versions' => array(
+ '2.1' => array(
+ 'js files' => array('tinymce-2.js'),
+ 'css files' => array('tinymce-2.css'),
+ 'download url' => 'http://sourceforge.net/project/showfiles.php?group_id=103281&package_id=111430&release_id=557383',
+ ),
+ // @todo Starting from 3.3, tiny_mce.js may support JS aggregation.
+ '3.1' => array(
+ 'js files' => array('tinymce-3.js'),
+ 'css files' => array('tinymce-3.css'),
+ 'libraries' => array(
+ '' => array(
+ 'title' => 'Minified',
+ 'files' => array(
+ 'tiny_mce.js' => array('preprocess' => FALSE),
+ ),
+ ),
+ 'jquery' => array(
+ 'title' => 'jQuery',
+ 'files' => array('tiny_mce_jquery.js'),
+ ),
+ 'src' => array(
+ 'title' => 'Source',
+ 'files' => array('tiny_mce_src.js'),
+ ),
+ ),
+ ),
+ ),
+ );
+ return $editor;
+}
+
+/**
+ * Detect editor version.
+ *
+ * @param $editor
+ * An array containing editor properties as returned from hook_editor().
+ *
+ * @return
+ * The installed editor version.
+ */
+function wysiwyg_tinymce_version($editor) {
+ $script = $editor['library path'] . '/tiny_mce.js';
+ if (!file_exists($script)) {
+ return;
+ }
+ $script = fopen($script, 'r');
+ // Version is contained in the first 200 chars.
+ $line = fgets($script, 200);
+ fclose($script);
+ // 2.x: this.majorVersion="2";this.minorVersion="1.3"
+ // 3.x: majorVersion:'3',minorVersion:'2.0.1'
+ if (preg_match('@majorVersion[=:]["\'](\d).+?minorVersion[=:]["\']([\d\.]+)@', $line, $version)) {
+ return $version[1] . '.' . $version[2];
+ }
+}
+
+/**
+ * Determine available editor themes or check/reset a given one.
+ *
+ * @param $editor
+ * A processed hook_editor() array of editor properties.
+ * @param $profile
+ * A wysiwyg editor profile.
+ *
+ * @return
+ * An array of theme names. The first returned name should be the default
+ * theme name.
+ */
+function wysiwyg_tinymce_themes($editor, $profile) {
+ /*
+ $themes = array();
+ $dir = $editor['library path'] . '/themes/';
+ if (is_dir($dir) && $dh = opendir($dir)) {
+ while (($file = readdir($dh)) !== FALSE) {
+ if (!in_array($file, array('.', '..', 'CVS', '.svn')) && is_dir($dir . $file)) {
+ $themes[$file] = $file;
+ }
+ }
+ closedir($dh);
+ asort($themes);
+ }
+ return $themes;
+ */
+ return array('advanced', 'simple');
+}
+
+/**
+ * Returns an initialization JavaScript for this editor library.
+ *
+ * @param array $editor
+ * The editor library definition.
+ * @param string $library
+ * The library variant key from $editor['libraries'].
+ * @param object $profile
+ * The (first) wysiwyg editor profile.
+ *
+ * @return string
+ * A string containing inline JavaScript to execute before the editor library
+ * script is loaded.
+ */
+function wysiwyg_tinymce_init($editor, $library) {
+ // TinyMCE unconditionally searches for its library filename in SCRIPT tags on
+ // on the page upon loading the library in order to determine the base path to
+ // itself. When JavaScript aggregation is enabled, this search fails and all
+ // relative constructed paths within TinyMCE are broken. The library has a
+ // tinyMCE.baseURL property, but it is not publicly documented and thus not
+ // reliable. The official support forum suggests to solve the issue through
+ // the global window.tinyMCEPreInit variable also used by various serverside
+ // compressor scrips available from the official website.
+ // @see http://www.tinymce.com/forum/viewtopic.php?id=23286
+ $settings = drupal_json_encode(array(
+ 'base' => base_path() . $editor['library path'],
+ 'suffix' => (strpos($library, 'src') !== FALSE || strpos($library, 'dev') !== FALSE ? '_src' : ''),
+ 'query' => '',
+ ));
+ return << TRUE, // @todo Add a setting for this.
+ 'document_base_url' => base_path(),
+ 'mode' => 'none',
+ 'plugins' => array(),
+ #'theme' => $theme,
+ 'theme' => 'advanced',
+ 'skin' => 'o2k7',
+ 'skin_variant' => "silver",
+ 'width' => '100%',
+ // Strict loading mode must be enabled; otherwise TinyMCE would use
+ // document.write() in IE and Chrome.
+ 'strict_loading_mode' => TRUE,
+ // TinyMCE's URL conversion magic breaks Drupal modules that use a special
+ // syntax for paths. This makes 'relative_urls' obsolete.
+ 'convert_urls' => FALSE,
+ // The default entity_encoding ('named') converts too many characters in
+ // languages (like Greek). Since Drupal supports Unicode, we only convert
+ // HTML control characters and invisible characters. TinyMCE always converts
+ // XML default characters '&', '<', '>'.
+ 'entities' => '160,nbsp,173,shy,8194,ensp,8195,emsp,8201,thinsp,8204,zwnj,8205,zwj,8206,lrm,8207,rlm',
+ );
+ if (isset($config['apply_source_formatting'])) {
+ $settings['apply_source_formatting'] = $config['apply_source_formatting'];
+ }
+ if (isset($config['convert_fonts_to_spans'])) {
+ $settings['convert_fonts_to_spans'] = $config['convert_fonts_to_spans'];
+ }
+ if (isset($config['language'])) {
+ $settings['language'] = $config['language'];
+ }
+ if (isset($config['paste_auto_cleanup_on_paste'])) {
+ $settings['paste_auto_cleanup_on_paste'] = $config['paste_auto_cleanup_on_paste'];
+ }
+ if (isset($config['preformatted'])) {
+ $settings['preformatted'] = $config['preformatted'];
+ }
+ if (isset($config['remove_linebreaks'])) {
+ $settings['remove_linebreaks'] = $config['remove_linebreaks'];
+ }
+ if (isset($config['verify_html'])) {
+ // TinyMCE performs a type-agnostic comparison on this particular setting.
+ $settings['verify_html'] = (bool) $config['verify_html'];
+ }
+
+ if (!empty($config['css_classes'])) {
+ $settings['theme_advanced_styles'] = implode(';', array_filter(explode("\n", str_replace("\r", '', $config['css_classes']))));
+ }
+
+ if (isset($config['css_setting'])) {
+ if ($config['css_setting'] == 'theme') {
+ $settings['content_css'] = implode(',', wysiwyg_get_css());
+ }
+ elseif ($config['css_setting'] == 'self' && isset($config['css_path'])) {
+ $settings['content_css'] = strtr($config['css_path'], array('%b' => base_path(), '%t' => drupal_get_path('theme', variable_get('theme_default', NULL))));
+ }
+ }
+
+ // Find the enabled buttons and the button row they belong on.
+ // Also map the plugin metadata for each button.
+ // @todo What follows is a pain; needs a rewrite.
+ // $settings['buttons'] are stacked into $settings['theme_advanced_buttons1']
+ // later.
+ $settings['buttons'] = array();
+ if (!empty($config['buttons']) && is_array($config['buttons'])) {
+ // Only array keys in $settings['extensions'] matter; added to
+ // $settings['plugins'] later.
+ $settings['extensions'] = array();
+ // $settings['extended_valid_elements'] are just stacked, unique'd later,
+ // and transformed into a comma-separated string in
+ // wysiwyg_add_editor_settings().
+ // @todo Needs a complete plugin API redesign using arrays for
+ // tag => attributes definitions and array_merge_recursive().
+ $settings['extended_valid_elements'] = array();
+
+ $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 buttons.
+ if ($type == 'buttons') {
+ $settings['buttons'][] = $button;
+ }
+ // Add external Drupal plugins to the list of extensions.
+ if ($type == 'buttons' && !empty($plugins[$plugin]['proxy'])) {
+ $settings['extensions'][_wysiwyg_tinymce_plugin_name('add', $button)] = 1;
+ }
+ // Add external plugins to the list of extensions.
+ elseif ($type == 'buttons' && empty($plugins[$plugin]['internal'])) {
+ $settings['extensions'][_wysiwyg_tinymce_plugin_name('add', $plugin)] = 1;
+ }
+ // Add internal buttons that also need to be loaded as extension.
+ elseif ($type == 'buttons' && !empty($plugins[$plugin]['load'])) {
+ $settings['extensions'][$plugin] = 1;
+ }
+ // Add plain extensions.
+ elseif ($type == 'extensions' && !empty($plugins[$plugin]['load'])) {
+ $settings['extensions'][$plugin] = 1;
+ }
+ // Allow plugins to add valid HTML elements.
+ if (!empty($plugins[$plugin]['extended_valid_elements'])) {
+ $settings['extended_valid_elements'] = array_merge($settings['extended_valid_elements'], $plugins[$plugin]['extended_valid_elements']);
+ }
+ // Allow plugins to add or override global configuration settings.
+ if (!empty($plugins[$plugin]['options'])) {
+ $settings = array_merge($settings, $plugins[$plugin]['options']);
+ }
+ }
+ }
+ }
+ // Clean-up.
+ $settings['extended_valid_elements'] = array_unique($settings['extended_valid_elements']);
+ if ($settings['extensions']) {
+ $settings['plugins'] = array_keys($settings['extensions']);
+ }
+ unset($settings['extensions']);
+ }
+
+ // Add theme-specific settings.
+ switch ($theme) {
+ case 'advanced':
+ $settings += array(
+ 'theme_advanced_resize_horizontal' => FALSE,
+ 'theme_advanced_resizing_use_cookie' => FALSE,
+ 'theme_advanced_statusbar_location' => isset($config['path_loc']) ? $config['path_loc'] : 'bottom',
+ 'theme_advanced_resizing' => isset($config['resizing']) ? $config['resizing'] : 1,
+ 'theme_advanced_toolbar_location' => isset($config['toolbar_loc']) ? $config['toolbar_loc'] : 'top',
+ 'theme_advanced_toolbar_align' => isset($config['toolbar_align']) ? $config['toolbar_align'] : 'left',
+ );
+ if (isset($config['block_formats'])) {
+ $settings['theme_advanced_blockformats'] = $config['block_formats'];
+ }
+ if (isset($settings['buttons'])) {
+ // These rows explicitly need to be set to be empty, otherwise TinyMCE
+ // loads its default buttons of the advanced theme for each row.
+ $settings += array(
+ 'theme_advanced_buttons1' => array(),
+ 'theme_advanced_buttons2' => array(),
+ 'theme_advanced_buttons3' => array(),
+ );
+ // @todo Allow to sort/arrange editor buttons.
+ for ($i = 0; $i < count($settings['buttons']); $i++) {
+ $settings['theme_advanced_buttons1'][] = $settings['buttons'][$i];
+ }
+ }
+ break;
+ }
+ unset($settings['buttons']);
+
+ // Convert the config values into the form expected by TinyMCE.
+ $csv_settings = array('plugins', 'extended_valid_elements', 'theme_advanced_buttons1', 'theme_advanced_buttons2', 'theme_advanced_buttons3');
+ foreach ($csv_settings as $key) {
+ if (isset($settings[$key]) && is_array($settings[$key])) {
+ $settings[$key] = implode(',', $settings[$key]);
+ }
+ }
+
+ return $settings;
+}
+
+/**
+ * Build a JS settings array of native external plugins that need to be loaded separately.
+ *
+ * TinyMCE requires that external plugins (i.e. not residing in the editor's
+ * directory) are loaded (once) upon initializing the editor.
+ */
+function wysiwyg_tinymce_plugin_settings($editor, $profile, $plugins) {
+ $settings = array();
+ foreach ($plugins as $name => $plugin) {
+ if (!empty($plugin['load'])) {
+ // Add path for native external plugins; internal ones are loaded
+ // automatically.
+ if (empty($plugin['internal']) && isset($plugin['filename'])) {
+ $settings[$name] = base_path() . $plugin['path'] . '/' . $plugin['filename'];
+ }
+ }
+ }
+ return $settings;
+}
+
+/**
+ * Build a JS settings array for Drupal plugins loaded via the proxy plugin.
+ */
+function wysiwyg_tinymce_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'],
+ );
+ if (isset($plugin['css file'])) {
+ $settings[$name]['css'] = base_path() . $plugin['css path'] . '/' . $plugin['css file'];
+ }
+ }
+ return $settings;
+}
+
+/**
+ * Add or remove leading hiven to/of external plugin names.
+ *
+ * TinyMCE requires that external plugins, which should not be loaded from
+ * its own plugin repository are prefixed with a hiven in the name.
+ *
+ * @param string $op
+ * Operation to perform, 'add' or 'remove' (hiven).
+ * @param string $name
+ * A plugin name.
+ */
+function _wysiwyg_tinymce_plugin_name($op, $name) {
+ if ($op == 'add') {
+ if (strpos($name, '-') !== 0) {
+ return '-' . $name;
+ }
+ return $name;
+ }
+ elseif ($op == 'remove') {
+ if (strpos($name, '-') === 0) {
+ return substr($name, 1);
+ }
+ return $name;
+ }
+}
+
+/**
+ * Return internal plugins for this editor; semi-implementation of hook_wysiwyg_plugin().
+ */
+function wysiwyg_tinymce_plugins($editor) {
+ $plugins = array(
+ 'default' => array(
+ 'path' => $editor['library path'] . '/themes/advanced',
+ 'buttons' => array(
+ 'bold' => t('Bold'), 'italic' => t('Italic'), 'underline' => t('Underline'),
+ 'strikethrough' => t('Strike-through'),
+ 'justifyleft' => t('Align left'), 'justifycenter' => t('Align center'), 'justifyright' => t('Align right'), 'justifyfull' => t('Justify'),
+ 'bullist' => t('Bullet list'), 'numlist' => t('Numbered list'),
+ 'outdent' => t('Outdent'), 'indent' => t('Indent'),
+ 'undo' => t('Undo'), 'redo' => t('Redo'),
+ 'link' => t('Link'), 'unlink' => t('Unlink'), 'anchor' => t('Anchor'),
+ 'image' => t('Image'),
+ 'cleanup' => t('Clean-up'),
+ 'formatselect' => t('Block format'), 'styleselect' => t('Styles'),
+ 'fontselect' => t('Font'), 'fontsizeselect' => t('Font size'),
+ 'forecolor' => t('Forecolor'), 'backcolor' => t('Backcolor'),
+ 'sup' => t('Superscript'), 'sub' => t('Subscript'),
+ 'blockquote' => t('Blockquote'), 'code' => t('Source code'),
+ 'hr' => t('Horizontal rule'),
+ 'cut' => t('Cut'), 'copy' => t('Copy'), 'paste' => t('Paste'),
+ 'visualaid' => t('Visual aid'),
+ 'removeformat' => t('Remove format'),
+ 'charmap' => t('Character map'),
+ 'help' => t('Help'),
+ ),
+ 'internal' => TRUE,
+ ),
+ 'advhr' => array(
+ 'path' => $editor['library path'] . '/plugins/advhr',
+ 'buttons' => array('advhr' => t('Advanced horizontal rule')),
+ 'extended_valid_elements' => array('hr[class|width|size|noshade]'),
+ 'url' => 'http://www.tinymce.com/wiki.php/Plugin:advhr',
+ 'internal' => TRUE,
+ 'load' => TRUE,
+ ),
+ 'advimage' => array(
+ 'path' => $editor['library path'] . '/plugins/advimage',
+ 'extensions' => array('advimage' => t('Advanced image')),
+ 'extended_valid_elements' => array('img[src|alt|title|align|width|height|usemap|hspace|vspace|border|style|class|onmouseover|onmouseout|id|name|longdesc]'),
+ 'url' => 'http://www.tinymce.com/wiki.php/Plugin:advimage',
+ 'internal' => TRUE,
+ 'load' => TRUE,
+ ),
+ 'advlink' => array(
+ 'path' => $editor['library path'] . '/plugins/advlink',
+ 'extensions' => array('advlink' => t('Advanced link')),
+ 'extended_valid_elements' => array('a[name|href|target|title|class|onfocus|onblur|onclick|ondlbclick|onmousedown|onmouseup|onmouseover|onmouseout|onkeypress|onkeydown|onkeyup|id|style|rel]'),
+ 'url' => 'http://www.tinymce.com/wiki.php/Plugin:advlink',
+ 'internal' => TRUE,
+ 'load' => TRUE,
+ ),
+ 'autosave' => array(
+ 'path' => $editor['library path'] . '/plugins/autosave',
+ 'extensions' => array('autosave' => t('Auto save')),
+ 'url' => 'http://www.tinymce.com/wiki.php/Plugin:autosave',
+ 'internal' => TRUE,
+ 'load' => TRUE,
+ ),
+ 'contextmenu' => array(
+ 'path' => $editor['library path'] . '/plugins/contextmenu',
+ 'extensions' => array('contextmenu' => t('Context menu')),
+ 'url' => 'http://www.tinymce.com/wiki.php/Plugin:contextmenu',
+ 'internal' => TRUE,
+ 'load' => TRUE,
+ ),
+ 'directionality' => array(
+ 'path' => $editor['library path'] . '/plugins/directionality',
+ 'buttons' => array('ltr' => t('Left-to-right'), 'rtl' => t('Right-to-left')),
+ 'url' => 'http://www.tinymce.com/wiki.php/Plugin:directionality',
+ 'internal' => TRUE,
+ 'load' => TRUE,
+ ),
+ 'emotions' => array(
+ 'path' => $editor['library path'] . '/plugins/emotions',
+ 'buttons' => array('emotions' => t('Emotions')),
+ 'url' => 'http://www.tinymce.com/wiki.php/Plugin:emotions',
+ 'internal' => TRUE,
+ 'load' => TRUE,
+ ),
+ 'fullscreen' => array(
+ 'path' => $editor['library path'] . '/plugins/fullscreen',
+ 'buttons' => array('fullscreen' => t('Fullscreen')),
+ 'url' => 'http://www.tinymce.com/wiki.php/Plugin:fullscreen',
+ 'internal' => TRUE,
+ 'load' => TRUE,
+ ),
+ 'inlinepopups' => array(
+ 'path' => $editor['library path'] . '/plugins/inlinepopups',
+ 'extensions' => array('inlinepopups' => t('Inline popups')),
+ 'options' => array(
+ 'dialog_type' => array('modal'),
+ ),
+ 'url' => 'http://www.tinymce.com/wiki.php/Plugin:inlinepopups',
+ 'internal' => TRUE,
+ 'load' => TRUE,
+ ),
+ 'insertdatetime' => array(
+ 'path' => $editor['library path'] . '/plugins/insertdatetime',
+ 'buttons' => array('insertdate' => t('Insert date'), 'inserttime' => t('Insert time')),
+ 'options' => array(
+ 'plugin_insertdate_dateFormat' => '%Y-%m-%d',
+ 'plugin_insertdate_timeFormat' => '%H:%M:%S',
+ ),
+ 'url' => 'http://www.tinymce.com/wiki.php/Plugin:insertdatetime',
+ 'internal' => TRUE,
+ 'load' => TRUE,
+ ),
+ 'layer' => array(
+ 'path' => $editor['library path'] . '/plugins/layer',
+ 'buttons' => array('insertlayer' => t('Insert layer'), 'moveforward' => t('Move forward'), 'movebackward' => t('Move backward'), 'absolute' => t('Absolute')),
+ 'url' => 'http://www.tinymce.com/wiki.php/Plugin:layer',
+ 'internal' => TRUE,
+ 'load' => TRUE,
+ ),
+ 'paste' => array(
+ 'path' => $editor['library path'] . '/plugins/paste',
+ 'buttons' => array('pastetext' => t('Paste text'), 'pasteword' => t('Paste from Word'), 'selectall' => t('Select all')),
+ 'url' => 'http://www.tinymce.com/wiki.php/Plugin:paste',
+ 'internal' => TRUE,
+ 'load' => TRUE,
+ ),
+ 'preview' => array(
+ 'path' => $editor['library path'] . '/plugins/preview',
+ 'buttons' => array('preview' => t('Preview')),
+ 'url' => 'http://www.tinymce.com/wiki.php/Plugin:preview',
+ 'internal' => TRUE,
+ 'load' => TRUE,
+ ),
+ 'print' => array(
+ 'path' => $editor['library path'] . '/plugins/print',
+ 'buttons' => array('print' => t('Print')),
+ 'url' => 'http://www.tinymce.com/wiki.php/Plugin:print',
+ 'internal' => TRUE,
+ 'load' => TRUE,
+ ),
+ 'searchreplace' => array(
+ 'path' => $editor['library path'] . '/plugins/searchreplace',
+ 'buttons' => array('search' => t('Search'), 'replace' => t('Replace')),
+ 'url' => 'http://www.tinymce.com/wiki.php/Plugin:searchreplace',
+ 'internal' => TRUE,
+ 'load' => TRUE,
+ ),
+ 'style' => array(
+ 'path' => $editor['library path'] . '/plugins/style',
+ 'buttons' => array('styleprops' => t('Advanced CSS styles')),
+ 'url' => 'http://www.tinymce.com/wiki.php/Plugin:style',
+ 'internal' => TRUE,
+ 'load' => TRUE,
+ ),
+ 'table' => array(
+ 'path' => $editor['library path'] . '/plugins/table',
+ 'buttons' => array('tablecontrols' => t('Table')),
+ 'url' => 'http://www.tinymce.com/wiki.php/Plugin:table',
+ 'internal' => TRUE,
+ 'load' => TRUE,
+ ),
+ );
+ if (version_compare($editor['installed version'], '3', '<')) {
+ $plugins['flash'] = array(
+ 'path' => $editor['library path'] . '/plugins/flash',
+ 'buttons' => array('flash' => t('Flash')),
+ 'extended_valid_elements' => array('img[class|src|alt|title|hspace|vspace|width|height|align|onmouseover|onmouseout|name|obj|param|embed]'),
+ 'internal' => TRUE,
+ 'load' => TRUE,
+ );
+ }
+ if (version_compare($editor['installed version'], '2.0.6', '>')) {
+ $plugins['media'] = array(
+ 'path' => $editor['library path'] . '/plugins/media',
+ 'buttons' => array('media' => t('Media')),
+ 'url' => 'http://www.tinymce.com/wiki.php/Plugin:media',
+ 'internal' => TRUE,
+ 'load' => TRUE,
+ );
+ $plugins['xhtmlxtras'] = array(
+ 'path' => $editor['library path'] . '/plugins/xhtmlxtras',
+ 'buttons' => array('cite' => t('Citation'), 'del' => t('Deleted'), 'abbr' => t('Abbreviation'), 'acronym' => t('Acronym'), 'ins' => t('Inserted'), 'attribs' => t('HTML attributes')),
+ 'url' => 'http://www.tinymce.com/wiki.php/Plugin:xhtmlxtras',
+ 'internal' => TRUE,
+ 'load' => TRUE,
+ );
+ }
+ if (version_compare($editor['installed version'], '3', '>')) {
+ $plugins['bbcode'] = array(
+ 'path' => $editor['library path'] . '/plugins/bbcode',
+ 'extensions' => array('bbcode' => t('BBCode')),
+ 'url' => 'http://www.tinymce.com/wiki.php/Plugin:bbcode',
+ 'internal' => TRUE,
+ 'load' => TRUE,
+ );
+ if (version_compare($editor['installed version'], '3.3', '<')) {
+ $plugins['safari'] = array(
+ 'path' => $editor['library path'] . '/plugins/safari',
+ 'extensions' => array('safari' => t('Safari compatibility')),
+ 'internal' => TRUE,
+ 'load' => TRUE,
+ );
+ }
+ }
+ if (version_compare($editor['installed version'], '3.2.5', '>=')) {
+ $plugins['autoresize'] = array(
+ 'path' => $editor['library path'] . '/plugins/autoresize',
+ 'extensions' => array('autoresize' => t('Auto resize')),
+ 'url' => 'http://www.tinymce.com/wiki.php/Plugin:autoresize',
+ 'internal' => TRUE,
+ 'load' => TRUE,
+ );
+ }
+ if (version_compare($editor['installed version'], '3.3', '>=')) {
+ $plugins['advlist'] = array(
+ 'path' => $editor['library path'] . '/plugins/advlist',
+ 'extensions' => array('advlist' => t('Advanced list')),
+ 'url' => 'http://www.tinymce.com/wiki.php/Plugin:advlist',
+ 'internal' => TRUE,
+ 'load' => TRUE,
+ );
+ }
+ if (version_compare($editor['installed version'], '3.2.6', '>=')) {
+ $plugins['wordcount'] = array(
+ 'path' => $editor['library path'] . '/plugins/wordcount',
+ 'extensions' => array('wordcount' => t('Word count')),
+ 'internal' => TRUE,
+ 'load' => TRUE,
+ );
+ }
+ if (version_compare($editor['installed version'], '3.4.1', '>=')) {
+ $plugins['lists'] = array(
+ 'path' => $editor['library path'] . 'plugins/lists',
+ 'extensions' => array('lists' => t('List normalizer')),
+ 'url' => 'http://www.tinymce.com/wiki.php/Plugin:lists',
+ 'internal' => TRUE,
+ 'load' => TRUE,
+ 'extended_valid_elements' => array(
+ 'li[class|dir|id|lang|onclick|ondblclick|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|style|title|type|value]',
+ 'ol[class|compact|dir|id|lang|onclick|ondblclick|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|start|style|title|type]',
+ 'ul[class|compact|dir|id|lang|onclick|ondblclick|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|style|title|type]',
+ ),
+ );
+ }
+ return $plugins;
+}
+
diff --git a/sites/all/modules/contrib/editor/wysiwyg/editors/whizzywig.inc b/sites/all/modules/contrib/editor/wysiwyg/editors/whizzywig.inc
new file mode 100644
index 00000000..acfc7de3
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/editors/whizzywig.inc
@@ -0,0 +1,147 @@
+ 'Whizzywig',
+ 'vendor url' => 'http://www.unverse.net',
+ 'download url' => 'http://www.unverse.net/whizzywig-download.html',
+ 'libraries' => array(
+ '' => array(
+ 'title' => 'Default',
+ 'files' => array('whizzywig.js', 'xhtml.js'),
+ ),
+ ),
+ 'version callback' => 'wysiwyg_whizzywig_version',
+ 'settings callback' => 'wysiwyg_whizzywig_settings',
+ 'plugin callback' => 'wysiwyg_whizzywig_plugins',
+ 'versions' => array(
+ '55' => array(
+ 'js files' => array('whizzywig.js'),
+ ),
+ '56' => array(
+ 'js files' => array('whizzywig-56.js'),
+ ),
+ '60' => array(
+ 'js files' => array('whizzywig-60.js'),
+ ),
+ ),
+ );
+ return $editor;
+}
+
+/**
+ * Detect editor version.
+ *
+ * @param $editor
+ * An array containing editor properties as returned from hook_editor().
+ *
+ * @return
+ * The installed editor version.
+ */
+function wysiwyg_whizzywig_version($editor) {
+ $script = $editor['library path'] . '/whizzywig.js';
+ if (!file_exists($script)) {
+ return;
+ }
+ $script = fopen($script, 'r');
+ $line = fgets($script, 43);
+ // 55: Whizzywig v55i
+ // 60: Whizzywig 60
+ if (preg_match('@Whizzywig v?([0-9]+)@', $line, $version)) {
+ fclose($script);
+ return $version[1];
+ }
+ 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_whizzywig_settings($editor, $config, $theme) {
+ $settings = array();
+
+ // Add path to button images, if available.
+ if (is_dir($editor['library path'] . '/btn')) {
+ $settings['buttonPath'] = base_path() . $editor['library path'] . '/btn/';
+ }
+ if (file_exists($editor['library path'] . '/WhizzywigToolbar.png')) {
+ $settings['toolbarImagePath'] = base_path() . $editor['library path'] . '/WhizzywigToolbar.png';
+ }
+ // Filename changed in version 60.
+ elseif (file_exists($editor['library path'] . '/icons.png')) {
+ $settings['toolbarImagePath'] = base_path() . $editor['library path'] . '/icons.png';
+ }
+
+ // Add configured buttons or all available.
+ $settings['buttons'] = array();
+ if (!empty($config['buttons'])) {
+ $buttons = array();
+ foreach ($config['buttons'] as $plugin) {
+ $buttons = array_merge($buttons, $plugin);
+ }
+ $settings['buttons'] = implode(' ', array_keys($buttons));
+ }
+
+ // Add editor content stylesheet.
+ if (isset($config['css_setting'])) {
+ if ($config['css_setting'] == 'theme') {
+ $css = drupal_get_path('theme', variable_get('theme_default', NULL)) . '/style.css';
+ if (file_exists($css)) {
+ $settings['externalCSS'] = base_path() . $css;
+ }
+ }
+ elseif ($config['css_setting'] == 'self' && isset($config['css_path'])) {
+ $settings['externalCSS'] = strtr($config['css_path'], array('%b' => base_path(), '%t' => drupal_get_path('theme', variable_get('theme_default', NULL))));
+ }
+ }
+
+ return $settings;
+}
+
+/**
+ * Return internal plugins for this editor; semi-implementation of hook_wysiwyg_plugin().
+ */
+function wysiwyg_whizzywig_plugins($editor) {
+ return array(
+ 'default' => array(
+ 'buttons' => array(
+ 'formatblock' => t('HTML block format'), 'fontname' => t('Font'), 'fontsize' => t('Font size'),
+ 'bold' => t('Bold'), 'italic' => t('Italic'), 'underline' => t('Underline'),
+ 'left' => t('Align left'), 'center' => t('Align center'), 'right' => t('Align right'),
+ 'bullet' => t('Bullet list'), 'number' => t('Numbered list'),
+ 'outdent' => t('Outdent'), 'indent' => t('Indent'),
+ 'undo' => t('Undo'), 'redo' => t('Redo'),
+ 'image' => t('Image'),
+ 'color' => t('Forecolor'), 'hilite' => t('Backcolor'),
+ 'rule' => t('Horizontal rule'),
+ 'link' => t('Link'),
+ 'image' => t('Image'),
+ 'table' => t('Table'),
+ 'clean' => t('Clean-up'),
+ 'html' => t('Source code'),
+ 'spellcheck' => t('Spell check'),
+ ),
+ 'internal' => TRUE,
+ ),
+ );
+}
+
diff --git a/sites/all/modules/contrib/editor/wysiwyg/editors/wymeditor.inc b/sites/all/modules/contrib/editor/wysiwyg/editors/wymeditor.inc
new file mode 100644
index 00000000..5f44d641
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/editors/wymeditor.inc
@@ -0,0 +1,234 @@
+ 'WYMeditor',
+ 'vendor url' => 'http://www.wymeditor.org/',
+ 'download url' => 'http://www.wymeditor.org/download/',
+ 'library path' => wysiwyg_get_path('wymeditor') . '/wymeditor',
+ 'libraries' => array(
+ 'min' => array(
+ 'title' => 'Minified',
+ 'files' => array('jquery.wymeditor.min.js'),
+ ),
+ 'pack' => array(
+ 'title' => 'Packed',
+ 'files' => array('jquery.wymeditor.pack.js'),
+ ),
+ 'src' => array(
+ 'title' => 'Source',
+ 'files' => array('jquery.wymeditor.js'),
+ ),
+ ),
+ 'version callback' => 'wysiwyg_wymeditor_version',
+ 'themes callback' => 'wysiwyg_wymeditor_themes',
+ 'settings callback' => 'wysiwyg_wymeditor_settings',
+ 'plugin callback' => 'wysiwyg_wymeditor_plugins',
+ 'versions' => array(
+ '0.5-rc1' => array(
+ 'js files' => array('wymeditor.js'),
+ ),
+ ),
+ );
+ return $editor;
+}
+
+/**
+ * Detect editor version.
+ *
+ * @param $editor
+ * An array containing editor properties as returned from hook_editor().
+ *
+ * @return
+ * The installed editor version.
+ */
+function wysiwyg_wymeditor_version($editor) {
+ $script = $editor['library path'] . '/jquery.wymeditor.js';
+ if (!file_exists($script)) {
+ return;
+ }
+ $script = fopen($script, 'r');
+ fgets($script);
+ $line = fgets($script);
+ if (preg_match('@version\s+([0-9a-z\.-]+)@', $line, $version)) {
+ fclose($script);
+ return $version[1];
+ }
+ fclose($script);
+}
+
+/**
+ * Determine available editor themes or check/reset a given one.
+ *
+ * @param $editor
+ * A processed hook_editor() array of editor properties.
+ * @param $profile
+ * A wysiwyg editor profile.
+ *
+ * @return
+ * An array of theme names. The first returned name should be the default
+ * theme name.
+ */
+function wysiwyg_wymeditor_themes($editor, $profile) {
+ return array('compact', 'default', 'minimal', 'silver', 'twopanels');
+}
+
+/**
+ * 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_wymeditor_settings($editor, $config, $theme) {
+ // @todo Setup $library in wysiwyg_load_editor() already.
+ $library = (isset($editor['library']) ? $editor['library'] : key($editor['libraries']));
+ $settings = array(
+ 'basePath' => base_path() . $editor['library path'] . '/',
+ 'wymPath' => $editor['libraries'][$library]['files'][0],
+ // @todo Does not work in Drupal; jQuery can live anywhere.
+ 'jQueryPath' => base_path() . 'misc/jquery.js',
+ 'updateSelector' => '.form-submit',
+ 'skin' => $theme,
+ );
+
+ if (isset($config['language'])) {
+ $settings['lang'] = $config['language'];
+ }
+
+ // Add configured buttons.
+ $settings['toolsItems'] = array();
+ if (!empty($config['buttons'])) {
+ $buttoninfo = _wysiwyg_wymeditor_button_info();
+ $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 buttons.
+ if ($type == 'buttons') {
+ // Merge meta-data for internal default buttons.
+ if (isset($buttoninfo[$button])) {
+ $buttoninfo[$button] += array('name' => $button);
+ $settings['toolsItems'][] = $buttoninfo[$button];
+ }
+ // For custom buttons, try to provide a valid button definition.
+ else {
+ $settings['toolsItems'][] = array(
+ 'name' => $button,
+ 'title' => $plugins[$plugin][$type][$button],
+ 'css' => 'wym_tools_' . $button,
+ );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (!empty($config['block_formats'])) {
+ $containers = array(
+ 'p' => 'Paragraph',
+ 'h1' => 'Heading_1',
+ 'h2' => 'Heading_2',
+ 'h3' => 'Heading_3',
+ 'h4' => 'Heading_4',
+ 'h5' => 'Heading_5',
+ 'h6' => 'Heading_6',
+ 'pre' => 'Preformatted',
+ 'blockquote' => 'Blockquote',
+ 'th' => 'Table_Header',
+ );
+ foreach (explode(',', $config['block_formats']) as $tag) {
+ if (isset($containers[$tag])) {
+ $settings['containersItems'][] = array(
+ 'name' => strtoupper($tag),
+ 'title' => $containers[$tag],
+ 'css' => 'wym_containers_' . $tag,
+ );
+ }
+ }
+ }
+
+ if (isset($config['css_setting'])) {
+ if ($config['css_setting'] == 'theme') {
+ // WYMeditor only supports one CSS file currently.
+ $css = wysiwyg_get_css();
+ $settings['stylesheet'] = reset($css);
+ }
+ elseif ($config['css_setting'] == 'self' && isset($config['css_path'])) {
+ $settings['stylesheet'] = strtr($config['css_path'], array('%b' => base_path(), '%t' => drupal_get_path('theme', variable_get('theme_default', NULL))));
+ }
+ }
+
+ return $settings;
+}
+
+/**
+ * Return internal plugins for this editor; semi-implementation of hook_wysiwyg_plugin().
+ */
+function wysiwyg_wymeditor_plugins($editor) {
+ $plugins = array(
+ 'default' => array(
+ 'buttons' => array(
+ 'Bold' => t('Bold'), 'Italic' => t('Italic'),
+ 'InsertOrderedList' => t('Numbered list'), 'InsertUnorderedList' => t('Bullet list'),
+ 'Outdent' => t('Outdent'), 'Indent' => t('Indent'),
+ 'Undo' => t('Undo'), 'Redo' => t('Redo'),
+ 'CreateLink' => t('Link'), 'Unlink' => t('Unlink'),
+ 'InsertImage' => t('Image'),
+ 'Superscript' => t('Superscript'), 'Subscript' => t('Subscript'),
+ 'ToggleHtml' => t('Source code'),
+ 'Paste' => t('Paste'),
+ 'InsertTable' => t('Table'),
+ 'Preview' => t('Preview'),
+ ),
+ 'internal' => TRUE,
+ ),
+ );
+ return $plugins;
+}
+
+/**
+ * Helper function to provide additional meta-data for internal default buttons.
+ */
+function _wysiwyg_wymeditor_button_info() {
+ return array(
+ 'Bold' => array('title' => 'Strong', 'css' => 'wym_tools_strong'),
+ 'Italic' => array('title' => 'Emphasis', 'css' => 'wym_tools_emphasis'),
+ 'Superscript' => array('title' => 'Superscript', 'css' => 'wym_tools_superscript'),
+ 'Subscript' => array('title' => 'Subscript', 'css' => 'wym_tools_subscript'),
+ 'InsertOrderedList' => array('title' => 'Ordered_List', 'css' => 'wym_tools_ordered_list'),
+ 'InsertUnorderedList' => array('title' => 'Unordered_List', 'css' => 'wym_tools_unordered_list'),
+ 'Indent' => array('title' => 'Indent', 'css' => 'wym_tools_indent'),
+ 'Outdent' => array('title' => 'Outdent', 'css' => 'wym_tools_outdent'),
+ 'Undo' => array('title' => 'Undo', 'css' => 'wym_tools_undo'),
+ 'Redo' => array('title' => 'Redo', 'css' => 'wym_tools_redo'),
+ 'CreateLink' => array('title' => 'Link', 'css' => 'wym_tools_link'),
+ 'Unlink' => array('title' => 'Unlink', 'css' => 'wym_tools_unlink'),
+ 'InsertImage' => array('title' => 'Image', 'css' => 'wym_tools_image'),
+ 'InsertTable' => array('title' => 'Table', 'css' => 'wym_tools_table'),
+ 'Paste' => array('title' => 'Paste_From_Word', 'css' => 'wym_tools_paste'),
+ 'ToggleHtml' => array('title' => 'HTML', 'css' => 'wym_tools_html'),
+ 'Preview' => array('title' => 'Preview', 'css' => 'wym_tools_preview'),
+ );
+}
diff --git a/sites/all/modules/contrib/editor/wysiwyg/editors/yui.inc b/sites/all/modules/contrib/editor/wysiwyg/editors/yui.inc
new file mode 100644
index 00000000..36d0a596
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/editors/yui.inc
@@ -0,0 +1,339 @@
+ 'YUI editor',
+ 'vendor url' => 'http://developer.yahoo.com/yui/editor/',
+ 'download url' => 'http://developer.yahoo.com/yui/download/',
+ 'library path' => wysiwyg_get_path('yui') . '/build',
+ 'libraries' => array(
+ 'min' => array(
+ 'title' => 'Minified',
+ 'files' => array(
+ 'yahoo-dom-event/yahoo-dom-event.js',
+ 'animation/animation-min.js',
+ 'element/element-min.js',
+ 'container/container-min.js',
+ 'menu/menu-min.js',
+ 'button/button-min.js',
+ 'editor/editor-min.js',
+ ),
+ ),
+ 'src' => array(
+ 'title' => 'Source',
+ 'files' => array(
+ 'yahoo-dom-event/yahoo-dom-event.js',
+ 'animation/animation.js',
+ 'element/element.js',
+ 'container/container.js',
+ 'menu/menu.js',
+ 'button/button.js',
+ 'editor/editor.js',
+ ),
+ ),
+ ),
+ 'version callback' => 'wysiwyg_yui_version',
+ 'themes callback' => 'wysiwyg_yui_themes',
+ 'load callback' => 'wysiwyg_yui_load',
+ 'settings callback' => 'wysiwyg_yui_settings',
+ 'plugin callback' => 'wysiwyg_yui_plugins',
+ 'plugin settings callback' => 'wysiwyg_yui_plugin_settings',
+ 'proxy plugin' => array(
+ 'drupal' => array(
+ 'load' => TRUE,
+ 'proxy' => TRUE,
+ ),
+ ),
+ 'proxy plugin settings callback' => 'wysiwyg_yui_proxy_plugin_settings',
+ 'versions' => array(
+ '2.7.0' => array(
+ 'js files' => array('yui.js'),
+ ),
+ ),
+ );
+ return $editor;
+}
+
+/**
+ * Detect editor version.
+ *
+ * @param $editor
+ * An array containing editor properties as returned from hook_editor().
+ *
+ * @return
+ * The installed editor version.
+ */
+function wysiwyg_yui_version($editor) {
+ $library = $editor['library path'] . '/editor/editor.js';
+ if (!file_exists($library)) {
+ return;
+ }
+ $library = fopen($library, 'r');
+ $max_lines = 10;
+ while ($max_lines && $line = fgets($library, 60)) {
+ if (preg_match('@version:\s([0-9\.]+)@', $line, $version)) {
+ fclose($library);
+ return $version[1];
+ }
+ $max_lines--;
+ }
+ fclose($library);
+}
+
+/**
+ * Determine available editor themes or check/reset a given one.
+ *
+ * @param $editor
+ * A processed hook_editor() array of editor properties.
+ * @param $profile
+ * A wysiwyg editor profile.
+ *
+ * @return
+ * An array of theme names. The first returned name should be the default
+ * theme name.
+ */
+function wysiwyg_yui_themes($editor, $profile) {
+ return array('sam');
+}
+
+/**
+ * Perform additional actions upon loading this editor.
+ *
+ * @param $editor
+ * A processed hook_editor() array of editor properties.
+ * @param $library
+ * The internal library name (array key) to use.
+ */
+function wysiwyg_yui_load($editor, $library) {
+ drupal_add_css($editor['library path'] . '/menu/assets/skins/sam/menu.css');
+ drupal_add_css($editor['library path'] . '/button/assets/skins/sam/button.css');
+ drupal_add_css($editor['library path'] . '/fonts/fonts-min.css');
+ drupal_add_css($editor['library path'] . '/container/assets/skins/sam/container.css');
+ drupal_add_css($editor['library path'] . '/editor/assets/skins/sam/editor.css');
+}
+
+/**
+ * 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_yui_settings($editor, $config, $theme) {
+ $settings = array(
+ 'theme' => $theme,
+ 'animate' => TRUE,
+ 'handleSubmit' => TRUE,
+ 'markup' => 'xhtml',
+ 'ptags' => TRUE,
+ );
+
+ if (isset($config['path_loc']) && $config['path_loc'] != 'none') {
+ $settings['dompath'] = $config['path_loc'];
+ }
+ // Enable auto-height feature when editor should be resizable.
+ if (!empty($config['resizing'])) {
+ $settings['autoHeight'] = TRUE;
+ }
+
+ $settings += array(
+ 'toolbar' => array(
+ 'collapse' => FALSE,
+ 'draggable' => TRUE,
+ 'buttonType' => 'advanced',
+ 'buttons' => array(),
+ ),
+ );
+ if (!empty($config['buttons'])) {
+ $buttons = array();
+ foreach ($config['buttons'] as $plugin => $enabled_buttons) {
+ foreach ($enabled_buttons as $button => $enabled) {
+ $extra = array();
+ if ($button == 'heading') {
+ $extra = array('menu' => array(
+ array('text' => 'Normal', 'value' => 'none', 'checked' => TRUE),
+ ));
+ if (!empty($config['block_formats'])) {
+ $headings = array(
+ 'p' => array('text' => 'Paragraph', 'value' => 'p'),
+ 'h1' => array('text' => 'Heading 1', 'value' => 'h1'),
+ 'h2' => array('text' => 'Heading 2', 'value' => 'h2'),
+ 'h3' => array('text' => 'Heading 3', 'value' => 'h3'),
+ 'h4' => array('text' => 'Heading 4', 'value' => 'h4'),
+ 'h5' => array('text' => 'Heading 5', 'value' => 'h5'),
+ 'h6' => array('text' => 'Heading 6', 'value' => 'h6'),
+ );
+ foreach (explode(',', $config['block_formats']) as $tag) {
+ if (isset($headings[$tag])) {
+ $extra['menu'][] = $headings[$tag];
+ }
+ }
+ }
+ }
+ elseif ($button == 'fontname') {
+ $extra = array('menu' => array(
+ array('text' => 'Arial', 'checked' => TRUE),
+ array('text' => 'Arial Black'),
+ array('text' => 'Comic Sans MS'),
+ array('text' => 'Courier New'),
+ array('text' => 'Lucida Console'),
+ array('text' => 'Tahoma'),
+ array('text' => 'Times New Roman'),
+ array('text' => 'Trebuchet MS'),
+ array('text' => 'Verdana'),
+ ));
+ }
+ $buttons[] = wysiwyg_yui_button_setting($editor, $plugin, $button, $extra);
+ }
+ }
+ // Group buttons in a dummy group.
+ $buttons = array('group' => 'default', 'label' => '', 'buttons' => $buttons);
+ $settings['toolbar']['buttons'] = array($buttons);
+ }
+
+ if (isset($config['css_setting'])) {
+ if ($config['css_setting'] == 'theme') {
+ $settings['extracss'] = wysiwyg_get_css();
+ }
+ elseif ($config['css_setting'] == 'self' && isset($config['css_path'])) {
+ $settings['extracss'] = strtr($config['css_path'], array('%b' => base_path(), '%t' => drupal_get_path('theme', variable_get('theme_default', NULL))));
+ $settings['extracss'] = explode(',', $settings['extracss']);
+ }
+ // YUI only supports inline CSS, so we need to use @import directives.
+ // Syntax: '@import "/base/path/to/theme/style.css"; '
+ if (!empty($settings['extracss'])) {
+ $settings['extracss'] = '@import "' . implode('"; @import "', $settings['extracss']) . '";';
+ }
+ }
+
+ return $settings;
+}
+
+/**
+ * Create the JavaScript structure for a YUI button.
+ *
+ * @param $editor
+ * A processed hook_editor() array of editor properties.
+ * @param $plugin
+ * The internal name of a plugin.
+ * @param $button
+ * The internal name of a button, defined by $plugin.
+ * @param $extra
+ * (optional) An array containing arbitrary other elements to add to the
+ * resulting button.
+ */
+function wysiwyg_yui_button_setting($editor, $plugin, $button, $extra = array()) {
+ static $plugins;
+
+ if (!isset($plugins)) {
+ $plugins = wysiwyg_get_plugins($editor['name']);
+ }
+
+ // Return a simple separator.
+ if ($button === 'separator') {
+ return array('type' => 'separator');
+ }
+ // Setup defaults.
+ $type = 'push';
+ $label = $plugins[$plugin]['buttons'][$button];
+
+ // Special handling for certain buttons.
+ if (in_array($button, array('heading', 'fontname'))) {
+ $type = 'select';
+ $label = $extra['menu'][0]['text'];
+ }
+ elseif (in_array($button, array('fontsize'))) {
+ $type = 'spin';
+ }
+ elseif (in_array($button, array('forecolor', 'backcolor'))) {
+ $type = 'color';
+ }
+
+ $button = array(
+ 'type' => $type,
+ 'label' => $label,
+ 'value' => $button,
+ );
+ // Add arbitrary other elements, if defined.
+ if (!empty($extra)) {
+ $button = array_merge($button, $extra);
+ }
+ return $button;
+}
+
+/**
+ * Build a JS settings array of native external plugins that need to be loaded separately.
+ */
+function wysiwyg_yui_plugin_settings($editor, $profile, $plugins) {
+ $settings = array();
+ foreach ($plugins as $name => $plugin) {
+ if (!empty($plugin['load'])) {
+ // Add path for native external plugins; internal ones are loaded
+ // automatically.
+ if (empty($plugin['internal']) && isset($plugin['path'])) {
+ $settings[$name] = base_path() . $plugin['path'];
+ }
+ }
+ }
+ return $settings;
+}
+
+/**
+ * Build a JS settings array for Drupal plugins loaded via the proxy plugin.
+ */
+function wysiwyg_yui_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 this editor; semi-implementation of hook_wysiwyg_plugin().
+ */
+function wysiwyg_yui_plugins($editor) {
+ return array(
+ 'default' => array(
+ 'buttons' => array(
+ 'bold' => t('Bold'), 'italic' => t('Italic'), 'underline' => t('Underline'),
+ 'strikethrough' => t('Strike-through'),
+ 'justifyleft' => t('Align left'), 'justifycenter' => t('Align center'), 'justifyright' => t('Align right'), 'justifyfull' => t('Justify'),
+ 'insertunorderedlist' => t('Bullet list'), 'insertorderedlist' => t('Numbered list'),
+ 'outdent' => t('Outdent'), 'indent' => t('Indent'),
+ 'undo' => t('Undo'), 'redo' => t('Redo'),
+ 'createlink' => t('Link'),
+ 'insertimage' => t('Image'),
+ 'forecolor' => t('Font Color'), 'backcolor' => t('Background Color'),
+ 'superscript' => t('Sup'), 'subscript' => t('Sub'),
+ 'hiddenelements' => t('Show/hide hidden elements'),
+ 'removeformat' => t('Remove format'),
+ 'heading' => t('HTML block format'), 'fontname' => t('Font'), 'fontsize' => t('Font size'),
+ ),
+ 'internal' => TRUE,
+ ),
+ );
+}
+
diff --git a/sites/all/modules/contrib/editor/wysiwyg/plugins/break.inc b/sites/all/modules/contrib/editor/wysiwyg/plugins/break.inc
new file mode 100644
index 00000000..887d5905
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/plugins/break.inc
@@ -0,0 +1,21 @@
+ t('Teaser break'),
+ 'vendor url' => 'http://drupal.org/project/wysiwyg',
+ 'icon file' => 'break.gif',
+ 'icon title' => t('Separate the teaser and body of this content'),
+ 'settings' => array(),
+ );
+ return $plugins;
+}
+
diff --git a/sites/all/modules/contrib/editor/wysiwyg/plugins/break/break.css b/sites/all/modules/contrib/editor/wysiwyg/plugins/break/break.css
new file mode 100644
index 00000000..4aaab761
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/plugins/break/break.css
@@ -0,0 +1,10 @@
+
+.wysiwyg-break {
+ display: block;
+ border: 0;
+ border-top: 1px dotted #ccc;
+ margin-top: 1em;
+ width: 100%;
+ height: 12px;
+ background: transparent url(images/breaktext.gif) no-repeat center top;
+}
diff --git a/sites/all/modules/contrib/editor/wysiwyg/plugins/break/break.js b/sites/all/modules/contrib/editor/wysiwyg/plugins/break/break.js
new file mode 100644
index 00000000..54aac4cd
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/plugins/break/break.js
@@ -0,0 +1,68 @@
+(function ($) {
+
+// @todo Array syntax required; 'break' is a predefined token in JavaScript.
+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.instances[instanceId].insert(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 content upon detaching editor.
+ */
+ detach: function(content, settings, instanceId) {
+ var $content = $('' + content + ''); // No .outerHTML() in jQuery :(
+ // #404532: document.createComment() required or IE will strip the comment.
+ // #474908: IE 8 breaks when using jQuery methods to replace the elements.
+ // @todo Add a generic implementation for all Drupal plugins for this.
+ $.each($('img.wysiwyg-break', $content), function (i, elem) {
+ elem.parentNode.insertBefore(document.createComment('break'), elem);
+ elem.parentNode.removeChild(elem);
+ });
+ return $content.html();
+ },
+
+ /**
+ * Helper function to return a HTML placeholder.
+ */
+ _getPlaceholder: function (settings) {
+ return '
';
+ }
+};
+
+})(jQuery);
diff --git a/sites/all/modules/contrib/editor/wysiwyg/plugins/break/images/break.gif b/sites/all/modules/contrib/editor/wysiwyg/plugins/break/images/break.gif
new file mode 100644
index 00000000..4ff564d5
Binary files /dev/null and b/sites/all/modules/contrib/editor/wysiwyg/plugins/break/images/break.gif differ
diff --git a/sites/all/modules/contrib/editor/wysiwyg/plugins/break/images/breaktext.gif b/sites/all/modules/contrib/editor/wysiwyg/plugins/break/images/breaktext.gif
new file mode 100644
index 00000000..61978735
Binary files /dev/null and b/sites/all/modules/contrib/editor/wysiwyg/plugins/break/images/breaktext.gif differ
diff --git a/sites/all/modules/contrib/editor/wysiwyg/plugins/break/images/spacer.gif b/sites/all/modules/contrib/editor/wysiwyg/plugins/break/images/spacer.gif
new file mode 100644
index 00000000..38848651
Binary files /dev/null and b/sites/all/modules/contrib/editor/wysiwyg/plugins/break/images/spacer.gif differ
diff --git a/sites/all/modules/contrib/editor/wysiwyg/plugins/break/langs/ca.js b/sites/all/modules/contrib/editor/wysiwyg/plugins/break/langs/ca.js
new file mode 100644
index 00000000..5ead9380
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/plugins/break/langs/ca.js
@@ -0,0 +1,6 @@
+
+tinyMCE.addToLang('break', {
+ title: 'Inserir marcador de document retallat',
+ desc: 'Generar el punt de separació entre la versió retallada del document i la resta del contingut'
+});
+
diff --git a/sites/all/modules/contrib/editor/wysiwyg/plugins/break/langs/de.js b/sites/all/modules/contrib/editor/wysiwyg/plugins/break/langs/de.js
new file mode 100644
index 00000000..d869a697
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/plugins/break/langs/de.js
@@ -0,0 +1,6 @@
+
+tinyMCE.addToLang('break', {
+ title: 'Anrisstext trennen',
+ desc: 'Separiert den Anrisstext und Textkörper des Inhalts an dieser Stelle'
+});
+
diff --git a/sites/all/modules/contrib/editor/wysiwyg/plugins/break/langs/en.js b/sites/all/modules/contrib/editor/wysiwyg/plugins/break/langs/en.js
new file mode 100644
index 00000000..6d75ef73
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/plugins/break/langs/en.js
@@ -0,0 +1,6 @@
+
+tinyMCE.addToLang('break', {
+ title: 'Insert teaser break',
+ desc: 'Separate teaser and body of this content'
+});
+
diff --git a/sites/all/modules/contrib/editor/wysiwyg/plugins/break/langs/es.js b/sites/all/modules/contrib/editor/wysiwyg/plugins/break/langs/es.js
new file mode 100644
index 00000000..206023da
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/plugins/break/langs/es.js
@@ -0,0 +1,6 @@
+
+tinyMCE.addToLang('break', {
+ title: 'Insertar marcador de documento recortado',
+ desc: 'Generar el punto de separación entre la versión recortada del documento y el resto del contenido'
+});
+
diff --git a/sites/all/modules/contrib/editor/wysiwyg/tests/wysiwyg.test b/sites/all/modules/contrib/editor/wysiwyg/tests/wysiwyg.test
new file mode 100644
index 00000000..263563c2
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/tests/wysiwyg.test
@@ -0,0 +1,7 @@
+ 'Ajaxified form',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('wysiwyg_test_ajax_form'),
+ 'access callback' => TRUE,
+ );
+ return $items;
+}
+
+/**
+ * Form constructor for an ajaxified form lazy-loading a textarea.
+ */
+function wysiwyg_test_ajax_form($form, &$form_state) {
+ $form['enable'] = array(
+ '#type' => 'checkbox',
+ '#title' => 'Load textarea',
+ '#ajax' => array(
+ 'callback' => 'wysiwyg_test_ajax_form_callback',
+ 'wrapper' => 'ajax-wrapper',
+ ),
+ );
+ $form['wrapper'] = array(
+ '#type' => 'container',
+ '#id' => 'ajax-wrapper',
+ );
+ return $form;
+}
+
+/**
+ * #ajax callback for wysiwyg_test_ajax_form().
+ */
+function wysiwyg_test_ajax_form_callback($form, &$form_state) {
+ $form['body'] = array(
+ '#type' => 'text_format',
+ '#default_value' => '',
+ );
+ form_builder($form['form_id']['#value'], $form, $form_state);
+ return $form['body'];
+}
diff --git a/sites/all/modules/contrib/editor/wysiwyg/wysiwyg-dialog-page.tpl.php b/sites/all/modules/contrib/editor/wysiwyg/wysiwyg-dialog-page.tpl.php
new file mode 100644
index 00000000..3e77d79b
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/wysiwyg-dialog-page.tpl.php
@@ -0,0 +1,28 @@
+
+
+
diff --git a/sites/all/modules/contrib/editor/wysiwyg/wysiwyg.admin.inc b/sites/all/modules/contrib/editor/wysiwyg/wysiwyg.admin.inc
new file mode 100644
index 00000000..497e5d4c
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/wysiwyg.admin.inc
@@ -0,0 +1,594 @@
+ '',
+ 'editor' => '',
+ );
+ if (empty($profile['settings'])) {
+ $profile['settings'] = array();
+ }
+ $profile['settings'] += array(
+ 'default' => TRUE,
+ 'user_choose' => FALSE,
+ 'show_toggle' => TRUE,
+ 'theme' => 'advanced',
+ 'language' => 'en',
+ 'access' => 1,
+ 'access_pages' => "node/*\nuser/*\ncomment/*",
+ 'buttons' => array(),
+ 'toolbar_loc' => 'top',
+ 'toolbar_align' => 'left',
+ 'path_loc' => 'bottom',
+ 'resizing' => TRUE,
+ // Also available, but buggy in TinyMCE 2.x: blockquote,code,dt,dd,samp.
+ 'block_formats' => 'p,address,pre,h2,h3,h4,h5,h6,div',
+ 'verify_html' => TRUE,
+ 'preformatted' => FALSE,
+ 'convert_fonts_to_spans' => TRUE,
+ 'remove_linebreaks' => TRUE,
+ 'apply_source_formatting' => FALSE,
+ 'paste_auto_cleanup_on_paste' => FALSE,
+ 'css_setting' => 'theme',
+ 'css_path' => NULL,
+ 'css_classes' => NULL,
+ );
+ $profile = (object) $profile;
+
+ $formats = filter_formats();
+ $editor = wysiwyg_get_editor($profile->editor);
+ drupal_set_title(t('%editor profile for %format', array('%editor' => $editor['title'], '%format' => $formats[$profile->format]->name)), PASS_THROUGH);
+
+ $form['format'] = array('#type' => 'value', '#value' => $profile->format);
+ $form['input_format'] = array('#type' => 'value', '#value' => $formats[$profile->format]->name);
+ $form['editor'] = array('#type' => 'value', '#value' => $profile->editor);
+
+ $form['basic'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Basic setup'),
+ '#collapsible' => TRUE,
+ '#collapsed' => TRUE,
+ );
+
+ $form['basic']['default'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Enabled by default'),
+ '#default_value' => $profile->settings['default'],
+ '#return_value' => 1,
+ '#description' => t('The default editor state for users having access to this profile. Users are able to override this state if the next option is enabled.'),
+ );
+
+ $form['basic']['user_choose'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Allow users to choose default'),
+ '#default_value' => $profile->settings['user_choose'],
+ '#return_value' => 1,
+ '#description' => t('If allowed, users will be able to choose their own editor default state in their user account settings.'),
+ );
+
+ $form['basic']['show_toggle'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Show enable/disable rich text toggle link'),
+ '#default_value' => $profile->settings['show_toggle'],
+ '#return_value' => 1,
+ '#description' => t('Whether or not to show the enable/disable rich text toggle link below a textarea. If disabled, the user setting or global default is used (see above).'),
+ );
+
+ $form['basic']['theme'] = array(
+ '#type' => 'hidden',
+ '#value' => $profile->settings['theme'],
+ );
+
+ $form['basic']['language'] = array(
+ '#type' => 'select',
+ '#title' => t('Interface language'),
+ '#default_value' => $profile->settings['language'],
+ );
+ // @see _locale_prepare_predefined_list()
+ require_once DRUPAL_ROOT . '/includes/iso.inc';
+ $predefined = _locale_get_predefined_list();
+ foreach ($predefined as $key => $value) {
+ // Include native name in output, if possible
+ if (count($value) > 1) {
+ $tname = t($value[0]);
+ $predefined[$key] = ($tname == $value[1]) ? $tname : "$tname ($value[1])";
+ }
+ else {
+ $predefined[$key] = t($value[0]);
+ }
+ }
+ asort($predefined);
+ $form['basic']['language']['#options'] = $predefined;
+
+ $form['buttons'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Buttons and plugins'),
+ '#collapsible' => TRUE,
+ '#collapsed' => TRUE,
+ '#tree' => TRUE,
+ '#theme' => 'wysiwyg_admin_button_table',
+ );
+
+ $plugins = wysiwyg_get_plugins($profile->editor);
+ // Generate the button list.
+ foreach ($plugins as $name => $meta) {
+ if (isset($meta['buttons']) && is_array($meta['buttons'])) {
+ foreach ($meta['buttons'] as $button => $title) {
+ $icon = '';
+ 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) ? '
' : '';
+ }
+ $title = (!empty($icon) ? $icon . ' ' . check_plain($title) : check_plain($title));
+ $form['buttons'][$name][$button] = array(
+ '#type' => 'checkbox',
+ '#title' => $title,
+ '#default_value' => !empty($profile->settings['buttons'][$name][$button]) ? $profile->settings['buttons'][$name][$button] : FALSE,
+ '#description' => isset($meta['url']) ? l($meta['url'], $meta['url']) : NULL,
+ );
+ }
+ }
+ elseif (isset($meta['extensions']) && is_array($meta['extensions'])) {
+ foreach ($meta['extensions'] as $extension => $title) {
+ $form['buttons'][$name][$extension] = array(
+ '#type' => 'checkbox',
+ '#title' => check_plain($title),
+ '#default_value' => !empty($profile->settings['buttons'][$name][$extension]) ? $profile->settings['buttons'][$name][$extension] : FALSE,
+ '#description' => isset($meta['url']) ? l($meta['url'], $meta['url']) : NULL,
+ );
+ }
+ }
+ }
+
+ $form['appearance'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Editor appearance'),
+ '#collapsible' => TRUE,
+ '#collapsed' => TRUE,
+ );
+
+ $form['appearance']['toolbar_loc'] = array(
+ '#type' => 'select',
+ '#title' => t('Toolbar location'),
+ '#default_value' => $profile->settings['toolbar_loc'],
+ '#options' => array('bottom' => t('Bottom'), 'top' => t('Top')),
+ '#description' => t('This option controls whether the editor toolbar is displayed above or below the editing area.'),
+ );
+
+ $form['appearance']['toolbar_align'] = array(
+ '#type' => 'select',
+ '#title' => t('Button alignment'),
+ '#default_value' => $profile->settings['toolbar_align'],
+ '#options' => array('center' => t('Center'), 'left' => t('Left'), 'right' => t('Right')),
+ '#description' => t('This option controls the alignment of icons in the editor toolbar.'),
+ );
+
+ $form['appearance']['path_loc'] = array(
+ '#type' => 'select',
+ '#title' => t('Path location'),
+ '#default_value' => $profile->settings['path_loc'],
+ '#options' => array('none' => t('Hide'), 'top' => t('Top'), 'bottom' => t('Bottom')),
+ '#description' => t('Where to display the path to HTML elements (i.e. body > table > tr > td).'),
+ );
+
+ $form['appearance']['resizing'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Enable resizing button'),
+ '#default_value' => $profile->settings['resizing'],
+ '#return_value' => 1,
+ '#description' => t('This option gives you the ability to enable/disable the resizing button. If enabled, the Path location toolbar must be set to "Top" or "Bottom" in order to display the resize icon.'),
+ );
+
+ $form['output'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Cleanup and output'),
+ '#collapsible' => TRUE,
+ '#collapsed' => TRUE,
+ );
+
+ $form['output']['verify_html'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Verify HTML'),
+ '#default_value' => $profile->settings['verify_html'],
+ '#return_value' => 1,
+ '#description' => t('If enabled, potentially malicious code like <HEAD> tags will be removed from HTML contents.'),
+ );
+
+ $form['output']['preformatted'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Preformatted'),
+ '#default_value' => $profile->settings['preformatted'],
+ '#return_value' => 1,
+ '#description' => t('If enabled, the editor will insert TAB characters on tab and preserve other whitespace characters just like a PRE element in HTML does.'),
+ );
+
+ $form['output']['convert_fonts_to_spans'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Convert <font> tags to styles'),
+ '#default_value' => $profile->settings['convert_fonts_to_spans'],
+ '#return_value' => 1,
+ '#description' => t('If enabled, HTML tags declaring the font size, font family, font color and font background color will be replaced by inline CSS styles.'),
+ );
+
+ $form['output']['remove_linebreaks'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Remove linebreaks'),
+ '#default_value' => $profile->settings['remove_linebreaks'],
+ '#return_value' => 1,
+ '#description' => t('If enabled, the editor will remove most linebreaks from contents. Disabling this option could avoid conflicts with other input filters.'),
+ );
+
+ $form['output']['apply_source_formatting'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Apply source formatting'),
+ '#default_value' => $profile->settings['apply_source_formatting'],
+ '#return_value' => 1,
+ '#description' => t('If enabled, the editor will re-format the HTML source code. Disabling this option could avoid conflicts with other input filters.'),
+ );
+
+ $form['output']['paste_auto_cleanup_on_paste'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Force cleanup on standard paste'),
+ '#default_value' => $profile->settings['paste_auto_cleanup_on_paste'],
+ '#return_value' => 1,
+ '#description' => t('If enabled, the default paste function (CTRL-V or SHIFT-INS) behaves like the "paste from word" plugin function.'),
+ );
+
+ $form['css'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('CSS'),
+ '#collapsible' => TRUE,
+ '#collapsed' => TRUE,
+ );
+
+ $form['css']['block_formats'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Block formats'),
+ '#default_value' => $profile->settings['block_formats'],
+ '#size' => 40,
+ '#maxlength' => 250,
+ '#description' => t('Comma separated list of HTML block formats. Possible values: @format-list.', array('@format-list' => 'p,h1,h2,h3,h4,h5,h6,div,blockquote,address,pre,code,dt,dd')),
+ );
+
+ $form['css']['css_setting'] = array(
+ '#type' => 'select',
+ '#title' => t('Editor CSS'),
+ '#default_value' => $profile->settings['css_setting'],
+ '#options' => array('theme' => t('Use theme CSS'), 'self' => t('Define CSS'), 'none' => t('Editor default CSS')),
+ '#description' => t('Defines the CSS to be used in the editor area.
Use theme CSS - loads stylesheets from current site theme.
Define CSS - enter path for stylesheet files below.
Editor default CSS - uses default stylesheets from editor.'),
+ );
+
+ $form['css']['css_path'] = array(
+ '#type' => 'textfield',
+ '#title' => t('CSS path'),
+ '#default_value' => $profile->settings['css_path'],
+ '#size' => 40,
+ '#maxlength' => 255,
+ '#description' => t('If "Define CSS" was selected above, enter path to a CSS file or a list of CSS files separated by a comma.') . '
' . t('Available tokens: %b (base path, eg: /), %t (path to theme, eg: themes/garland)') . '
' . t('Example:') . ' css/editor.css,/themes/garland/style.css,%b%t/style.css,http://example.com/external.css',
+ );
+
+ $form['css']['css_classes'] = array(
+ '#type' => 'textarea',
+ '#title' => t('CSS classes'),
+ '#default_value' => $profile->settings['css_classes'],
+ '#description' => t('Optionally define CSS classes for the "Font style" dropdown list.
Enter one class on each line in the format: !format. Example: !example
If left blank, CSS classes are automatically imported from all loaded stylesheet(s).', array('!format' => '[title]=[class]', '!example' => 'My heading=header1')),
+ );
+
+ $form['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Save'),
+ '#weight' => 100,
+ );
+ $form['cancel'] = array(
+ '#value' => l(t('Cancel'), 'admin/config/content/wysiwyg'),
+ '#weight' => 110,
+ );
+
+ // Supply contextual information for other callbacks and handlers.
+ // @todo Modernize this form for D7+ and declare these earlier.
+ // $profile is the primary object of this form, and as an entity, usually
+ // expected to live in $form_state[$entity_type].
+ $form_state['wysiwyg_profile'] = $profile;
+ $form_state['wysiwyg']['editor'] = $editor;
+ $form_state['wysiwyg']['plugins'] = $plugins;
+
+ // Allow editor library specific changes to be made to the form.
+ if (isset($editor['settings form callback'])) {
+ $editor['settings form callback']($form, $form_state);
+ }
+
+ return $form;
+}
+
+/**
+ * Submit callback for Wysiwyg profile form.
+ *
+ * @see wysiwyg_profile_form()
+ */
+function wysiwyg_profile_form_submit($form, &$form_state) {
+ $values = $form_state['values'];
+ if (isset($values['buttons'])) {
+ // Store only enabled buttons for each plugin.
+ foreach ($values['buttons'] as $plugin => $buttons) {
+ $values['buttons'][$plugin] = array_filter($values['buttons'][$plugin]);
+ }
+ // Store only enabled plugins.
+ $values['buttons'] = array_filter($values['buttons']);
+ }
+ // Remove any white-space from 'block_formats' setting, since editor
+ // implementations rely on a comma-separated list to explode().
+ $values['block_formats'] = preg_replace('@\s+@', '', $values['block_formats']);
+
+ // Remove input format name.
+ $format = $values['format'];
+ $input_format = $values['input_format'];
+ $editor = $values['editor'];
+ unset($values['format'], $values['input_format'], $values['editor']);
+
+ // Remove FAPI values.
+ // @see system_settings_form_submit()
+ unset($values['submit'], $values['form_id'], $values['op'], $values['form_token'], $values['form_build_id']);
+
+ // Insert new profile data.
+ db_merge('wysiwyg')
+ ->key(array('format' => $format))
+ ->fields(array(
+ 'editor' => $editor,
+ 'settings' => serialize($values),
+ ))
+ ->execute();
+ wysiwyg_profile_cache_clear();
+
+ drupal_set_message(t('Wysiwyg profile for %format has been saved.', array('%format' => $input_format)));
+
+ $form_state['redirect'] = 'admin/config/content/wysiwyg';
+}
+
+/**
+ * Layout for the buttons in the Wysiwyg Editor profile form.
+ */
+function theme_wysiwyg_admin_button_table($variables) {
+ $form = $variables['form'];
+ $buttons = array();
+
+ // Flatten forms array.
+ foreach (element_children($form) as $name) {
+ foreach (element_children($form[$name]) as $button) {
+ $buttons[] = drupal_render($form[$name][$button]);
+ }
+ }
+
+ // Split checkboxes into rows with 3 columns.
+ $total = count($buttons);
+ $rows = array();
+ for ($i = 0; $i < $total; $i += 3) {
+ $row = array();
+ $row_buttons = array_slice($buttons, $i, 3) + array_fill(0, 3, array());
+ foreach ($row_buttons as $row_button) {
+ $row[] = array('data' => $row_button);
+ }
+ $rows[] = $row;
+ }
+
+ $output = theme('table', array('rows' => $rows, 'attributes' => array('width' => '100%')));
+
+ return $output;
+}
+
+/**
+ * Display overview of setup Wysiwyg Editor profiles; menu callback.
+ */
+function wysiwyg_profile_overview($form, &$form_state) {
+ include_once './includes/install.inc';
+
+ // Check which wysiwyg editors are installed.
+ $editors = wysiwyg_get_all_editors();
+ $count = count($editors);
+ $status = array();
+ $options = array('' => t('No editor'));
+
+ // D7's seven theme displays links in table headers as block elements.
+ drupal_add_css('table.system-status-report th a {display: inline;}', 'inline');
+
+ foreach ($editors as $name => $editor) {
+ $status[$name] = array(
+ 'severity' => (isset($editor['error']) ? REQUIREMENT_ERROR : ($editor['installed'] ? REQUIREMENT_OK : REQUIREMENT_INFO)),
+ 'title' => t('@editor (Download)', array('!vendor-url' => $editor['vendor url'], '@editor' => $editor['title'], '!download-url' => $editor['download url'])),
+ 'value' => (isset($editor['installed version']) ? $editor['installed version'] : t('Not installed.')),
+ 'description' => (isset($editor['error']) ? $editor['error'] : ''),
+ );
+ if ($editor['installed']) {
+ $options[$name] = $editor['title'] . (isset($editor['installed version']) ? ' ' . $editor['installed version'] : '');
+ }
+ else {
+ // Build on-site installation instructions.
+ // @todo Setup $library in wysiwyg_load_editor() already.
+ $library = (isset($editor['library']) ? $editor['library'] : key($editor['libraries']));
+ $targs = array(
+ '@editor-path' => $editor['editor path'],
+ '@library-filepath' => $editor['library path'] . '/' . (isset($editor['libraries'][$library]['files'][0]) ? $editor['libraries'][$library]['files'][0] : key($editor['libraries'][$library]['files'])),
+ );
+ $instructions = '' . t('Extract the archive and copy its contents into a new folder in the following location:
@editor-path', $targs) . '
';
+ $instructions .= '' . t('So the actual library can be found at:
@library-filepath', $targs) . '
';
+
+ // Add any install notes.
+ if (!empty($editor['install note callback']) && function_exists($editor['install note callback'])) {
+ $instructions .= '' . $editor['install note callback']() . '';
+ }
+
+ $status[$name]['description'] .= $instructions;
+ $count--;
+ }
+ // In case there is an error, always show installation instructions.
+ if (isset($editor['error'])) {
+ $show_instructions = TRUE;
+ }
+ }
+ if (!$count) {
+ $show_instructions = TRUE;
+ }
+ $form['status'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Installation instructions'),
+ '#collapsible' => TRUE,
+ '#collapsed' => !isset($show_instructions),
+ '#description' => (!$count ? t('There are no editor libraries installed currently. The following list contains a list of currently supported editors:') : ''),
+ '#weight' => 10,
+ );
+ $form['status']['report'] = array('#markup' => theme('status_report', array('requirements' => $status)));
+
+ if (!$count) {
+ return $form;
+ }
+
+ $formats = filter_formats();
+ $profiles = wysiwyg_profile_load_all();
+ $form['formats'] = array(
+ '#type' => 'item',
+ '#description' => t('To assign a different editor to a text format, click "delete" to remove the existing first.'),
+ '#tree' => TRUE,
+ );
+
+ $enable_save = FALSE;
+ foreach ($formats as $id => $format) {
+ $form['formats'][$id]['name'] = array(
+ '#markup' => check_plain($format->name),
+ );
+ // Only display editor selection for associated input formats to avoid
+ // confusion about disabled selection.
+ if (isset($profiles[$id]) && !empty($profiles[$id]->editor)) {
+ $editor_name = $profiles[$id]->editor;
+ $installed = !empty($editors[$editor_name]['installed']);
+ $form['formats'][$id]['editor'] = array(
+ '#wysiwyg-editor-name' => $editor_name,
+ );
+ if ($installed) {
+ $form['formats'][$id]['editor']['#markup'] = $options[$editor_name];
+ }
+ else {
+ drupal_set_message(t('Missing %editor library for %format format. Re-install the %editor library or delete the editor profile.', array(
+ '%editor' => $editors[$editor_name]['title'],
+ '%format' => $format->name,
+ )), 'warning');
+ }
+ }
+ else {
+ $form['formats'][$id]['editor'] = array(
+ '#type' => 'select',
+ '#default_value' => '',
+ '#options' => $options,
+ );
+ $enable_save = TRUE;
+ }
+ if (isset($profiles[$id]) && !empty($profiles[$id]->editor)) {
+ $form['formats'][$id]['edit'] = array(
+ '#markup' => l(t('Edit'), "admin/config/content/wysiwyg/profile/$id/edit"),
+ );
+ $form['formats'][$id]['delete'] = array(
+ '#markup' => l(t('Delete'), "admin/config/content/wysiwyg/profile/$id/delete"),
+ );
+ }
+ }
+
+ // Submitting the form when no editors can be selected causes errors.
+ if ($enable_save) {
+ $form['submit'] = array('#type' => 'submit', '#value' => t('Save'));
+ }
+ return $form;
+}
+
+/**
+ * Return HTML for the Wysiwyg profile overview form.
+ */
+function theme_wysiwyg_profile_overview($variables) {
+ $form = $variables['form'];
+ if (!isset($form['formats'])) {
+ return;
+ }
+ $editors = wysiwyg_get_all_editors();
+ $output = '';
+ $header = array(t('Text format'), t('Editor'), array('data' => t('Operations'), 'colspan' => 2));
+ $rows = array();
+ foreach (element_children($form['formats']) as $item) {
+ $format = &$form['formats'][$item];
+ $row = array(
+ 'data' => array(
+ drupal_render($format['name']),
+ drupal_render($format['editor']),
+ isset($format['edit']) ? drupal_render($format['edit']) : '',
+ isset($format['delete']) ? drupal_render($format['delete']) : '',
+ ),
+ );
+ if (empty($row['data'][1])) {
+ $row['data'][1] = array(
+ 'data' => t('Missing library: @library', array('@library' => $editors[$format['editor']['#wysiwyg-editor-name']]['title'])),
+ 'class' => 'error',
+ );
+ $row['class'] = array('error');
+ }
+ $rows[] = $row;
+ }
+ $form['formats']['table']['#markup'] = theme('table', array('header' => $header, 'rows' => $rows));
+ $output .= drupal_render_children($form);
+ return $output;
+}
+
+/**
+ * Submit callback for Wysiwyg profile overview form.
+ */
+function wysiwyg_profile_overview_submit($form, &$form_state) {
+ foreach ($form_state['values']['formats'] as $format => $values) {
+ db_merge('wysiwyg')
+ ->key(array('format' => $format))
+ ->fields(array(
+ 'editor' => $values['editor'],
+ ))
+ ->execute();
+ }
+ wysiwyg_profile_cache_clear();
+}
+
+/**
+ * Delete editor profile confirmation form.
+ */
+function wysiwyg_profile_delete_confirm($form, &$form_state, $profile) {
+ $formats = filter_formats();
+ $format = $formats[$profile->format];
+ $form['format'] = array('#type' => 'value', '#value' => $format);
+ return confirm_form(
+ $form,
+ t('Are you sure you want to remove the profile for %name?', array('%name' => $format->name)),
+ 'admin/config/content/wysiwyg',
+ t('This action cannot be undone.'), t('Remove'), t('Cancel')
+ );
+}
+
+/**
+ * Submit callback for Wysiwyg profile delete form.
+ *
+ * @see wysiwyg_profile_delete_confirm()
+ */
+function wysiwyg_profile_delete_confirm_submit($form, &$form_state) {
+ $format = $form_state['values']['format'];
+ wysiwyg_profile_delete($format->format);
+ wysiwyg_profile_cache_clear();
+
+ drupal_set_message(t('Wysiwyg profile for %name has been deleted.', array('%name' => $format->name)));
+ $form_state['redirect'] = 'admin/config/content/wysiwyg';
+}
diff --git a/sites/all/modules/contrib/editor/wysiwyg/wysiwyg.api.js b/sites/all/modules/contrib/editor/wysiwyg/wysiwyg.api.js
new file mode 100644
index 00000000..0318b0b4
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/wysiwyg.api.js
@@ -0,0 +1,97 @@
+
+/**
+ * Wysiwyg plugin button implementation for Awesome plugin.
+ */
+Drupal.wysiwyg.plugins.awesome = {
+ /**
+ * Return whether the passed node belongs to this plugin.
+ *
+ * @param node
+ * The currently focused DOM element in the editor content.
+ */
+ isNode: function(node) {
+ return ($(node).is('img.mymodule-awesome'));
+ },
+
+ /**
+ * Execute the button.
+ *
+ * @param data
+ * An object containing data about the current selection:
+ * - format: 'html' when the passed data is HTML content, 'text' when the
+ * passed data is plain-text content.
+ * - node: When 'format' is 'html', the focused DOM element in the editor.
+ * - content: The textual representation of the focused/selected editor
+ * content.
+ * @param settings
+ * The plugin settings, as provided in the plugin's PHP include file.
+ * @param instanceId
+ * The ID of the current editor instance.
+ */
+ invoke: function(data, settings, instanceId) {
+ // Generate HTML markup.
+ if (data.format == 'html') {
+ // Prevent duplicating a teaser break.
+ if ($(data.node).is('img.mymodule-awesome')) {
+ return;
+ }
+ var content = this._getPlaceholder(settings);
+ }
+ // Generate plain text.
+ else {
+ var content = '';
+ }
+ // Insert new content into the editor.
+ if (typeof content != 'undefined') {
+ Drupal.wysiwyg.instances[instanceId].insert(content);
+ }
+ },
+
+ /**
+ * Prepare all plain-text contents of this plugin with HTML representations.
+ *
+ * Optional; only required for "inline macro tag-processing" plugins.
+ *
+ * @param content
+ * The plain-text contents of a textarea.
+ * @param settings
+ * The plugin settings, as provided in the plugin's PHP include file.
+ * @param instanceId
+ * The ID of the current editor instance.
+ */
+ attach: function(content, settings, instanceId) {
+ content = content.replace(//g, this._getPlaceholder(settings));
+ return content;
+ },
+
+ /**
+ * Process all HTML placeholders of this plugin with plain-text contents.
+ *
+ * Optional; only required for "inline macro tag-processing" plugins.
+ *
+ * @param content
+ * The HTML content string of the editor.
+ * @param settings
+ * The plugin settings, as provided in the plugin's PHP include file.
+ * @param instanceId
+ * The ID of the current editor instance.
+ */
+ detach: function(content, settings, instanceId) {
+ var $content = $('' + content + '');
+ $.each($('img.mymodule-awesome', $content), function (i, elem) {
+ //...
+ });
+ return $content.html();
+ },
+
+ /**
+ * Helper function to return a HTML placeholder.
+ *
+ * The 'drupal-content' CSS class is required for HTML elements in the editor
+ * content that shall not trigger any editor's native buttons (such as the
+ * image button for this example placeholder markup).
+ */
+ _getPlaceholder: function (settings) {
+ return '
';
+ }
+};
diff --git a/sites/all/modules/contrib/editor/wysiwyg/wysiwyg.api.php b/sites/all/modules/contrib/editor/wysiwyg/wysiwyg.api.php
new file mode 100644
index 00000000..c4d88579
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/wysiwyg.api.php
@@ -0,0 +1,283 @@
+ 3) {
+ return array(
+ 'myplugin' => array(
+ // A URL to the plugin's homepage.
+ 'url' => 'http://drupal.org/project/img_assist',
+ // The full path to the native editor plugin, no trailing slash.
+ // Ignored when 'internal' is set to TRUE below.
+ 'path' => drupal_get_path('module', 'img_assist') . '/drupalimage',
+ // The name of the plugin's main JavaScript file.
+ // Ignored when 'internal' is set to TRUE below.
+ // Default value depends on which editor the plugin is for.
+ 'filename' => 'editor_plugin.js',
+ // A list of buttons provided by this native plugin. The key has to
+ // match the corresponding JavaScript implementation. The value is
+ // is displayed on the editor configuration form only.
+ 'buttons' => array(
+ 'img_assist' => t('Image Assist'),
+ ),
+ // A list of editor extensions provided by this native plugin.
+ // Extensions are not displayed as buttons and touch the editor's
+ // internals, so you should know what you are doing.
+ 'extensions' => array(
+ 'imce' => t('IMCE'),
+ ),
+ // A list of global, native editor configuration settings to
+ // override. To be used rarely and only when required.
+ 'options' => array(
+ 'file_browser_callback' => 'imceImageBrowser',
+ 'inline_styles' => TRUE,
+ ),
+ // Boolean whether the editor needs to load this plugin. When TRUE,
+ // the editor will automatically load the plugin based on the 'path'
+ // variable provided. If FALSE, the plugin either does not need to
+ // be loaded or is already loaded by something else on the page.
+ // Most plugins should define TRUE here.
+ 'load' => TRUE,
+ // Boolean whether this plugin is a native plugin, i.e. shipped with
+ // the editor. Definition must be ommitted for plugins provided by
+ // other modules. TRUE means 'path' and 'filename' above are ignored
+ // and the plugin is instead loaded from the editor's plugin folder.
+ 'internal' => TRUE,
+ // TinyMCE-specific: Additional HTML elements to allow in the markup.
+ 'extended_valid_elements' => array(
+ 'img[class|src|border=0|alt|title|width|height|align|name|style]',
+ ),
+ ),
+ );
+ }
+ break;
+ }
+}
+
+/**
+ * Register a directory containing Wysiwyg plugins.
+ *
+ * @param $type
+ * The type of objects being collected: either 'plugins' or 'editors'.
+ * @return
+ * A sub-directory of the implementing module that contains the corresponding
+ * plugin files. This directory must only contain integration files for
+ * Wysiwyg module.
+ */
+function hook_wysiwyg_include_directory($type) {
+ switch ($type) {
+ case 'plugins':
+ // You can just return $type, if you place your Wysiwyg plugins into a
+ // sub-directory named 'plugins'.
+ return $type;
+ }
+}
+
+/**
+ * Define a Wysiwyg plugin.
+ *
+ * Supposed to be used for "Drupal plugins" (cross-editor plugins) only.
+ *
+ * @see hook_wysiwyg_plugin()
+ *
+ * Each plugin file in the specified plugin directory of a module needs to
+ * define meta information about the particular plugin provided.
+ * The plugin's hook implementation function name is built out of the following:
+ * - 'hook': The name of the module providing the plugin.
+ * - 'INCLUDE': The basename of the file containing the plugin definition.
+ * - 'plugin': Static.
+ *
+ * For example, if your module's name is 'mymodule' and
+ * mymodule_wysiwyg_include_directory() returned 'plugins' as plugin directory,
+ * and this directory contains an "awesome" plugin file named 'awesome.inc', i.e.
+ * sites/all/modules/mymodule/plugins/awesome.inc
+ * then the corresponding plugin hook function name is:
+ * mymodule_awesome_plugin()
+ *
+ * @see hook_wysiwyg_include_directory()
+ *
+ * @return
+ * Meta information about the buttons provided by this plugin.
+ */
+function hook_INCLUDE_plugin() {
+ $plugins['awesome'] = array(
+ // The plugin's title; defaulting to its internal name ('awesome').
+ 'title' => t('Awesome plugin'),
+ // The (vendor) homepage of this plugin; defaults to ''.
+ 'vendor url' => 'http://drupal.org/project/wysiwyg',
+ // The path to the button's icon; defaults to
+ // '/[path-to-module]/[plugins-directory]/[plugin-name]/images'.
+ 'icon path' => 'path to icon',
+ // The button image filename; defaults to '[plugin-name].png'.
+ 'icon file' => 'name of the icon file with extension',
+ // The button title to display on hover.
+ 'icon title' => t('Do something'),
+ // An alternative path to the integration JavaScript; defaults to
+ // '[path-to-module]/[plugins-directory]/[plugin-name]'.
+ 'js path' => drupal_get_path('module', 'mymodule') . '/awesomeness',
+ // An alternative filename of the integration JavaScript; defaults to
+ // '[plugin-name].js'.
+ 'js file' => 'awesome.js',
+ // An alternative path to the integration stylesheet; defaults to
+ // '[path-to-module]/[plugins-directory]/[plugin-name]'.
+ 'css path' => drupal_get_path('module', 'mymodule') . '/awesomeness',
+ // An alternative filename of the integration stylesheet; defaults to
+ // '[plugin-name].css'.
+ 'css file' => 'awesome.css',
+ // An array of settings for this button. Required, but API is still in flux.
+ 'settings' => array(
+ ),
+ // TinyMCE-specific: Additional HTML elements to allow in the markup.
+ 'extended_valid_elements' => array(
+ 'tag1[attribute1|attribute2]',
+ 'tag2[attribute3|attribute4]',
+ ),
+ );
+ return $plugins;
+}
+
+/**
+ * Define a Wysiwyg editor library.
+ *
+ * @todo Complete this documentation.
+ */
+function hook_INCLUDE_editor() {
+ $editor['ckeditor'] = array(
+ // The official, human-readable label of the editor library.
+ 'title' => 'CKEditor',
+ // The URL to the library's homepage.
+ 'vendor url' => 'http://ckeditor.com',
+ // The URL to the library's download page.
+ 'download url' => 'http://ckeditor.com/download',
+ // A definition of available variants for the editor library.
+ // The first defined is used by default.
+ 'libraries' => array(
+ '' => array(
+ 'title' => 'Default',
+ 'files' => array(
+ 'ckeditor.js' => array('preprocess' => FALSE),
+ ),
+ ),
+ 'src' => array(
+ 'title' => 'Source',
+ 'files' => array(
+ 'ckeditor_source.js' => array('preprocess' => FALSE),
+ ),
+ ),
+ ),
+ // (optional) A callback to invoke to return additional notes for installing
+ // the editor library in the administrative list/overview.
+ 'install note callback' => 'wysiwyg_ckeditor_install_note',
+ // A callback to determine the library's version.
+ 'version callback' => 'wysiwyg_ckeditor_version',
+ // A callback to return available themes/skins for the editor library.
+ 'themes callback' => 'wysiwyg_ckeditor_themes',
+ // (optional) A callback to perform editor-specific adjustments or
+ // enhancements for the administrative editor profile settings form.
+ 'settings form callback' => 'wysiwyg_ckeditor_settings_form',
+ // (optional) A callback to return an initialization JavaScript snippet for
+ // this editor library, loaded before the actual library files. The returned
+ // JavaScript is executed as inline script in a primitive environment,
+ // before the DOM is loaded; typically used to prime a base path and other
+ // global window variables for the editor library before it is loaded.
+ // All implementations should verbosely document what they are doing and
+ // why that is required.
+ 'init callback' => 'wysiwyg_ckeditor_init',
+ // A callback to convert administrative profile/editor settings into
+ // JavaScript settings.
+ 'settings callback' => 'wysiwyg_ckeditor_settings',
+ // A callback to supply definitions of available editor plugins.
+ 'plugin callback' => 'wysiwyg_ckeditor_plugins',
+ // A callback to convert administrative plugin settings for a editor profile
+ // into JavaScript settings.
+ 'plugin settings callback' => 'wysiwyg_ckeditor_plugin_settings',
+ // (optional) Defines the proxy plugin that handles plugins provided by
+ // Drupal modules, which work in all editors that support proxy plugins.
+ 'proxy plugin' => array(
+ 'drupal' => array(
+ 'load' => TRUE,
+ 'proxy' => TRUE,
+ ),
+ ),
+ // (optional) A callback to convert proxy plugin settings into JavaScript
+ // settings.
+ 'proxy plugin settings callback' => 'wysiwyg_ckeditor_proxy_plugin_settings',
+ // Defines the list of supported (minimum) versions of the editor library,
+ // and the respective Drupal integration files to load.
+ 'versions' => array(
+ '3.0.0.3665' => array(
+ 'js files' => array('ckeditor-3.0.js'),
+ ),
+ ),
+ );
+ return $editor;
+}
+
+/**
+ * Act on editor profile settings.
+ *
+ * This hook is invoked from wysiwyg_get_editor_config() after the JavaScript
+ * settings have been generated for an editor profile and before the settings
+ * are added to the page. The settings may be customized or enhanced; typically
+ * with options that cannot be controlled through Wysiwyg module's
+ * administrative UI currently.
+ *
+ * Modules implementing this hook to enforce settings that can also be
+ * controlled through the UI should also implement
+ * hook_form_wysiwyg_profile_form_alter() to adjust or at least indicate on the
+ * editor profile configuration form that certain/affected settings cannot be
+ * changed.
+ *
+ * @param $settings
+ * An associative array of JavaScript settings to pass to the editor.
+ * @param $context
+ * An associative array containing additional context information:
+ * - editor: The plugin definition array of the editor.
+ * - profile: The editor profile object, as loaded from the database.
+ * - theme: The name of the editor theme/skin.
+ */
+function hook_wysiwyg_editor_settings_alter(&$settings, $context) {
+ // Each editor has its own collection of native settings that may be extended
+ // or overridden. Please consult the respective official vendor documentation
+ // for details.
+ if ($context['profile']->editor == 'tinymce') {
+ // Supported values to JSON data types.
+ $settings['cleanup_on_startup'] = TRUE;
+ }
+}
diff --git a/sites/all/modules/contrib/editor/wysiwyg/wysiwyg.dialog.inc b/sites/all/modules/contrib/editor/wysiwyg/wysiwyg.dialog.inc
new file mode 100644
index 00000000..c296d2e0
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/wysiwyg.dialog.inc
@@ -0,0 +1,183 @@
+ $plugin,
+ 'instance' => $instance,
+ );
+ drupal_add_js(array('wysiwyg' => $settings), 'setting');
+
+ $build = $callback($instance);
+ if (!is_array($build)) {
+ $build = array('#markup' => $build);
+ }
+ $build += array(
+ '#instance' => $instance,
+ '#plugin' => $plugin,
+ );
+ return $build;
+}
+
+/**
+ * @see drupal_deliver_html_page()
+ */
+function wysiwyg_deliver_dialog_page($page_callback_result) {
+ // Menu status constants are integers; page content is a string or array.
+ if (is_int($page_callback_result)) {
+ return drupal_deliver_html_page($page_callback_result);
+ }
+
+ // Emit the correct charset HTTP header, but not if the page callback
+ // result is NULL, since that likely indicates that it printed something
+ // in which case, no further headers may be sent, and not if code running
+ // for this page request has already set the content type header.
+ if (isset($page_callback_result) && is_null(drupal_get_http_header('Content-Type'))) {
+ drupal_add_http_header('Content-Type', 'text/html; charset=utf-8');
+ }
+
+ // Send appropriate HTTP-Header for browsers and search engines.
+ global $language;
+ drupal_add_http_header('Content-Language', $language->language);
+
+ if (isset($page_callback_result)) {
+ // Print anything besides a menu constant, assuming it's not NULL or
+ // undefined.
+ print wysiwyg_render_dialog_page($page_callback_result);
+ }
+
+ // Perform end-of-request tasks.
+ drupal_page_footer();
+}
+
+/**
+ * @see drupal_render_page()
+ */
+function wysiwyg_render_dialog_page($page) {
+ $main_content_display = &drupal_static('system_main_content_added', FALSE);
+
+ // Allow menu callbacks to return strings or arbitrary arrays to render.
+ // If the array returned is not of #type page directly, we need to fill
+ // in the page with defaults.
+ if (is_string($page) || (is_array($page) && (!isset($page['#type']) || ($page['#type'] != 'page')))) {
+ drupal_set_page_content($page);
+ $page = element_info('wysiwyg_dialog_page');
+ }
+
+ // Modules alter the $page as needed. Blocks are populated into regions like
+ // 'sidebar_first', 'footer', etc.
+ drupal_alter(array('wysiwyg_dialog_page', 'page'), $page);
+
+ // If no module has taken care of the main content, add it to the page now.
+ // This allows the site to still be usable even if no modules that
+ // control page regions (for example, the Block module) are enabled.
+ if (!$main_content_display) {
+ $page['content']['system_main'] = drupal_set_page_content();
+ }
+
+ return drupal_render($page);
+}
+
+/**
+ * Template preprocess function for theme_wysiwyg_dialog_page().
+ *
+ * @see wysiwyg_dialog()
+ * @see wysiwyg-dialog-page.tpl.php
+ * @see template_preprocess_page()
+ */
+function template_preprocess_wysiwyg_dialog_page(&$variables) {
+ template_preprocess_page($variables);
+}
+
+
+/**
+ * Template process function for theme_wysiwyg_dialog_page().
+ *
+ * @see wysiwyg_dialog()
+ * @see wysiwyg-dialog-page.tpl.php
+ * @see template_process_page()
+ */
+function template_process_wysiwyg_dialog_page(&$variables) {
+ template_process_page($variables);
+}
+
diff --git a/sites/all/modules/contrib/editor/wysiwyg/wysiwyg.features.inc b/sites/all/modules/contrib/editor/wysiwyg/wysiwyg.features.inc
new file mode 100644
index 00000000..5edd6421
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/wysiwyg.features.inc
@@ -0,0 +1,92 @@
+name;
+ }
+ }
+ return $profiles;
+}
+
+/**
+ * Implements hook_features_export().
+ */
+function wysiwyg_features_export($data, &$export, $module_name = '') {
+ $pipe = array();
+
+ // The wysiwyg_default_formats() hook integration is provided by the
+ // features module so we need to add it as a dependency.
+ $export['dependencies']['features'] = 'features';
+ $export['dependencies']['wysiwyg'] = 'wysiwyg';
+
+ foreach ($data as $name) {
+ if ($profile = wysiwyg_get_profile($name)) {
+ // Add profile to exports.
+ $export['features']['wysiwyg'][$profile->format] = $profile->format;
+
+ // Chain filter format for export.
+ $pipe['filter'][] = $profile->format;
+ }
+ }
+
+ return $pipe;
+}
+
+/**
+ * Implements hook_features_export_render().
+ */
+function wysiwyg_features_export_render($module, $data, $export = NULL) {
+ $code = array();
+ $code[] = ' $profiles = array();';
+ $code[] = '';
+
+ foreach ($data as $name) {
+ if ($profile = wysiwyg_get_profile($name)) {
+ $profile_export = features_var_export($profile, ' ');
+ $profile_identifier = features_var_export($profile->format);
+ $code[] = " // Exported profile: {$profile->format}";
+ $code[] = " \$profiles[{$profile_identifier}] = {$profile_export};";
+ $code[] = "";
+ }
+ }
+
+ $code[] = ' return $profiles;';
+ $code = implode("\n", $code);
+ return array('wysiwyg_default_profiles' => $code);
+}
+
+/**
+ * Implements hook_features_revert().
+ */
+function wysiwyg_features_revert($module) {
+ return wysiwyg_features_rebuild($module);
+}
+
+/**
+ * Implements hook_features_rebuild().
+ */
+function wysiwyg_features_rebuild($module) {
+ if ($defaults = features_get_default('wysiwyg', $module)) {
+ foreach ($defaults as $profile) {
+ db_merge('wysiwyg')
+ ->key(array('format' => $profile['format']))
+ ->fields(array(
+ 'editor' => $profile['editor'],
+ 'settings' => serialize($profile['settings']),
+ ))
+ ->execute();
+ }
+ wysiwyg_profile_cache_clear();
+ }
+}
+
diff --git a/sites/all/modules/contrib/editor/wysiwyg/wysiwyg.info b/sites/all/modules/contrib/editor/wysiwyg/wysiwyg.info
new file mode 100644
index 00000000..5f70c0cb
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/wysiwyg.info
@@ -0,0 +1,17 @@
+name = Wysiwyg
+description = Allows to edit content with client-side editors.
+package = User interface
+;dependencies[] = libraries
+;dependencies[] = ctools
+;dependencies[] = debug
+core = 7.x
+configure = admin/config/content/wysiwyg
+files[] = wysiwyg.module
+files[] = tests/wysiwyg.test
+
+; Information added by drupal.org packaging script on 2012-10-02
+version = "7.x-2.2"
+core = "7.x"
+project = "wysiwyg"
+datestamp = "1349213776"
+
diff --git a/sites/all/modules/contrib/editor/wysiwyg/wysiwyg.init.js b/sites/all/modules/contrib/editor/wysiwyg/wysiwyg.init.js
new file mode 100644
index 00000000..6ccdb314
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/wysiwyg.init.js
@@ -0,0 +1,19 @@
+
+Drupal.wysiwyg = Drupal.wysiwyg || { 'instances': {} };
+
+Drupal.wysiwyg.editor = Drupal.wysiwyg.editor || { 'init': {}, 'attach': {}, 'detach': {}, 'instance': {} };
+
+Drupal.wysiwyg.plugins = Drupal.wysiwyg.plugins || {};
+
+(function ($) {
+ // Determine support for queryCommandEnabled().
+ // An exception should be thrown for non-existing commands.
+ // Safari and Chrome (WebKit based) return -1 instead.
+ try {
+ document.queryCommandEnabled('__wysiwygTestCommand');
+ $.support.queryCommandEnabled = false;
+ }
+ catch (error) {
+ $.support.queryCommandEnabled = true;
+ }
+})(jQuery);
diff --git a/sites/all/modules/contrib/editor/wysiwyg/wysiwyg.install b/sites/all/modules/contrib/editor/wysiwyg/wysiwyg.install
new file mode 100644
index 00000000..e5dd046d
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/wysiwyg.install
@@ -0,0 +1,313 @@
+ 'Stores Wysiwyg profiles.',
+ 'fields' => array(
+ 'format' => array(
+ 'description' => 'The {filter_format}.format of the text format.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ // Primary keys are implicitly not null.
+ 'not null' => TRUE,
+ ),
+ 'editor' => array(
+ 'description' => 'Internal name of the editor attached to the text format.',
+ 'type' => 'varchar',
+ 'length' => 128,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'settings' => array(
+ 'description' => 'Configuration settings for the editor.',
+ 'type' => 'text',
+ 'size' => 'normal',
+ 'serialize' => TRUE,
+ ),
+ ),
+ 'primary key' => array('format'),
+ 'foreign keys' => array(
+ 'format' => array(
+ 'table' => 'filter_format',
+ 'columns' => array('format' => 'format'),
+ ),
+ ),
+ );
+ $schema['wysiwyg_user'] = array(
+ 'description' => 'Stores user preferences for wysiwyg profiles.',
+ 'fields' => array(
+ 'uid' => array(
+ 'description' => 'The {users}.uid of the user.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'format' => array(
+ 'description' => 'The {filter_format}.format of the text format.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => FALSE,
+ ),
+ 'status' => array(
+ 'description' => 'Boolean indicating whether the format is enabled by default.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'size' => 'tiny',
+ ),
+ ),
+ 'indexes' => array(
+ 'uid' => array('uid'),
+ 'format' => array('format'),
+ ),
+ 'foreign keys' => array(
+ 'uid' => array(
+ 'table' => 'users',
+ 'columns' => array('uid' => 'uid'),
+ ),
+ 'format' => array(
+ 'table' => 'filter_format',
+ 'columns' => array('format' => 'format'),
+ ),
+ ),
+ );
+ return $schema;
+}
+
+/**
+ * Implementation of hook_enable().
+ */
+function wysiwyg_enable() {
+ // Disable conflicting, obsolete editor integration modules whenever this
+ // module is enabled. This is crude, but the only way to ensure no conflicts.
+ module_disable(array(
+ 'ckeditor',
+ 'editarea',
+ 'editonpro',
+ 'editor',
+ 'fckeditor',
+ 'freerte',
+ 'htmlarea',
+ 'htmlbox',
+ 'jwysiwyg',
+ 'markitup',
+ 'nicedit',
+ 'openwysiwyg',
+ 'pegoeditor',
+ 'quicktext',
+ 'tinymce',
+ 'tinymce_autoconf',
+ 'tinytinymce',
+ 'whizzywig',
+ 'widgeditor',
+ 'wymeditor',
+ 'xstandard',
+ 'yui_editor',
+ ));
+}
+
+/**
+ * Implements hook_update_dependencies().
+ */
+function wysiwyg_update_dependencies() {
+ // Ensure that format columns are only changed after Filter module has changed
+ // the primary records.
+ $dependencies['wysiwyg'][7000] = array(
+ 'filter' => 7010,
+ );
+
+ return $dependencies;
+}
+
+/**
+ * Retrieve a list of input formats to associate profiles to.
+ */
+function _wysiwyg_install_get_formats() {
+ $formats = array();
+ $result = db_query("SELECT format, name FROM {filter_formats}");
+ while ($format = db_fetch_object($result)) {
+ // Build a list of all formats.
+ $formats[$format->format] = $format->name;
+ // Fetch filters.
+ $result2 = db_query("SELECT module, delta FROM {filters} WHERE format = %d", $format->format);
+ while ($filter = db_fetch_object($result2)) {
+ // If PHP filter is enabled, remove this format.
+ if ($filter->module == 'php') {
+ unset($formats[$format->format]);
+ break;
+ }
+ }
+ }
+ return $formats;
+}
+
+/**
+ * Associate Wysiwyg profiles with input formats.
+ *
+ * Since there was no association yet, we can only assume that there is one
+ * profile only, and that profile must be duplicated and assigned to all input
+ * formats (except PHP code format). Also, input formats already have
+ * titles/names, so Wysiwyg profiles do not need an own.
+ *
+ * Because input formats are already granted to certain user roles only, we can
+ * remove our custom Wysiwyg profile permissions. A 1:1 relationship between
+ * input formats and permissions makes plugin_count obsolete, too.
+ *
+ * Since the resulting table is completely different, a new schema is installed.
+ */
+function wysiwyg_update_6001() {
+ $ret = array();
+ if (db_table_exists('wysiwyg')) {
+ return $ret;
+ }
+ // Install new schema.
+ db_create_table($ret, 'wysiwyg', array(
+ 'fields' => array(
+ 'format' => array('type' => 'int', 'not null' => TRUE, 'default' => 0),
+ 'editor' => array('type' => 'varchar', 'length' => 128, 'not null' => TRUE, 'default' => ''),
+ 'settings' => array('type' => 'text', 'size' => 'normal'),
+ ),
+ 'primary key' => array('format'),
+ ));
+
+ // Fetch all input formats.
+ $formats = _wysiwyg_install_get_formats();
+
+ // Fetch all profiles.
+ $result = db_query("SELECT name, settings FROM {wysiwyg_profile}");
+ while ($profile = db_fetch_object($result)) {
+ $profile->settings = unserialize($profile->settings);
+ // Extract editor name from profile settings.
+ $profile->editor = $profile->settings['editor'];
+ // Clean-up.
+ unset($profile->settings['editor']);
+ unset($profile->settings['old_name']);
+ unset($profile->settings['name']);
+ unset($profile->settings['rids']);
+ // Sorry. There Can Be Only One. ;)
+ break;
+ }
+
+ if ($profile) {
+ // Rebuild profiles and associate with input formats.
+ foreach ($formats as $format => $name) {
+ // Insert profiles.
+ // We can't use update_sql() here because of curly braces in serialized
+ // array.
+ db_query("INSERT INTO {wysiwyg} (format, editor, settings) VALUES (%d, '%s', '%s')", $format, $profile->editor, serialize($profile->settings));
+ $ret[] = array(
+ 'success' => TRUE,
+ 'query' => strtr('Wysiwyg profile %profile converted and associated with input format %format.', array('%profile' => check_plain($profile->name), '%format' => check_plain($name))),
+ );
+ }
+ }
+
+ // Drop obsolete tables {wysiwyg_profile} and {wysiwyg_role}.
+ db_drop_table($ret, 'wysiwyg_profile');
+ db_drop_table($ret, 'wysiwyg_role');
+
+ return $ret;
+}
+
+/**
+ * Clear JS/CSS caches to ensure that clients load fresh copies.
+ */
+function wysiwyg_update_6200() {
+ $ret = array();
+ // Change query-strings on css/js files to enforce reload for all users.
+ _drupal_flush_css_js();
+
+ drupal_clear_css_cache();
+ drupal_clear_js_cache();
+
+ // Rebuild the menu to remove old admin/settings/wysiwyg/profile item.
+ menu_rebuild();
+
+ // Flush content caches.
+ cache_clear_all();
+
+ $ret[] = array(
+ 'success' => TRUE,
+ 'query' => 'Caches have been flushed.',
+ );
+ return $ret;
+}
+
+/**
+ * Change {wysiwyg}.format into a string.
+ */
+function wysiwyg_update_7000() {
+ db_drop_primary_key('wysiwyg');
+ db_change_field('wysiwyg', 'format', 'format', array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ ));
+ db_add_primary_key('wysiwyg', array('format'));
+}
+
+/**
+ * Create the {wysiwyg_user} table.
+ */
+function wysiwyg_update_7200() {
+ if (!db_table_exists('wysiwyg_user')) {
+ db_create_table('wysiwyg_user', array(
+ 'description' => 'Stores user preferences for wysiwyg profiles.',
+ 'fields' => array(
+ 'uid' => array(
+ 'description' => 'The {users}.uid of the user.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'format' => array(
+ 'description' => 'The {filter_format}.format of the text format.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => FALSE,
+ ),
+ 'status' => array(
+ 'description' => 'Boolean indicating whether the format is enabled by default.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'size' => 'tiny',
+ ),
+ ),
+ 'indexes' => array(
+ 'uid' => array('uid'),
+ 'format' => array('format'),
+ ),
+ 'foreign keys' => array(
+ 'uid' => array(
+ 'table' => 'users',
+ 'columns' => array('uid' => 'uid'),
+ ),
+ 'format' => array(
+ 'table' => 'filter_format',
+ 'columns' => array('format' => 'format'),
+ ),
+ ),
+ ));
+ }
+ else {
+ db_change_field('wysiwyg_user', 'format', 'format', array(
+ 'description' => 'The {filter_format}.format of the text format.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => FALSE,
+ ));
+ }
+}
diff --git a/sites/all/modules/contrib/editor/wysiwyg/wysiwyg.js b/sites/all/modules/contrib/editor/wysiwyg/wysiwyg.js
new file mode 100644
index 00000000..29e2c54b
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/wysiwyg.js
@@ -0,0 +1,269 @@
+(function($) {
+
+/**
+ * Initialize editor libraries.
+ *
+ * Some editors need to be initialized before the DOM is fully loaded. The
+ * init hook gives them a chance to do so.
+ */
+Drupal.wysiwygInit = function() {
+ // This breaks in Konqueror. Prevent it from running.
+ if (/KDE/.test(navigator.vendor)) {
+ return;
+ }
+ jQuery.each(Drupal.wysiwyg.editor.init, function(editor) {
+ // Clone, so original settings are not overwritten.
+ this(jQuery.extend(true, {}, Drupal.settings.wysiwyg.configs[editor]));
+ });
+};
+
+/**
+ * Attach editors to input formats and target elements (f.e. textareas).
+ *
+ * This behavior searches for input format selectors and formatting guidelines
+ * that have been preprocessed by Wysiwyg API. All CSS classes of those elements
+ * with the prefix 'wysiwyg-' are parsed into input format parameters, defining
+ * the input format, configured editor, target element id, and variable other
+ * properties, which are passed to the attach/detach hooks of the corresponding
+ * editor.
+ *
+ * Furthermore, an "enable/disable rich-text" toggle link is added after the
+ * target element to allow users to alter its contents in plain text.
+ *
+ * This is executed once, while editor attach/detach hooks can be invoked
+ * multiple times.
+ *
+ * @param context
+ * A DOM element, supplied by Drupal.attachBehaviors().
+ */
+Drupal.behaviors.attachWysiwyg = {
+ attach: function (context, settings) {
+ // This breaks in Konqueror. Prevent it from running.
+ if (/KDE/.test(navigator.vendor)) {
+ return;
+ }
+
+ $('.wysiwyg', context).once('wysiwyg', function () {
+ if (!this.id || typeof Drupal.settings.wysiwyg.triggers[this.id] === 'undefined') {
+ return;
+ }
+ var $this = $(this);
+ var params = Drupal.settings.wysiwyg.triggers[this.id];
+ for (var format in params) {
+ params[format].format = format;
+ params[format].trigger = this.id;
+ params[format].field = params.field;
+ }
+ var format = 'format' + this.value;
+ // Directly attach this editor, if the input format is enabled or there is
+ // only one input format at all.
+ if ($this.is(':input')) {
+ Drupal.wysiwygAttach(context, params[format]);
+ }
+ // Attach onChange handlers to input format selector elements.
+ if ($this.is('select')) {
+ $this.change(function() {
+ // If not disabled, detach the current and attach a new editor.
+ Drupal.wysiwygDetach(context, params[format]);
+ format = 'format' + this.value;
+ Drupal.wysiwygAttach(context, params[format]);
+ });
+ }
+ // Detach any editor when the containing form is submitted.
+ $('#' + params.field).parents('form').submit(function (event) {
+ // Do not detach if the event was cancelled.
+ if (event.isDefaultPrevented()) {
+ return;
+ }
+ Drupal.wysiwygDetach(context, params[format], 'serialize');
+ });
+ });
+ },
+
+ detach: function (context, settings, trigger) {
+ var wysiwygs;
+ // The 'serialize' trigger indicates that we should simply update the
+ // underlying element with the new text, without destroying the editor.
+ if (trigger == 'serialize') {
+ // Removing the wysiwyg-processed class guarantees that the editor will
+ // be reattached. Only do this if we're planning to destroy the editor.
+ wysiwygs = $('.wysiwyg-processed', context);
+ }
+ else {
+ wysiwygs = $('.wysiwyg', context).removeOnce('wysiwyg');
+ }
+ wysiwygs.each(function () {
+ var params = Drupal.settings.wysiwyg.triggers[this.id];
+ Drupal.wysiwygDetach(context, params, trigger);
+ });
+ }
+};
+
+/**
+ * Attach an editor to a target element.
+ *
+ * This tests whether the passed in editor implements the attach hook and
+ * invokes it if available. Editor profile settings are cloned first, so they
+ * cannot be overridden. After attaching the editor, the toggle link is shown
+ * again, except in case we are attaching no editor.
+ *
+ * @param context
+ * A DOM element, supplied by Drupal.attachBehaviors().
+ * @param params
+ * An object containing input format parameters.
+ */
+Drupal.wysiwygAttach = function(context, params) {
+ if (typeof Drupal.wysiwyg.editor.attach[params.editor] == 'function') {
+ // (Re-)initialize field instance.
+ Drupal.wysiwyg.instances[params.field] = {};
+ // Provide all input format parameters to editor instance.
+ jQuery.extend(Drupal.wysiwyg.instances[params.field], params);
+ // Provide editor callbacks for plugins, if available.
+ if (typeof Drupal.wysiwyg.editor.instance[params.editor] == 'object') {
+ jQuery.extend(Drupal.wysiwyg.instances[params.field], Drupal.wysiwyg.editor.instance[params.editor]);
+ }
+ // Store this field id, so (external) plugins can use it.
+ // @todo Wrong point in time. Probably can only supported by editors which
+ // support an onFocus() or similar event.
+ Drupal.wysiwyg.activeId = params.field;
+ // Attach or update toggle link, if enabled.
+ if (params.toggle) {
+ Drupal.wysiwygAttachToggleLink(context, params);
+ }
+ // Otherwise, ensure that toggle link is hidden.
+ else {
+ $('#wysiwyg-toggle-' + params.field).hide();
+ }
+ // Attach editor, if enabled by default or last state was enabled.
+ if (params.status) {
+ Drupal.wysiwyg.editor.attach[params.editor](context, params, (Drupal.settings.wysiwyg.configs[params.editor] ? jQuery.extend(true, {}, Drupal.settings.wysiwyg.configs[params.editor][params.format]) : {}));
+ }
+ // Otherwise, attach default behaviors.
+ else {
+ Drupal.wysiwyg.editor.attach.none(context, params);
+ Drupal.wysiwyg.instances[params.field].editor = 'none';
+ }
+ }
+};
+
+/**
+ * Detach all editors from a target element.
+ *
+ * @param context
+ * A DOM element, supplied by Drupal.attachBehaviors().
+ * @param params
+ * An object containing input format parameters.
+ * @param trigger
+ * A string describing what is causing the editor to be detached.
+ *
+ * @see Drupal.detachBehaviors
+ */
+Drupal.wysiwygDetach = function (context, params, trigger) {
+ // Do not attempt to detach an unknown editor instance (Ajax).
+ if (typeof Drupal.wysiwyg.instances[params.field] == 'undefined') {
+ return;
+ }
+ trigger = trigger || 'unload';
+ var editor = Drupal.wysiwyg.instances[params.field].editor;
+ if (jQuery.isFunction(Drupal.wysiwyg.editor.detach[editor])) {
+ Drupal.wysiwyg.editor.detach[editor](context, params, trigger);
+ }
+};
+
+/**
+ * Append or update an editor toggle link to a target element.
+ *
+ * @param context
+ * A DOM element, supplied by Drupal.attachBehaviors().
+ * @param params
+ * An object containing input format parameters.
+ */
+Drupal.wysiwygAttachToggleLink = function(context, params) {
+ if (!$('#wysiwyg-toggle-' + params.field).length) {
+ var text = document.createTextNode(params.status ? Drupal.settings.wysiwyg.disable : Drupal.settings.wysiwyg.enable);
+ var a = document.createElement('a');
+ $(a).attr({ id: 'wysiwyg-toggle-' + params.field, href: 'javascript:void(0);' }).append(text);
+ var div = document.createElement('div');
+ $(div).addClass('wysiwyg-toggle-wrapper').append(a);
+ $('#' + params.field).after(div);
+ }
+ $('#wysiwyg-toggle-' + params.field)
+ .html(params.status ? Drupal.settings.wysiwyg.disable : Drupal.settings.wysiwyg.enable).show()
+ .unbind('click.wysiwyg', Drupal.wysiwyg.toggleWysiwyg)
+ .bind('click.wysiwyg', { params: params, context: context }, Drupal.wysiwyg.toggleWysiwyg);
+
+ // Hide toggle link in case no editor is attached.
+ if (params.editor == 'none') {
+ $('#wysiwyg-toggle-' + params.field).hide();
+ }
+};
+
+/**
+ * Callback for the Enable/Disable rich editor link.
+ */
+Drupal.wysiwyg.toggleWysiwyg = function (event) {
+ var context = event.data.context;
+ var params = event.data.params;
+ if (params.status) {
+ // Detach current editor.
+ params.status = false;
+ Drupal.wysiwygDetach(context, params);
+ // After disabling the editor, re-attach default behaviors.
+ // @todo We HAVE TO invoke Drupal.wysiwygAttach() here.
+ Drupal.wysiwyg.editor.attach.none(context, params);
+ Drupal.wysiwyg.instances[params.field] = Drupal.wysiwyg.editor.instance.none;
+ Drupal.wysiwyg.instances[params.field].editor = 'none';
+ Drupal.wysiwyg.instances[params.field].field = params.field;
+ $(this).html(Drupal.settings.wysiwyg.enable).blur();
+ }
+ else {
+ // Before enabling the editor, detach default behaviors.
+ Drupal.wysiwyg.editor.detach.none(context, params);
+ // Attach new editor using parameters of the currently selected input format.
+ params = Drupal.settings.wysiwyg.triggers[params.trigger]['format' + $('#' + params.trigger).val()];
+ params.status = true;
+ Drupal.wysiwygAttach(context, params);
+ $(this).html(Drupal.settings.wysiwyg.disable).blur();
+ }
+}
+
+/**
+ * Parse the CSS classes of an input format DOM element into parameters.
+ *
+ * Syntax for CSS classes is "wysiwyg-name-value".
+ *
+ * @param element
+ * An input format DOM element containing CSS classes to parse.
+ * @param params
+ * (optional) An object containing input format parameters to update.
+ */
+Drupal.wysiwyg.getParams = function(element, params) {
+ var classes = element.className.split(' ');
+ var params = params || {};
+ for (var i = 0; i < classes.length; i++) {
+ if (classes[i].substr(0, 8) == 'wysiwyg-') {
+ var parts = classes[i].split('-');
+ var value = parts.slice(2).join('-');
+ params[parts[1]] = value;
+ }
+ }
+ // Convert format id into string.
+ params.format = 'format' + params.format;
+ // Convert numeric values.
+ params.status = parseInt(params.status, 10);
+ params.toggle = parseInt(params.toggle, 10);
+ params.resizable = parseInt(params.resizable, 10);
+ return params;
+};
+
+/**
+ * Allow certain editor libraries to initialize before the DOM is loaded.
+ */
+Drupal.wysiwygInit();
+
+// Respond to CTools detach behaviors event.
+$(document).bind('CToolsDetachBehaviors', function(event, context) {
+ Drupal.behaviors.attachWysiwyg.detach(context, {}, 'unload');
+});
+
+})(jQuery);
diff --git a/sites/all/modules/contrib/editor/wysiwyg/wysiwyg.module b/sites/all/modules/contrib/editor/wysiwyg/wysiwyg.module
new file mode 100644
index 00000000..22130eab
--- /dev/null
+++ b/sites/all/modules/contrib/editor/wysiwyg/wysiwyg.module
@@ -0,0 +1,1135 @@
+ t('Wysiwyg profile'),
+ 'base table' => 'wysiwyg',
+ 'controller class' => 'WysiwygProfileController',
+ 'fieldable' => FALSE,
+ // When loading all entities, DrupalDefaultEntityController::load() ignores
+ // its static cache. Therefore, wysiwyg_profile_load_all() implements a
+ // custom static cache.
+ 'static cache' => FALSE,
+ 'entity keys' => array(
+ 'id' => 'format',
+ ),
+ );
+ return $types;
+}
+
+/**
+ * Controller class for Wysiwyg profiles.
+ */
+class WysiwygProfileController extends DrupalDefaultEntityController {
+ /**
+ * Overrides DrupalDefaultEntityController::attachLoad().
+ */
+ function attachLoad(&$queried_entities, $revision_id = FALSE) {
+ // Unserialize the profile settings.
+ foreach ($queried_entities as $key => $record) {
+ $queried_entities[$key]->settings = unserialize($record->settings);
+ }
+ // Call the default attachLoad() method.
+ parent::attachLoad($queried_entities, $revision_id);
+ }
+}
+
+/**
+ * Implementation of hook_menu().
+ */
+function wysiwyg_menu() {
+ $items['admin/config/content/wysiwyg'] = array(
+ 'title' => 'Wysiwyg profiles',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('wysiwyg_profile_overview'),
+ 'description' => 'Configure client-side editors.',
+ 'access arguments' => array('administer filters'),
+ 'file' => 'wysiwyg.admin.inc',
+ );
+ $items['admin/config/content/wysiwyg/profile'] = array(
+ 'title' => 'List',
+ 'type' => MENU_DEFAULT_LOCAL_TASK,
+ );
+ $items['admin/config/content/wysiwyg/profile/%wysiwyg_profile/edit'] = array(
+ 'title' => 'Edit',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('wysiwyg_profile_form', 5),
+ 'access arguments' => array('administer filters'),
+ 'file' => 'wysiwyg.admin.inc',
+ 'tab_root' => 'admin/config/content/wysiwyg/profile',
+ 'tab_parent' => 'admin/config/content/wysiwyg/profile/%wysiwyg_profile',
+ 'type' => MENU_LOCAL_TASK,
+ );
+ $items['admin/config/content/wysiwyg/profile/%wysiwyg_profile/delete'] = array(
+ 'title' => 'Remove',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('wysiwyg_profile_delete_confirm', 5),
+ 'access arguments' => array('administer filters'),
+ 'file' => 'wysiwyg.admin.inc',
+ 'tab_root' => 'admin/config/content/wysiwyg/profile',
+ 'tab_parent' => 'admin/config/content/wysiwyg/profile/%wysiwyg_profile',
+ 'type' => MENU_LOCAL_TASK,
+ 'weight' => 10,
+ );
+ // @see wysiwyg_dialog()
+ $items['wysiwyg/%'] = array(
+ 'page callback' => 'wysiwyg_dialog',
+ 'page arguments' => array(1),
+ 'delivery callback' => 'wysiwyg_deliver_dialog_page',
+ 'access arguments' => array('access content'),
+ 'type' => MENU_CALLBACK,
+ 'file' => 'wysiwyg.dialog.inc',
+ );
+ return $items;
+}
+
+/**
+ * Implements hook_element_info().
+ */
+function wysiwyg_element_info() {
+ // @see wysiwyg_dialog()
+ $types['wysiwyg_dialog_page'] = array(
+ '#theme' => 'wysiwyg_dialog_page',
+ '#theme_wrappers' => array('html'),
+ '#show_messages' => TRUE,
+ );
+ return $types;
+}
+
+/**
+ * Implementation of hook_theme().
+ *
+ * @see drupal_common_theme(), common.inc
+ * @see template_preprocess_page(), theme.inc
+ */
+function wysiwyg_theme() {
+ return array(
+ 'wysiwyg_profile_overview' => array(
+ 'render element' => 'form',
+ ),
+ 'wysiwyg_admin_button_table' => array(
+ 'render element' => 'form',
+ ),
+ // @see wysiwyg_dialog()
+ 'wysiwyg_dialog_page' => array(
+ 'render element' => 'page',
+ 'file' => 'wysiwyg.dialog.inc',
+ 'template' => 'wysiwyg-dialog-page',
+ ),
+ );
+}
+
+/**
+ * Implementation of hook_help().
+ */
+function wysiwyg_help($path, $arg) {
+ switch ($path) {
+ case 'admin/config/content/wysiwyg':
+ $output = '' . t('A Wysiwyg profile is associated with a text format. A Wysiwyg profile defines which client-side editor is loaded with a particular text format, what buttons or themes are enabled for the editor, how the editor is displayed, and a few other editor-specific functions.') . '
';
+ return $output;
+ }
+}
+
+/**
+ * Implementation of hook_form_alter().
+ */
+function wysiwyg_form_alter(&$form, &$form_state) {
+ // Teaser splitter is unconditionally removed and NOT supported.
+ if (isset($form['body_field'])) {
+ unset($form['body_field']['teaser_js']);
+ }
+}
+
+/**
+ * Implements hook_element_info_alter().
+ */
+function wysiwyg_element_info_alter(&$types) {
+ $types['text_format']['#pre_render'][] = 'wysiwyg_pre_render_text_format';
+}
+
+/**
+ * Process a text format widget to load and attach editors.
+ *
+ * The element's #id is used as reference to attach client-side editors.
+ */
+function wysiwyg_pre_render_text_format($element) {
+ // filter_process_format() copies properties to the expanded 'value' child
+ // element. Skip this text format widget, if it contains no 'format' or when
+ // the current user does not have access to edit the value.
+ if (!isset($element['format']) || !empty($element['value']['#disabled'])) {
+ return $element;
+ }
+ // Allow modules to programmatically enforce no client-side editor by setting
+ // the #wysiwyg property to FALSE.
+ if (isset($element['#wysiwyg']) && !$element['#wysiwyg']) {
+ return $element;
+ }
+
+ $format_field = &$element['format'];
+ $field = &$element['value'];
+ $settings = array(
+ 'field' => $field['#id'],
+ );
+
+ // If this textarea is #resizable and we will load at least one
+ // editor, then only load the behavior and let the 'none' editor
+ // attach/detach it to avoid hi-jacking the UI. Due to our CSS class
+ // parsing, we can add arbitrary parameters for each input format.
+ // The #resizable property will be removed below, if at least one
+ // profile has been loaded.
+ $resizable = 0;
+ if (!empty($field['#resizable'])) {
+ $resizable = 1;
+ drupal_add_js('misc/textarea.js');
+ }
+ // Determine the available text formats.
+ foreach ($format_field['format']['#options'] as $format_id => $format_name) {
+ $format = 'format' . $format_id;
+ // Initialize default settings, defaulting to 'none' editor.
+ $settings[$format] = array(
+ 'editor' => 'none',
+ 'status' => 1,
+ 'toggle' => 1,
+ 'resizable' => $resizable,
+ );
+
+ // Fetch the profile associated to this text format.
+ $profile = wysiwyg_get_profile($format_id);
+ if ($profile) {
+ $loaded = TRUE;
+ $settings[$format]['editor'] = $profile->editor;
+ $settings[$format]['status'] = (int) wysiwyg_user_get_status($profile);
+ if (isset($profile->settings['show_toggle'])) {
+ $settings[$format]['toggle'] = (int) $profile->settings['show_toggle'];
+ }
+ // Check editor theme (and reset it if not/no longer available).
+ $theme = wysiwyg_get_editor_themes($profile, (isset($profile->settings['theme']) ? $profile->settings['theme'] : ''));
+
+ // Add plugin settings (first) for this text format.
+ wysiwyg_add_plugin_settings($profile);
+ // Add profile settings for this text format.
+ wysiwyg_add_editor_settings($profile, $theme);
+ }
+ }
+ // Use a hidden element for a single text format.
+ if (!$format_field['format']['#access']) {
+ $format_field['wysiwyg'] = array(
+ '#type' => 'hidden',
+ '#name' => $format_field['format']['#name'],
+ '#value' => $format_id,
+ '#attributes' => array(
+ 'id' => $format_field['format']['#id'],
+ 'class' => array('wysiwyg'),
+ ),
+ );
+ $format_field['wysiwyg']['#attached']['js'][] = array(
+ 'data' => array(
+ 'wysiwyg' => array(
+ 'triggers' => array(
+ $format_field['format']['#id'] => $settings,
+ ),
+ ),
+ ),
+ 'type' => 'setting',
+ );
+ }
+ // Otherwise, attach to text format selector.
+ else {
+ $format_field['format']['#attributes']['class'][] = 'wysiwyg';
+ $format_field['format']['#attached']['js'][] = array(
+ 'data' => array(
+ 'wysiwyg' => array(
+ 'triggers' => array(
+ $format_field['format']['#id'] => $settings,
+ ),
+ ),
+ ),
+ 'type' => 'setting',
+ );
+ }
+
+ // If we loaded at least one editor, then the 'none' editor will
+ // handle resizable textareas instead of core.
+ if (isset($loaded) && $resizable) {
+ $field['#resizable'] = FALSE;
+ }
+
+ return $element;
+}
+
+/**
+ * Determine the profile to use for a given input format id.
+ *
+ * This function also performs sanity checks for the configured editor in a
+ * profile to ensure that we do not load a malformed editor.
+ *
+ * @param $format
+ * The internal id of an input format.
+ *
+ * @return
+ * A wysiwyg profile.
+ *
+ * @see wysiwyg_load_editor(), wysiwyg_get_editor()
+ */
+function wysiwyg_get_profile($format) {
+ if ($profile = wysiwyg_profile_load($format)) {
+ if (wysiwyg_load_editor($profile)) {
+ return $profile;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * Load an editor library and initialize basic Wysiwyg settings.
+ *
+ * @param $profile
+ * A wysiwyg editor profile.
+ *
+ * @return
+ * TRUE if the editor has been loaded, FALSE if not.
+ *
+ * @see wysiwyg_get_profile()
+ */
+function wysiwyg_load_editor($profile) {
+ static $settings_added;
+ static $loaded = array();
+ $path = drupal_get_path('module', 'wysiwyg');
+
+ $name = $profile->editor;
+ // Library files must be loaded only once.
+ if (!isset($loaded[$name])) {
+ // Load editor.
+ $editor = wysiwyg_get_editor($name);
+ if ($editor) {
+ $default_library_options = array(
+ 'type' => 'file',
+ 'scope' => 'header',
+ 'defer' => FALSE,
+ 'cache' => TRUE,
+ 'preprocess' => TRUE,
+ );
+ // Determine library files to load.
+ // @todo Allow to configure the library/execMode to use.
+ if (isset($profile->settings['library']) && isset($editor['libraries'][$profile->settings['library']])) {
+ $library = $profile->settings['library'];
+ $files = $editor['libraries'][$library]['files'];
+ }
+ else {
+ // Fallback to the first defined library by default (external libraries can change).
+ $library = key($editor['libraries']);
+ $files = array_shift($editor['libraries']);
+ $files = $files['files'];
+ }
+
+ // Check whether the editor requires an initialization script.
+ if (!empty($editor['init callback'])) {
+ $init = $editor['init callback']($editor, $library, $profile);
+ if (!empty($init)) {
+ // Build a file for each of the editors to hold the init scripts.
+ // @todo Aggregate all initialization scripts into one file.
+ $uri = 'public://js/wysiwyg/wysiwyg_' . $name . '_' . drupal_hash_base64($init) . '.js';
+ $init_exists = file_exists($uri);
+ if (!$init_exists) {
+ $js_path = dirname($uri);
+ file_prepare_directory($js_path, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
+ }
+ // Attempt to create the file, or fall back to an inline script (which
+ // will not work in Ajax calls).
+ if (!$init_exists && !file_unmanaged_save_data($init, $uri, FILE_EXISTS_REPLACE)) {
+ drupal_add_js($init, array('type' => 'inline') + $default_library_options);
+ }
+ else {
+ drupal_add_js(file_create_url($uri), $default_library_options);
+ }
+ }
+ }
+
+ foreach ($files as $file => $options) {
+ if (is_array($options)) {
+ $options += $default_library_options;
+ drupal_add_js($editor['library path'] . '/' . $file, $options);
+ }
+ else {
+ drupal_add_js($editor['library path'] . '/' . $options);
+ }
+ }
+ // If editor defines an additional load callback, invoke it.
+ // @todo Isn't the settings callback sufficient?
+ if (isset($editor['load callback']) && function_exists($editor['load callback'])) {
+ $editor['load callback']($editor, $library);
+ }
+ // Load JavaScript integration files for this editor.
+ $files = array();
+ if (isset($editor['js files'])) {
+ $files = $editor['js files'];
+ }
+ foreach ($files as $file) {
+ drupal_add_js($editor['js path'] . '/' . $file);
+ }
+ // Load CSS stylesheets for this editor.
+ $files = array();
+ if (isset($editor['css files'])) {
+ $files = $editor['css files'];
+ }
+ foreach ($files as $file) {
+ drupal_add_css($editor['css path'] . '/' . $file);
+ }
+ $loaded[$name] = TRUE;
+ }
+ else {
+ $loaded[$name] = FALSE;
+ }
+ }
+
+ // Add basic Wysiwyg settings if any editor has been added.
+ if (!isset($settings_added) && $loaded[$name]) {
+ drupal_add_js(array('wysiwyg' => array(
+ 'configs' => array(),
+ 'plugins' => array(),
+ 'disable' => t('Disable rich-text'),
+ 'enable' => t('Enable rich-text'),
+ )), 'setting');
+
+ // Initialize our namespaces in the *header* to do not force editor
+ // integration scripts to check and define Drupal.wysiwyg on its own.
+ drupal_add_js($path . '/wysiwyg.init.js', array('group' => JS_LIBRARY));
+
+ // The 'none' editor is a special editor implementation, allowing us to
+ // attach and detach regular Drupal behaviors just like any other editor.
+ drupal_add_js($path . '/editors/js/none.js');
+
+ // Add wysiwyg.js to the footer to ensure it's executed after the
+ // Drupal.settings array has been rendered and populated. Also, since editor
+ // library initialization functions must be loaded first by the browser,
+ // and Drupal.wysiwygInit() must be executed AFTER editors registered
+ // their callbacks and BEFORE Drupal.behaviors are applied, this must come
+ // last.
+ drupal_add_js($path . '/wysiwyg.js', array('scope' => 'footer'));
+
+ $settings_added = TRUE;
+ }
+
+ return $loaded[$name];
+}
+
+/**
+ * Add editor settings for a given input format.
+ */
+function wysiwyg_add_editor_settings($profile, $theme) {
+ static $formats = array();
+
+ if (!isset($formats[$profile->format])) {
+ $config = wysiwyg_get_editor_config($profile, $theme);
+ // drupal_to_js() does not properly convert numeric array keys, so we need
+ // to use a string instead of the format id.
+ if ($config) {
+ drupal_add_js(array('wysiwyg' => array('configs' => array($profile->editor => array('format' . $profile->format => $config)))), 'setting');
+ }
+ $formats[$profile->format] = TRUE;
+ }
+}
+
+/**
+ * Add settings for external plugins.
+ *
+ * Plugins can be used in multiple profiles, but not necessarily in all. Because
+ * of that, we need to process plugins for each profile, even if most of their
+ * settings are not stored per profile.
+ *
+ * Implementations of hook_wysiwyg_plugin() may execute different code for each
+ * editor. Therefore, we have to invoke those implementations for each editor,
+ * but process the resulting plugins separately for each profile.
+ *
+ * Drupal plugins differ to native plugins in that they have plugin-specific
+ * definitions and settings, which need to be processed only once. But they are
+ * also passed to the editor to prepare settings specific to the editor.
+ * Therefore, we load and process the Drupal plugins only once, and hand off the
+ * effective definitions for each profile to the editor.
+ *
+ * @param $profile
+ * A wysiwyg editor profile.
+ *
+ * @todo Rewrite wysiwyg_process_form() to build a registry of effective
+ * profiles in use, so we can process plugins in multiple profiles in one shot
+ * and simplify this entire function.
+ */
+function wysiwyg_add_plugin_settings($profile) {
+ static $plugins = array();
+ static $processed_plugins = array();
+ static $processed_formats = array();
+
+ // Each input format must only processed once.
+ // @todo ...as long as we do not have multiple profiles per format.
+ if (isset($processed_formats[$profile->format])) {
+ return;
+ }
+ $processed_formats[$profile->format] = TRUE;
+
+ $editor = wysiwyg_get_editor($profile->editor);
+
+ // Collect native plugins for this editor provided via hook_wysiwyg_plugin()
+ // and Drupal plugins provided via hook_wysiwyg_include_directory().
+ if (!array_key_exists($editor['name'], $plugins)) {
+ $plugins[$editor['name']] = wysiwyg_get_plugins($editor['name']);
+ }
+
+ // Nothing to do, if there are no plugins.
+ if (empty($plugins[$editor['name']])) {
+ return;
+ }
+
+ // Determine name of proxy plugin for Drupal plugins.
+ $proxy = (isset($editor['proxy plugin']) ? key($editor['proxy plugin']) : '');
+
+ // Process native editor plugins.
+ if (isset($editor['plugin settings callback'])) {
+ // @todo Require PHP 5.1 in 3.x and use array_intersect_key().
+ $profile_plugins_native = array();
+ foreach ($plugins[$editor['name']] as $plugin => $meta) {
+ // Skip Drupal plugins (handled below).
+ if ($plugin === $proxy) {
+ continue;
+ }
+ // Only keep native plugins that are enabled in this profile.
+ if (isset($profile->settings['buttons'][$plugin])) {
+ $profile_plugins_native[$plugin] = $meta;
+ }
+ }
+ // Invoke the editor's plugin settings callback, so it can populate the
+ // settings for native external plugins with required values.
+ $settings_native = call_user_func($editor['plugin settings callback'], $editor, $profile, $profile_plugins_native);
+
+ if ($settings_native) {
+ drupal_add_js(array('wysiwyg' => array('plugins' => array('format' . $profile->format => array('native' => $settings_native)))), 'setting');
+ }
+ }
+
+ // Process Drupal plugins.
+ if ($proxy && isset($editor['proxy plugin settings callback'])) {
+ $profile_plugins_drupal = array();
+ foreach (wysiwyg_get_all_plugins() as $plugin => $meta) {
+ if (isset($profile->settings['buttons'][$proxy][$plugin])) {
+ // JavaScript and plugin-specific settings for Drupal plugins must be
+ // loaded and processed only once. Plugin information is cached
+ // statically to pass it to the editor's proxy plugin settings callback.
+ if (!isset($processed_plugins[$proxy][$plugin])) {
+ $profile_plugins_drupal[$plugin] = $processed_plugins[$proxy][$plugin] = $meta;
+ // Load the Drupal plugin's JavaScript.
+ drupal_add_js($meta['js path'] . '/' . $meta['js file']);
+ // Add plugin-specific settings.
+ if (isset($meta['settings'])) {
+ drupal_add_js(array('wysiwyg' => array('plugins' => array('drupal' => array($plugin => $meta['settings'])))), 'setting');
+ }
+ }
+ else {
+ $profile_plugins_drupal[$plugin] = $processed_plugins[$proxy][$plugin];
+ }
+ }
+ }
+ // Invoke the editor's proxy plugin settings callback, so it can populate
+ // the settings for Drupal plugins with custom, required values.
+ $settings_drupal = call_user_func($editor['proxy plugin settings callback'], $editor, $profile, $profile_plugins_drupal);
+
+ if ($settings_drupal) {
+ drupal_add_js(array('wysiwyg' => array('plugins' => array('format' . $profile->format => array('drupal' => $settings_drupal)))), 'setting');
+ }
+ }
+}
+
+/**
+ * Retrieve available themes for an editor.
+ *
+ * Editor themes control the visual presentation of an editor.
+ *
+ * @param $profile
+ * A wysiwyg editor profile; passed/altered by reference.
+ * @param $selected_theme
+ * An optional theme name that ought to be used.
+ *
+ * @return
+ * An array of theme names, or a single, checked theme name if $selected_theme
+ * was given.
+ */
+function wysiwyg_get_editor_themes(&$profile, $selected_theme = NULL) {
+ static $themes = array();
+
+ if (!isset($themes[$profile->editor])) {
+ $editor = wysiwyg_get_editor($profile->editor);
+ if (isset($editor['themes callback']) && function_exists($editor['themes callback'])) {
+ $themes[$editor['name']] = $editor['themes callback']($editor, $profile);
+ }
+ // Fallback to 'default' otherwise.
+ else {
+ $themes[$editor['name']] = array('default');
+ }
+ }
+
+ // Check optional $selected_theme argument, if given.
+ if (isset($selected_theme)) {
+ // If the passed theme name does not exist, use the first available.
+ if (!in_array($selected_theme, $themes[$profile->editor])) {
+ $selected_theme = $profile->settings['theme'] = $themes[$profile->editor][0];
+ }
+ }
+
+ return isset($selected_theme) ? $selected_theme : $themes[$profile->editor];
+}
+
+/**
+ * Return plugin metadata from the plugin registry.
+ *
+ * @param $editor_name
+ * The internal name of an editor to return plugins for.
+ *
+ * @return
+ * An array for each plugin.
+ */
+function wysiwyg_get_plugins($editor_name) {
+ $plugins = array();
+ if (!empty($editor_name)) {
+ $editor = wysiwyg_get_editor($editor_name);
+ // Add internal editor plugins.
+ if (isset($editor['plugin callback']) && function_exists($editor['plugin callback'])) {
+ $plugins = $editor['plugin callback']($editor);
+ }
+ // Add editor plugins provided via hook_wysiwyg_plugin().
+ $plugins = array_merge($plugins, module_invoke_all('wysiwyg_plugin', $editor['name'], $editor['installed version']));
+ // Add API plugins provided by Drupal modules.
+ // @todo We need to pass the filepath to the plugin icon for Drupal plugins.
+ if (isset($editor['proxy plugin'])) {
+ $plugins += $editor['proxy plugin'];
+ $proxy = key($editor['proxy plugin']);
+ foreach (wysiwyg_get_all_plugins() as $plugin_name => $info) {
+ $plugins[$proxy]['buttons'][$plugin_name] = $info['title'];
+ }
+ }
+ }
+ return $plugins;
+}
+
+/**
+ * Return an array of initial editor settings for a Wysiwyg profile.
+ */
+function wysiwyg_get_editor_config($profile, $theme) {
+ $editor = wysiwyg_get_editor($profile->editor);
+ $settings = array();
+ if (!empty($editor['settings callback']) && function_exists($editor['settings callback'])) {
+ $settings = $editor['settings callback']($editor, $profile->settings, $theme);
+
+ // Allow other modules to alter the editor settings for this format.
+ $context = array('editor' => $editor, 'profile' => $profile, 'theme' => $theme);
+ drupal_alter('wysiwyg_editor_settings', $settings, $context);
+ }
+ return $settings;
+}
+
+/**
+ * Retrieve stylesheets for HTML/IFRAME-based editors.
+ *
+ * This assumes that the content editing area only needs stylesheets defined
+ * for the scope 'theme'.
+ *
+ * @return
+ * An array containing CSS files, including proper base path.
+ */
+function wysiwyg_get_css() {
+ static $files;
+
+ if (isset($files)) {
+ return $files;
+ }
+ // In node form previews, the theme has not been initialized yet.
+ if (!empty($_POST)) {
+ drupal_theme_initialize();
+ }
+
+ $files = array();
+ foreach (drupal_add_css() as $filepath => $info) {
+ if ($info['group'] >= CSS_THEME && $info['media'] != 'print') {
+ if ($info['type'] == 'external') {
+ $files[] = $filepath;
+ }
+ elseif (file_exists($filepath)) {
+ $files[] = base_path() . $filepath;
+ }
+ }
+ }
+ return $files;
+}
+
+/**
+ * Loads a profile for a given text format.
+ *
+ * Since there are commonly not many text formats, and each text format-enabled
+ * form element will possibly have to load every single profile, all existing
+ * profiles are loaded and cached once to reduce the amount of database queries.
+ */
+function wysiwyg_profile_load($format) {
+ $profiles = wysiwyg_profile_load_all();
+ return (isset($profiles[$format]) ? $profiles[$format] : FALSE);
+}
+
+/**
+ * Loads all profiles.
+ */
+function wysiwyg_profile_load_all() {
+ // entity_load(..., FALSE) does not re-use its own static cache upon
+ // repetitive calls, so a custom static cache is required.
+ // @see wysiwyg_entity_info()
+ $profiles = &drupal_static(__FUNCTION__);
+
+ if (!isset($profiles)) {
+ // Additional database cache to support alternative caches like memcache.
+ if ($cached = cache_get('wysiwyg_profiles')) {
+ $profiles = $cached->data;
+ }
+ else {
+ $profiles = entity_load('wysiwyg_profile', FALSE);
+ cache_set('wysiwyg_profiles', $profiles);
+ }
+ }
+
+ return $profiles;
+}
+
+/**
+ * Deletes a profile from the database.
+ */
+function wysiwyg_profile_delete($format) {
+ db_delete('wysiwyg')
+ ->condition('format', $format)
+ ->execute();
+}
+
+/**
+ * Clear all Wysiwyg profile caches.
+ */
+function wysiwyg_profile_cache_clear() {
+ entity_get_controller('wysiwyg_profile')->resetCache();
+ drupal_static_reset('wysiwyg_profile_load_all');
+ cache_clear_all('wysiwyg_profiles', 'cache');
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ */
+function wysiwyg_form_user_profile_form_alter(&$form, &$form_state, $form_id) {
+ $account = $form['#user'];
+ $user_formats = filter_formats($account);
+ $options = array();
+ $options_default = array();
+ foreach (wysiwyg_profile_load_all() as $format => $profile) {
+ // Only show profiles that have user_choose enabled.
+ if (!empty($profile->settings['user_choose']) && isset($user_formats[$format])) {
+ $options[$format] = check_plain($user_formats[$format]->name);
+ if (wysiwyg_user_get_status($profile, $account)) {
+ $options_default[] = $format;
+ }
+ }
+ }
+ if (!empty($options)) {
+ $form['wysiwyg']['wysiwyg_status'] = array(
+ '#type' => 'checkboxes',
+ '#title' => t('Text formats enabled for rich-text editing'),
+ '#options' => $options,
+ '#default_value' => $options_default,
+ );
+ }
+}
+
+/**
+ * Implements hook_user_insert().
+ *
+ * Wysiwyg's user preferences are normally not exposed on the user registration
+ * form, but in case they are manually altered in, we invoke
+ * wysiwyg_user_update() accordingly.
+ */
+function wysiwyg_user_insert(&$edit, $account, $category) {
+ wysiwyg_user_update($edit, $account, $category);
+}
+
+/**
+ * Implements hook_user_update().
+ */
+function wysiwyg_user_update(&$edit, $account, $category) {
+ if (isset($edit['wysiwyg_status'])) {
+ db_delete('wysiwyg_user')
+ ->condition('uid', $account->uid)
+ ->execute();
+ $query = db_insert('wysiwyg_user')
+ ->fields(array('uid', 'format', 'status'));
+ foreach ($edit['wysiwyg_status'] as $format => $status) {
+ $query->values(array(
+ 'uid' => $account->uid,
+ 'format' => $format,
+ 'status' => (int) (bool) $status,
+ ));
+ }
+ $query->execute();
+ }
+}
+
+function wysiwyg_user_get_status($profile, $account = NULL) {
+ global $user;
+
+ if (!isset($account)) {
+ $account = $user;
+ }
+
+ // Default wysiwyg editor status information is only required on forms, so we
+ // do not pre-emptively load and attach this information on every user_load().
+ if (!isset($account->wysiwyg_status)) {
+ $account->wysiwyg_status = db_query("SELECT format, status FROM {wysiwyg_user} WHERE uid = :uid", array(
+ ':uid' => $account->uid,
+ ))->fetchAllKeyed();
+ }
+
+ if (!empty($profile->settings['user_choose']) && isset($account->wysiwyg_status[$profile->format])) {
+ $status = $account->wysiwyg_status[$profile->format];
+ }
+ else {
+ $status = isset($profile->settings['default']) ? $profile->settings['default'] : TRUE;
+ }
+
+ return (bool) $status;
+}
+
+/**
+ * @defgroup wysiwyg_api Wysiwyg API
+ * @{
+ *
+ * @todo Forked from Panels; abstract into a separate API module that allows
+ * contrib modules to define supported include/plugin types.
+ */
+
+/**
+ * Return library information for a given editor.
+ *
+ * @param $name
+ * The internal name of an editor.
+ *
+ * @return
+ * The library information for the editor, or FALSE if $name is unknown or not
+ * installed properly.
+ */
+function wysiwyg_get_editor($name) {
+ $editors = wysiwyg_get_all_editors();
+ return isset($editors[$name]) && $editors[$name]['installed'] ? $editors[$name] : FALSE;
+}
+
+/**
+ * Compile a list holding all supported editors including installed editor version information.
+ */
+function wysiwyg_get_all_editors() {
+ static $editors;
+
+ if (isset($editors)) {
+ return $editors;
+ }
+
+ $editors = wysiwyg_load_includes('editors', 'editor');
+ foreach ($editors as $editor => $properties) {
+ // Fill in required properties.
+ $editors[$editor] += array(
+ 'title' => '',
+ 'vendor url' => '',
+ 'download url' => '',
+ 'editor path' => wysiwyg_get_path($editors[$editor]['name']),
+ 'library path' => wysiwyg_get_path($editors[$editor]['name']),
+ 'libraries' => array(),
+ 'version callback' => NULL,
+ 'themes callback' => NULL,
+ 'settings form callback' => NULL,
+ 'settings callback' => NULL,
+ 'plugin callback' => NULL,
+ 'plugin settings callback' => NULL,
+ 'versions' => array(),
+ 'js path' => $editors[$editor]['path'] . '/js',
+ 'css path' => $editors[$editor]['path'] . '/css',
+ );
+ // Check whether library is present.
+ if (!($editors[$editor]['installed'] = file_exists($editors[$editor]['library path']))) {
+ continue;
+ }
+ // Detect library version.
+ if (function_exists($editors[$editor]['version callback'])) {
+ $editors[$editor]['installed version'] = $editors[$editor]['version callback']($editors[$editor]);
+ }
+ if (empty($editors[$editor]['installed version'])) {
+ $editors[$editor]['error'] = t('The version of %editor could not be detected.', array('%editor' => $properties['title']));
+ $editors[$editor]['installed'] = FALSE;
+ continue;
+ }
+ // Determine to which supported version the installed version maps.
+ ksort($editors[$editor]['versions']);
+ $version = 0;
+ foreach ($editors[$editor]['versions'] as $supported_version => $version_properties) {
+ if (version_compare($editors[$editor]['installed version'], $supported_version, '>=')) {
+ $version = $supported_version;
+ }
+ }
+ if (!$version) {
+ $editors[$editor]['error'] = t('The installed version %version of %editor is not supported.', array('%version' => $editors[$editor]['installed version'], '%editor' => $editors[$editor]['title']));
+ $editors[$editor]['installed'] = FALSE;
+ continue;
+ }
+ // Apply library version specific definitions and overrides.
+ $editors[$editor] = array_merge($editors[$editor], $editors[$editor]['versions'][$version]);
+ unset($editors[$editor]['versions']);
+ }
+ return $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/default 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,
+ );
+ // Fill in default settings.
+ $plugin['settings'] += array(
+ 'path' => base_path() . $plugin['path'] . '/' . $plugin['name'],
+ );
+ // 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
+ * The type of includes to search for, can be 'editors'.
+ * @param $hook
+ * The hook name to invoke.
+ * @param $file
+ * An optional include file name without .inc extension to limit the search to.
+ *
+ * @see wysiwyg_get_directories(), _wysiwyg_process_include()
+ */
+function wysiwyg_load_includes($type = 'editors', $hook = 'editor', $file = NULL) {
+ // Determine implementations.
+ $directories = wysiwyg_get_directories($type);
+ $directories['wysiwyg'] = drupal_get_path('module', 'wysiwyg') . '/' . $type;
+ $file_list = array();
+ foreach ($directories as $module => $path) {
+ $file_list[$module] = drupal_system_listing("/{$file}.inc\$/", $path, 'name', 0);
+ }
+
+ // Load implementations.
+ $info = array();
+ foreach (array_filter($file_list) as $module => $files) {
+ foreach ($files as $file) {
+ include_once './' . $file->uri;
+ $result = _wysiwyg_process_include($module, $module . '_' . $file->name, dirname($file->uri), $hook);
+ if (is_array($result)) {
+ $info = array_merge($info, $result);
+ }
+ }
+ }
+ return $info;
+}
+
+/**
+ * Helper function to build paths to libraries.
+ *
+ * @param $library
+ * The external library name to return the path for.
+ * @param $base_path
+ * Whether to prefix the resulting path with base_path().
+ *
+ * @return
+ * The path to the specified library.
+ *
+ * @ingroup libraries
+ */
+function wysiwyg_get_path($library, $base_path = FALSE) {
+ static $libraries;
+
+ if (!isset($libraries)) {
+ $libraries = wysiwyg_get_libraries();
+ }
+ if (!isset($libraries[$library])) {
+ // Most often, external libraries can be shared across multiple sites.
+ return 'sites/all/libraries/' . $library;
+ }
+
+ $path = ($base_path ? base_path() : '');
+ $path .= $libraries[$library];
+
+ return $path;
+}
+
+/**
+ * Return an array of library directories.
+ *
+ * Returns an array of library directories from the all-sites directory
+ * (i.e. sites/all/libraries/), the profiles directory, and site-specific
+ * directory (i.e. sites/somesite/libraries/). The returned array will be keyed
+ * by the library name. Site-specific libraries are prioritized over libraries
+ * in the default directories. That is, if a library with the same name appears
+ * in both the site-wide directory and site-specific directory, only the
+ * site-specific version will be listed.
+ *
+ * @return
+ * A list of library directories.
+ *
+ * @ingroup libraries
+ */
+function wysiwyg_get_libraries() {
+ global $profile;
+
+ // When this function is called during Drupal's initial installation process,
+ // the name of the profile that is about to be installed is stored in the
+ // global $profile variable. At all other times, the regular system variable
+ // contains the name of the current profile, and we can call variable_get()
+ // to determine the profile.
+ if (!isset($profile)) {
+ $profile = variable_get('install_profile', 'default');
+ }
+
+ $directory = 'libraries';
+ $searchdir = array();
+ $config = conf_path();
+
+ // The 'profiles' directory contains pristine collections of modules and
+ // themes as organized by a distribution. It is pristine in the same way
+ // that /modules is pristine for core; users should avoid changing anything
+ // there in favor of sites/all or sites/ directories.
+ if (file_exists("profiles/$profile/$directory")) {
+ $searchdir[] = "profiles/$profile/$directory";
+ }
+
+ // Always search sites/all/*.
+ $searchdir[] = 'sites/all/' . $directory;
+
+ // Also search sites//*.
+ if (file_exists("$config/$directory")) {
+ $searchdir[] = "$config/$directory";
+ }
+
+ // Retrieve list of directories.
+ // @todo Core: Allow to scan for directories.
+ $directories = array();
+ $nomask = array('CVS');
+ foreach ($searchdir as $dir) {
+ if (is_dir($dir) && $handle = opendir($dir)) {
+ while (FALSE !== ($file = readdir($handle))) {
+ if (!in_array($file, $nomask) && $file[0] != '.') {
+ if (is_dir("$dir/$file")) {
+ $directories[$file] = "$dir/$file";
+ }
+ }
+ }
+ closedir($handle);
+ }
+ }
+
+ return $directories;
+}
+
+/**
+ * Return a list of directories by modules implementing wysiwyg_include_directory().
+ *
+ * @param $plugintype
+ * The type of a plugin; can be 'editors'.
+ *
+ * @return
+ * An array containing module names suffixed with '_' and their defined
+ * directory.
+ *
+ * @see wysiwyg_load_includes(), _wysiwyg_process_include()
+ */
+function wysiwyg_get_directories($plugintype) {
+ $directories = array();
+ foreach (module_implements('wysiwyg_include_directory') as $module) {
+ $result = module_invoke($module, 'wysiwyg_include_directory', $plugintype);
+ if (isset($result) && is_string($result)) {
+ $directories[$module] = drupal_get_path('module', $module) . '/' . $result;
+ }
+ }
+ return $directories;
+}
+
+/**
+ * Process a single hook implementation of a wysiwyg editor.
+ *
+ * @param $module
+ * The module that owns the hook.
+ * @param $identifier
+ * Either the module or 'wysiwyg_' . $file->name
+ * @param $hook
+ * The name of the hook being invoked.
+ */
+function _wysiwyg_process_include($module, $identifier, $path, $hook) {
+ $function = $identifier . '_' . $hook;
+ if (!function_exists($function)) {
+ return NULL;
+ }
+ $result = $function();
+ if (!isset($result) || !is_array($result)) {
+ return NULL;
+ }
+
+ // Fill in defaults.
+ foreach ($result as $editor => $properties) {
+ $result[$editor]['module'] = $module;
+ $result[$editor]['name'] = $editor;
+ $result[$editor]['path'] = $path;
+ }
+ return $result;
+}
+
+/**
+ * @} End of "defgroup wysiwyg_api".
+ */
+
+/**
+ * Implements hook_features_api().
+ */
+function wysiwyg_features_api() {
+ return array(
+ 'wysiwyg' => array(
+ 'name' => t('Wysiwyg profiles'),
+ 'default_hook' => 'wysiwyg_default_profiles',
+ 'default_file' => FEATURES_DEFAULTS_INCLUDED,
+ 'feature_source' => TRUE,
+ 'file' => drupal_get_path('module', 'wysiwyg') . '/wysiwyg.features.inc',
+ ),
+ );
+}
+