diff --git a/core/modules/ckeditor/ckeditor.admin.inc b/core/modules/ckeditor/ckeditor.admin.inc new file mode 100644 index 0000000..3342e1b --- /dev/null +++ b/core/modules/ckeditor/ckeditor.admin.inc @@ -0,0 +1,173 @@ +direction) && $language->direction === LANGUAGE_RTL ? 'rtl' : 'ltr'; + + // Create lists of active and disabled buttons. + $editor = $variables['editor']; + $plugins = $variables['plugins']; + $buttons = array(); + $variables['multiple_buttons'] = array(); + foreach ($plugins as $plugin) { + if (isset($plugin['buttons'])) { + foreach ($plugin['buttons'] as $button_name => $button) { + if (!empty($button['multiple'])) { + $variables['multiple_buttons'][$button_name] = $button; + } + $button['name'] = $button_name; + $buttons[$button_name] = $button; + } + } + } + $variables['active_buttons'] = array(); + foreach ($editor->settings['toolbar']['buttons'] as $row_number => $row) { + foreach ($row as $button_name) { + if (isset($buttons[$button_name])) { + $variables['active_buttons'][$row_number][] = $buttons[$button_name]; + if (empty($buttons[$button_name]['multiple'])) { + unset($buttons[$button_name]); + } + } + } + } + $variables['disabled_buttons'] = array_diff_key($buttons, $variables['multiple_buttons']); +} + +/** + * Displays the toolbar configuration for CKEditor. + */ +function theme_ckeditor_settings_toolbar($variables) { + $editor = $variables['editor']; + $plugins = $variables['plugins']; + $rtl = $variables['language_direction'] === 'rtl' ? '_rtl' : ''; + + // Assemble items to be added to active button rows. + foreach ($variables['active_buttons'] as $row_number => $row_buttons) { + foreach ($row_buttons as $button) { + $button_name = $button['name']; + if (isset($button['image_alternative'])) { + $data = $button['image_alternative']; + } + elseif (isset($button['image'])) { + $data = theme('image', array('uri' => $button['image' . $rtl], 'title' => $button['label'])); + } + else { + $data = '?'; + } + $button_item = array( + 'data' => $data, + 'data-button-name' => $button_name, + ); + if (!empty($button['multiple'])) { + $button['attributes']['class'][] = 'ckeditor-multiple-button'; + } + if (!empty($button['attributes'])) { + $button_item = array_merge($button_item, $button['attributes']); + } + $active_buttons[$row_number][] = $button_item; + } + } + // Assemble list of disabled buttons (which are always a single row). + foreach ($variables['disabled_buttons'] as $button_name => $button) { + if (isset($button['image_alternative'])) { + $data = $button['image_alternative']; + } + elseif (isset($button['image'])) { + $data = theme('image', array('uri' => $button['image' . $rtl], 'title' => $button['label'])); + } + else { + $data = '?'; + } + $button_item = array( + 'data' => $data, + 'data-button-name' => $button_name, + ); + if (isset($button['attributes'])) { + $button_item = array_merge($button_item, $button['attributes']); + } + $disabled_buttons[] = $button_item; + } + // Assemble list of multiple buttons that may be added multiple times. + foreach ($variables['multiple_buttons'] as $button_name => $button) { + if (isset($button['image_alternative'])) { + $data = $button['image_alternative']; + } + elseif (isset($button['image'])) { + $data = theme('image', array('uri' => $button['image' . $rtl], 'title' => $button['label'])); + } + else { + $data = '?'; + } + $button_item = array( + 'data' => $data, + 'data-button-name' => $button_name, + ); + $button['attributes']['class'][] = 'ckeditor-multiple-button'; + if (isset($button['attributes'])) { + $button_item = array_merge($button_item, $button['attributes']); + } + $multiple_buttons[] = $button_item; + } + + // We don't use theme_item_list() below in case there are no buttons in the + // active or disabled list, as theme_item_list() will not print an empty UL. + $output = ''; + $output .= '' . t('Active toolbar') . ''; + $output .= '
'; + foreach ($active_buttons as $button_row) { + $output .= ''; + } + if (empty($active_buttons)) { + $output .= ''; + } + + $output .= '
'; + $output .= '-'; + $output .= '+'; + $output .= '
'; + + $output .= '
'; + + $output .= '' . t('Available buttons') . ''; + $output .= '
'; + $output .= ''; + $output .= '' . t('Dividers') . ': '; + $output .= ''; + $output .= '
'; + + return $output; +} diff --git a/core/modules/ckeditor/ckeditor.api.php b/core/modules/ckeditor/ckeditor.api.php new file mode 100644 index 0000000..f434b54 --- /dev/null +++ b/core/modules/ckeditor/ckeditor.api.php @@ -0,0 +1,158 @@ + drupal_get_path('module', 'mymodule') . '/js/myplugin', + 'file' => 'plugin.js', + 'css' => array(drupal_get_path('module', 'mymodule') . '/css/myplugin.css'), + 'enabled callback' => 'mymodule_myplugin_plugin_check', + ); + + return $plugins; +} + +/** + * Modify the list of available CKEditor plugins. + * + * This hook may be used to modify plugin properties after they have been + * specified by other modules. + * + * @param $plugins + * An array of all the existing plugin definitions, passed by reference. + * + * @see hook_ckeditor_plugins() + */ +function hook_ckeditor_plugins_alter(array &$plugins) { + $plugins['someplugin']['enabled callback'] = 'mymodule_someplugin_enabled_callback'; +} + +/** + * Modify the list of CSS files that will be added to a CKEditor instance. + * + * Modules may use this hook to provide their own custom CSS file without + * providing a CKEditor plugin. This list of CSS files is only used in the + * iframe versions of CKEditor. + * + * Note that because this hook is only called for modules and the active theme, + * front-end themes will not be able to use this hook to add their own CSS files + * if a different admin theme is active. Instead, front-end themes and base + * themes may specify CSS files to be used in iframe instances of CKEditor + * through an entry in their .info file: + * + * @code + * ckeditor_stylesheets[] = css/ckeditor-iframe.css + * @endcode + * + * @param $css + * An array of CSS files, passed by reference. This is a flat list of file + * paths relative to the Drupal root. + * @param $editor + * The editor object as returned by editor_load(), for which these files are + * being loaded. + * @param $format + * The corresponding text format object as returned by filter_format_load() + * for which the current text editor is being displayed. + * + * @see _ckeditor_theme_css() + */ +function hook_ckeditor_css_alter(array &$css, $editor, $format) { + $css[] = drupal_get_path('module', 'mymodule') . '/css/mymodule-ckeditor.css'; +} + +/** + * @} End of "addtogroup hooks". + */ + +/** + * Enabled callback for hook_ckeditor_plugins(). + * + * Note: This is not really a hook. The function name is manually specified via + * 'enabled callback' in hook_ckeditor_plugins(), with this recommended callback + * name pattern. It is called from + * \Drupal\ckeditor\Plugin\editor\editor\CKEditor:getJSSettings(). + * + * This callback should determine if a plugin should be enabled for a CKEditor + * instance. Plugins may be enabled based off an explicit setting, or enable + * themselves based on the configuration of another setting, such as enabling + * based on a particular button being present in the toolbar. + * + * @param object $editor + * An editor instance as returned by editor_load(). The editor's settings may + * be found in $editor->settings. + * @param string $plugin_name + * String name of the plugin that is being checked. + * + * @return boolean + * Boolean TRUE if the plugin should be enabled, FALSE otherwise. + * + * @see hook_ckeditor_plugins() + * @see \Drupal\ckeditor\Plugin\editor\editor\CKEditor:getJSSettings() + */ +function hook_ckeditor_PLUGIN_plugin_check($editor, $plugin_name) { + // Automatically enable this plugin if the Underline button is enabled. + foreach ($editor->settings['toolbar']['buttons'] as $row) { + if (in_array('Underline', $row)) { + return TRUE; + } + } +} diff --git a/core/modules/ckeditor/ckeditor.info b/core/modules/ckeditor/ckeditor.info new file mode 100644 index 0000000..9c40ac2 --- /dev/null +++ b/core/modules/ckeditor/ckeditor.info @@ -0,0 +1,6 @@ +name = CKEditor +description = WYSIWYG editing for rich text fields using CKEditor. +package = Core +core = 8.x +version = VERSION +dependencies[] = editor diff --git a/core/modules/ckeditor/ckeditor.module b/core/modules/ckeditor/ckeditor.module new file mode 100644 index 0000000..5eae915 --- /dev/null +++ b/core/modules/ckeditor/ckeditor.module @@ -0,0 +1,302 @@ + array( + 'modulePath' => drupal_get_path('module', 'ckeditor'), + ), + ); + $libraries['drupal.ckeditor'] = array( + 'title' => 'Drupal behavior to enable CKEditor on textareas.', + 'version' => VERSION, + 'js' => array( + $module_path . '/js/ckeditor.js' => array(), + array('data' => $settings, 'type' => 'setting'), + ), + 'dependencies' => array( + array('editor', 'drupal.editor'), + array('ckeditor', 'ckeditor'), + ), + ); + $libraries['drupal.ckeditor.css'] = array( + 'title' => 'Formatting CSS for common classes used in CKEditor.', + 'version' => VERSION, + 'css' => array( + $module_path . '/css/ckeditor.css' => array(), + ), + ); + $libraries['drupal.ckeditor.admin'] = array( + 'title' => 'Drupal behavior to enable CKEditor on textareas.', + 'version' => VERSION, + 'js' => array( + $module_path . '/js/ckeditor.admin.js' => array(), + ), + 'css' => array( + $module_path . '/css/ckeditor.admin.css' => array(), + ), + 'dependencies' => array( + array('system', 'jquery.once'), + array('system', 'jquery.ui.sortable'), + array('system', 'jquery.ui.draggable'), + ), + ); + $libraries['ckeditor'] = array( + 'title' => 'Loads the main CKEditor library.', + 'version' => CKEDITOR_VERSION, + 'js' => array( + '/core/misc/ckeditor/ckeditor.js' => array(), + ), + ); + + return $libraries; +} + +/** + * Implements hook_theme(). + */ +function ckeditor_theme() { + return array( + 'ckeditor_settings_toolbar' => array( + 'file' => 'ckeditor.admin.inc', + 'variables' => array('editor' => NULL, 'plugins' => NULL), + ), + ); +} + +/** + * Implements hook_page_build(). + */ +function ckeditor_page_build(&$page) { + // Add our CSS file that adds common needed classes, such as align-left, + // align-right, underline, indent, etc. + $page['#attached']['library'][] = array('ckeditor', 'drupal.ckeditor.css'); +} + +/** + * Retrieves the full list of installed CKEditor plugins. + */ +function ckeditor_plugins() { + $plugins = module_invoke_all('ckeditor_plugins'); + drupal_alter('ckeditor_plugins', $plugins); + return $plugins; +} + +/** + * Implements hook_ckeditor_plugins(). + * + * Return a list of all plugins provided by this module. + */ +function ckeditor_ckeditor_plugins() { + $image_prefix = drupal_get_path('module', 'ckeditor') . '/images/buttons/'; + $buttons = array( + 'Bold' => array( + 'label' => t('Bold'), + 'required_tags' => array('strong'), + ), + 'Italic' => array( + 'label' => t('Italic'), + 'required_tags' => array('em'), + ), + 'Underline' => array( + 'label' => t('Underline'), + // A class is used on spans for underline. + 'required_tags' => array('span'), + ), + 'Strike' => array( + 'label' => t('Strike-through'), + 'required_tags' => array('del'), + ), + 'JustifyLeft' => array( + 'label' => t('Align left'), + 'required_tags' => array('p'), + ), + 'JustifyCenter' => array( + 'label' => t('Align center'), + 'required_tags' => array('p'), + ), + 'JustifyRight' => array( + 'label' => t('Align right'), + 'required_tags' => array('p'), + ), + 'JustifyBlock' => array( + 'label' => t('Justify'), + 'required_tags' => array('p'), + ), + 'BulletedList' => array( + 'label' => t('Bullet list'), + 'image_rtl' => $image_prefix . '/bulletedlist-rtl.png', + 'required_tags' => array('ul', 'li'), + ), + 'NumberedList' => array( + 'label' => t('Numbered list'), + 'image_rtl' => $image_prefix . '/numberedlist-rtl.png', + 'required_tags' => array('ol', 'li'), + ), + 'Outdent' => array( + 'label' => t('Outdent'), + 'image_rtl' => $image_prefix . '/outdent-rtl.png', + 'required_tags' => array('p'), + ), + 'Indent' => array( + 'label' => t('Indent'), + 'image_rtl' => $image_prefix . '/indent-rtl.png', + 'required_tags' => array('p'), + ), + 'Undo' => array( + 'label' => t('Undo'), + 'image_rtl' => $image_prefix . '/undo-rtl.png', + ), + 'Redo' => array( + 'label' => t('Redo'), + 'image_rtl' => $image_prefix . '/redo-rtl.png', + ), + 'Link' => array( + 'label' => t('Link'), + 'required_tags' => array('a'), + ), + 'Unlink' => array( + 'label' => t('Unlink'), + 'required_tags' => array('a'), + ), + 'Anchor' => array( + 'image_rtl' => $image_prefix . '/anchor-rtl.png', + 'label' => t('Anchor'), + 'required_tags' => array('a'), + ), + 'Superscript' => array( + 'label' => t('Superscript'), + 'required_tags' => array('sup'), + ), + 'Subscript' => array( + 'label' => t('Subscript'), + 'required_tags' => array('sub'), + ), + 'Blockquote' => array( + 'label' => t('Blockquote'), + 'required_tags' => array('blockquote'), + ), + 'Source' => array( + 'label' => t('Source code'), + ), + 'HorizontalRule' => array( + 'label' => t('Horizontal rule'), + 'required_tags' => array('hr'), + ), + 'Cut' => array( + 'label' => t('Cut'), + ), + 'Copy' => array( + 'label' => t('Copy'), + ), + 'Paste' => array( + 'label' => t('Paste'), + ), + 'PasteText' => array( + 'label' => t('Paste Text'), + 'image_rtl' => $image_prefix . '/pastetext-rtl.png', + ), + 'PasteFromWord' => array( + 'label' => t('Paste from Word'), + 'image_rtl' => $image_prefix . '/pastefromword-rtl.png', + ), + 'ShowBlocks' => array( + 'label' => t('Show blocks'), + 'image_rtl' => $image_prefix . '/showblocks-rtl.png', + ), + 'RemoveFormat' => array( + 'label' => t('Remove format'), + ), + 'SpecialChar' => array( + 'label' => t('Character map'), + ), + 'Format' => array( + 'label' => t('HTML block format'), + 'image_alternative' => '' . t('Format') . '', + ), + 'Styles' => array( + 'label' => t('Font style'), + 'image_alternative' => '' . t('Styles') . '', + ), + 'Image' => array( + 'label' => t('Image'), + 'required_tags' => array('img'), + ), + 'Table' => array( + 'label' => t('Table'), + 'required_tags' => array('table', 'thead', 'tbody', 'tr', 'td', 'th'), + ), + 'Maximize' => array( + 'label' => t('Maximize'), + ), + '|' => array( + 'label' => t('Group separator'), + 'image_alternative' => ' ', + 'attributes' => array('class' => array('ckeditor-group-button-separator')), + 'multiple' => TRUE, + ), + '-' => array( + 'label' => t('Separator'), + 'image_alternative' => ' ', + 'attributes' => array('class' => array('ckeditor-button-separator')), + 'multiple' => TRUE, + ), + ); + + // Populate image locations, which match button names. + foreach ($buttons as $button_name => &$button) { + if (!isset($button['image_alternative']) && !isset($button['image'])) { + // Because button names are ASCII text, drupal_strtolower() is not needed. + $button['image'] = $image_prefix . strtolower($button_name) . '.png'; + } + } + + // List all the basic plugin buttons as an "internal" plugin. + $plugins['default'] = array( + 'buttons' => $buttons, + 'internal' => TRUE, + ); + + return $plugins; +} + +/** + * Retrieves the default theme's CKEditor stylesheets defined in the .info file. + * + * Themes may specify iframe-specific CSS files for use with CKEditor by + * including a "ckeditor_stylesheets" key in the theme .info file. + * + * @code + * ckeditor_stylesheets[] = css/ckeditor-iframe.css + * @endcode + */ +function _ckeditor_theme_css($theme = NULL) { + $css = array(); + if (!isset($theme)) { + $theme = variable_get('theme_default'); + } + if ($theme_path = drupal_get_path('theme', $theme)) { + $info = system_get_info('theme', $theme); + if (isset($info['ckeditor_stylesheets'])) { + $css = $info['ckeditor_stylesheets']; + foreach ($css as $key => $path) { + $css[$key] = $theme_path . '/' . $path; + } + } + if (isset($info['base theme'])) { + $css = array_merge($css, _ckeditor_theme_css($info['base theme'])); + } + } + return $css; +} diff --git a/core/modules/ckeditor/css/ckeditor-iframe.css b/core/modules/ckeditor/css/ckeditor-iframe.css new file mode 100644 index 0000000..54f4b3f --- /dev/null +++ b/core/modules/ckeditor/css/ckeditor-iframe.css @@ -0,0 +1,18 @@ +/** + * CSS added to iframe-based instances only. + */ +body { + font-family: Arial, Verdana, sans-serif; + font-size: 12px; + color: #222; + background-color: #fff; + margin: 8px; +} + +ol, ul, dl { + /* IE7: reset rtl list margin. (CKEditor issue #7334) */ + *margin-right: 0px; + /* Preserved spaces for list items with text direction other than the list. + * (CKEditor issues #6249,#8049) */ + padding: 0 40px; +} diff --git a/core/modules/ckeditor/css/ckeditor-rtl.css b/core/modules/ckeditor/css/ckeditor-rtl.css new file mode 100644 index 0000000..59f97b8 --- /dev/null +++ b/core/modules/ckeditor/css/ckeditor-rtl.css @@ -0,0 +1,18 @@ +/** + * RTL styles used by CKEditor. Added to front-end theme and iframe editors. + */ +.align-left { + text-align: right; +} +.align-right { + text-align: left; +} +.indent1 { + margin: 0 40px 0 0; +} +.indent2 { + margin: 0 80px 0 0; +} +.indent3 { + margin: 0 120px 0 0; +} diff --git a/core/modules/ckeditor/css/ckeditor.admin.css b/core/modules/ckeditor/css/ckeditor.admin.css new file mode 100644 index 0000000..15fad3b --- /dev/null +++ b/core/modules/ckeditor/css/ckeditor.admin.css @@ -0,0 +1,161 @@ +/** + * @file + * Styles for configuration of CKEditor module. + * + * Many of these styles are adapted directly from the default CKEditor theme + * "moono". + */ + +.ckeditor-toolbar-active { + border: 1px solid #b6b6b6; + padding: 6px 8px 2px; + box-shadow: 0 1px 0 white inset; + background: #cfd1cf; + background-image: -webkit-gradient(linear, left top, left bottom, from(whiteSmoke), to(#cfd1cf)); + background-image: -moz-linear-gradient(top, whiteSmoke, #cfd1cf); + background-image: -o-linear-gradient(top, whiteSmoke, #cfd1cf); + background-image: -ms-linear-gradient(top, whiteSmoke, #cfd1cf); + background-image: linear-gradient(top, whiteSmoke, #cfd1cf); + filter: progid:DXImageTransform.Microsoft.gradient(gradientType=0, startColorstr='#fff5f5f5', endColorstr='#ffcfd1cf'); + margin: 5px 0; + overflow: nowrap; +} +.ckeditor-toolbar-disabled { + margin: 5px 0; +} + +ul.ckeditor-buttons { + min-height: 26px; + min-width: 26px; + list-style: none; + + float: left; + clear: left; + padding: 0; + margin: 0 6px 5px 0; + border: 1px solid #a6a6a6; + border-bottom-color: #979797; + border-radius: 3px; + box-shadow: 0 1px 0 rgba(255, 255, 255, .5), 0 0 2px rgba(255, 255, 255, .15) inset, 0 1px 0 rgba(255, 255, 255, .15) inset; +} +ul.ckeditor-buttons li { + display: inline-block; + height: 18px; + padding: 4px 6px; + outline: none; + cursor: move; + float: left; + border: 0; + white-space: nowrap; + + background: #e4e4e4; + background-image: -webkit-gradient(linear,left top,left bottom,from(white),to(#e4e4e4)); + background-image: -moz-linear-gradient(top,white,#e4e4e4); + background-image: -webkit-linear-gradient(top,white,#e4e4e4); + background-image: -o-linear-gradient(top,white,#e4e4e4); + background-image: -ms-linear-gradient(top,white,#e4e4e4); + background-image: linear-gradient(top,white,#e4e4e4); + filter: progid:DXImageTransform.Microsoft.gradient(gradientType=0,startColorstr='#ffffffff',endColorstr='#ffe4e4e4'); +} +ul.ckeditor-buttons li:first-child { + border-top-left-radius: 2px; + border-bottom-left-radius: 2px; +} +ul.ckeditor-buttons li:last-child { + border-top-right-radius: 2px; + border-bottom-right-radius: 2px; +} +ul.ckeditor-buttons li.ckeditor-button-placeholder { + background: #333; + opacity: 0.3; +} +ul.ckeditor-multiple-buttons { + padding: 2px; + margin: 0; + list-style: none; + float: left; +} +ul.ckeditor-multiple-buttons li { + padding: 2px 0; + margin: 0; + display: inline-block; + height: 18px; + cursor: move; + float: left; +} +.ckeditor-multiple-label { + float: left; + padding: 4px; +} +ul.ckeditor-buttons li.ckeditor-group-button-separator, +ul.ckeditor-multiple-buttons li.ckeditor-group-button-separator { + background: url(../images/group-separator.png) no-repeat center center; + width: 13px; + padding: 0; + height: 29px; + margin: -1px -3px -2px; + position: relative; + z-index: 10; +} +ul.ckeditor-buttons li.ckeditor-button-separator { + width: 2px; + padding: 0 4px; + height: 26px; + margin: 0 -4px; + position: relative; + z-index: 10; + + background: #e4e4e4; + background-image: -webkit-gradient(linear, left top, left bottom, from(white), to(#e4e4e4)); + background-image: -moz-linear-gradient(top, white, #e4e4e4); + background-image: -webkit-linear-gradient(top, white, #e4e4e4); + background-image: -o-linear-gradient(top, white, #e4e4e4); + background-image: -ms-linear-gradient(top, white, #e4e4e4); + background-image: linear-gradient(top, white, #e4e4e4); + filter: progid:DXImageTransform.Microsoft.gradient(gradientType=0, startColorstr='#ffffffff', endColorstr='#ffe4e4e4'); +} +ul.ckeditor-multiple-buttons li.ckeditor-button-separator { + width: 2px; + padding: 0; + height: 26px; + margin: 0 10px; +} +.ckeditor-separator { + background-color: silver; + background-color: rgba(0, 0, 0, .2); + margin: 5px 0; + height: 18px; + width: 1px; + display: block; + -webkit-box-shadow: 1px 0 1px rgba(255, 255, 255, .5); + -moz-box-shadow: 1px 0 1px rgba(255,255,255,.5); + box-shadow: 1px 0 1px rgba(255, 255, 255, .5) +} +.ckeditor-button-arrow { + width: 0; + text-align: center; + border-left: 3px solid transparent; + border-right: 3px solid transparent; + border-top: 3px solid #333; + display: inline-block; + margin: 0 4px 2px; +} + +.ckeditor-row-controls { + float: right; + font-size: 18px; + width: 40px; + text-align: right; +} +.ckeditor-row-controls a { + display: inline-block; + padding: 6px 2px; + height: 16px; + width: 16px; + line-height: 0.9; + font-weight: bold; + color: #333; +} +.ckeditor-row-controls a:hover { + text-decoration: none; +} diff --git a/core/modules/ckeditor/css/ckeditor.css b/core/modules/ckeditor/css/ckeditor.css new file mode 100644 index 0000000..d5c6efe --- /dev/null +++ b/core/modules/ckeditor/css/ckeditor.css @@ -0,0 +1,56 @@ +/** + * Common styles used by CKEditor. Added to front-end theme and iframe editors. + */ +.align-left { + text-align: left; /* RTL */ +} +.align-right { + text-align: right; /* RTL */ +} +.align-center { + text-align: center; +} +.align-justify { + text-align: justify; +} +img.align-left { + float: left; /* RTL */ +} +img.align-right { + float: right; /* RTL */ +} +img.align-center { + margin-left: auto; + margin-right: auto; + display: block; +} +img.full-width { + width: 100%; + height: auto; +} +.underline { + text-decoration: underline; +} +.indent1 { + margin: 0 0 0 40px; /* RTL */ +} +.indent2 { + margin: 0 0 0 80px; /* RTL */ +} +.indent3 { + margin: 0 0 0 120px; /* RTL */ +} +.caption-left { + float: left; /* RTL */ +} +.caption-right { + float: right; /* RTL */ +} +.caption-center { + text-align: center; + margin-left: auto; + margin-right: auto; +} +.align-justify { + text-align: justify; +} diff --git a/core/modules/ckeditor/js/ckeditor.admin.js b/core/modules/ckeditor/js/ckeditor.admin.js new file mode 100644 index 0000000..d22ac6e --- /dev/null +++ b/core/modules/ckeditor/js/ckeditor.admin.js @@ -0,0 +1,101 @@ +(function ($, Drupal) { + +"use strict"; + +Drupal.ckeditor = Drupal.ckeditor || {}; + +Drupal.behaviors.ckeditorAdmin = { + attach: function (context, settings) { + var $context = $(context); + $(context).find('.ckeditor-toolbar-configuration').once('ckeditor-toolbar', function() { + var $wrapper = $(this); + var $textareaWrapper = $(this).find('.form-item-editor-settings-toolbar-buttons').hide(); + var $textarea = $textareaWrapper.find('textarea'); + var $toolbarAdmin = $(settings.ckeditor.toolbarAdmin); + var sortableSettings = { + connectWith: '.ckeditor-buttons', + placeholder: 'ckeditor-button-placeholder', + forcePlaceholderSize: true, + tolerance: 'pointer', + cursor: 'move', + stop: adminToolbarValue + }; + $toolbarAdmin.insertAfter($textareaWrapper).find('.ckeditor-buttons').sortable(sortableSettings); + $toolbarAdmin.find('.ckeditor-multiple-buttons li').draggable({ + connectToSortable: '.ckeditor-toolbar-active .ckeditor-buttons', + helper: 'clone' + }); + $toolbarAdmin.on('click.ckeditorAddRow', 'a.ckeditor-row-add', adminToolbarAddRow); + $toolbarAdmin.on('click.ckeditorAddRow', 'a.ckeditor-row-remove', adminToolbarRemoveRow); + if ($toolbarAdmin.find('.ckeditor-toolbar-active ul').length > 1) { + $toolbarAdmin.find('a.ckeditor-row-remove').hide(); + } + + /** + * Add a new row of buttons. + */ + function adminToolbarAddRow(event) { + var $this = $(this); + var $rows = $this.closest('.ckeditor-toolbar-active').find('.ckeditor-buttons'); + $rows.last().clone().empty().insertAfter($rows.last()).sortable(sortableSettings); + $this.siblings('a').show(); + redrawToolbarGradient(); + event.preventDefault(); + } + + /** + * Remove a row of buttons. + */ + function adminToolbarRemoveRow(event) { + var $this = $(this); + var $rows = $this.closest('.ckeditor-toolbar-active').find('.ckeditor-buttons'); + if ($rows.length === 2) { + $this.hide(); + } + if ($rows.length > 1) { + var $lastRow = $rows.last(); + var $disabledButtons = $wrapper.find('.ckeditor-toolbar-disabled .ckeditor-buttons'); + $lastRow.children(':not(.ckeditor-multiple-button)').prependTo($disabledButtons); + $lastRow.sortable('destroy').remove(); + redrawToolbarGradient(); + } + event.preventDefault(); + } + + /** + * Browser quirk work-around to redraw CSS3 gradients. + */ + function redrawToolbarGradient() { + $wrapper.find('.ckeditor-toolbar-active').css('position', 'relative'); + window.setTimeout(function() { + $wrapper.find('.ckeditor-toolbar-active').css('position', ''); + }, 10); + } + + /** + * jQuery Sortable stop event. Save updated toolbar positions to the textarea. + */ + function adminToolbarValue(event, ui) { + // Update the toolbar config after updating a sortable. + var toolbarConfig = []; + $wrapper.find('.ckeditor-toolbar-active ul').each(function() { + var $rowButtons = $(this).find('li'); + var rowConfig = []; + if ($rowButtons.length) { + $rowButtons.each(function() { + rowConfig.push(this.getAttribute('data-button-name')); + }); + toolbarConfig.push(rowConfig); + } + }); + $textarea.val(JSON.stringify(toolbarConfig, null, ' ')); + } + + }); + }, + detach: function (context, settings) { + // @todo + } +}; + +})(jQuery, Drupal); diff --git a/core/modules/ckeditor/js/ckeditor.js b/core/modules/ckeditor/js/ckeditor.js new file mode 100644 index 0000000..d72cc04 --- /dev/null +++ b/core/modules/ckeditor/js/ckeditor.js @@ -0,0 +1,32 @@ +(function ($, Drupal, drupalSettings, CKEDITOR) { + +"use strict"; + +Drupal.editors.ckeditor = { + attach: function (element, format) { + // Register additional Drupal plugins as necessary. + if (format.editorSettings.externalPlugins) { + for (var pluginName in format.editorSettings.externalPlugins) { + if (format.editorSettings.externalPlugins.hasOwnProperty(pluginName)) { + CKEDITOR.plugins.addExternal(pluginName, drupalSettings.basePath + format.editorSettings.externalPlugins[pluginName]['path'] + '/', format.editorSettings.externalPlugins[pluginName]['file']); + } + } + delete format.editorSettings.externalPlugins; + } + return !!CKEDITOR.replace(element, format.editorSettings); + }, + detach: function (element, format, trigger) { + var editor = CKEDITOR.dom.element.get(element).getEditor(); + if (editor) { + if (trigger === 'serialize') { + editor.updateElement(); + } + else { + editor.destroy(); + } + } + return !!editor; + } +}; + +})(jQuery, Drupal, drupalSettings, CKEDITOR); diff --git a/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/editor/editor/CKEditor.php b/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/editor/editor/CKEditor.php new file mode 100644 index 0000000..8ab010e --- /dev/null +++ b/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/editor/editor/CKEditor.php @@ -0,0 +1,223 @@ + array( + 'buttons' => array( + array( + 'Source', '|', 'Bold', 'Italic', '|', + 'NumberedList', 'BulletedList', 'Blockquote', '|', + 'JustifyLeft', 'JustifyCenter', 'JustifyRight', '|', + 'Link', 'Unlink', '|', 'Image', 'Maximize', + ), + ), + 'format_list' => array('p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'), + 'style_list' => array(), + ), + ); + } + + /** + * Implements \Drupal\editor\Plugin\EditorInterface::settingsForm(). + */ + function settingsForm(array $form, array &$form_state, Editor $editor) { + $module_path = drupal_get_path('module', 'ckeditor'); + $plugins = ckeditor_plugins(); + + $form['toolbar'] = array( + '#type' => 'fieldset', + '#title' => t('Toolbar'), + '#attached' => array( + 'library' => array(array('ckeditor', 'drupal.ckeditor.admin')), + 'js' => array( + array('data' => array('ckeditor' => array('toolbarAdmin' => theme('ckeditor_settings_toolbar', array('editor' => $editor, 'plugins' => $plugins)))), 'type' => 'setting') + ), + ), + '#attributes' => array('class' => array('ckeditor-toolbar-configuration')), + ); + $form['toolbar']['buttons'] = array( + '#type' => 'textarea', + '#title' => t('Toolbar buttons'), + '#default_value' => json_encode($editor->settings['toolbar']['buttons']), + '#attributes' => array('class' => array('ckeditor-toolbar-textarea')), + ); + $form['toolbar']['format_list'] = array( + '#type' => 'textfield', + '#title' => t('Format list'), + '#default_value' => implode(', ', $editor->settings['toolbar']['format_list']), + '#description' => t('A list of tags that will be provided in the "Format" dropdown, separated by commas.') + ); + $form['toolbar']['style_list'] = array( + '#type' => 'textarea', + '#title' => t('Style list'), + '#rows' => 4, + '#default_value' => implode("\n", $editor->settings['toolbar']['style_list']), + '#description' => t('A list of classes that will be provided in the "Styles" dropdown, each on a separate line. These styles should be available in your theme\'s editor.css as well as in your theme\'s main CSS file.') + ); + + return $form; + } + + /** + * Implements \Drupal\editor\Plugin\EditorInterface::settingsFormSubmit(). + */ + function settingsFormSubmit(array $form, array &$form_state) { + // Modify the toolbar settings by reference. The values in + // $form_state['values']['editor_settings'] will be saved directly by + // editor_form_filter_admin_format_submit(). + $toolbar_settings = &$form_state['values']['editor_settings']['toolbar']; + + $toolbar_settings['buttons'] = json_decode($toolbar_settings['buttons'], FALSE); + + $format_list = array(); + foreach (explode(',', $toolbar_settings['format_list']) as $format) { + $format_list[] = trim($format); + } + $toolbar_settings['format_list'] = $format_list; + + $style_list = array(); + foreach (explode(',', $toolbar_settings['style_list']) as $style) { + $style_list[] = trim($style); + } + $toolbar_settings['style_list'] = $style_list; + } + + /** + * Implements \Drupal\editor\Plugin\EditorInterface::getJSSettings(). + */ + function getJSSettings(Editor $editor) { + global $language; + + // Loop through all available plugins and check to see if it has been + // explicitly enabled. At the same time, associate each plugin with its + // buttons (if any) so we can check if the plugin should be enabled implicitly + // based on the toolbar. + $plugin_info = ckeditor_plugins(); + $external_plugins = array(); + $external_css = array(); + $all_buttons = array(); + foreach ($plugin_info as $plugin_name => $plugin) { + // Check if this plugin should be enabled. + if (isset($plugin['enabled callback'])) { + if ($plugin['enabled callback'] === TRUE || $plugin['enabled callback']($editor, $plugin_name) && !empty($plugin['path'])) { + $external_plugins[$plugin_name]['file'] = $plugin['file']; + $external_plugins[$plugin_name]['path'] = $plugin['path']; + if (isset($plugin['css'])) { + $external_css = array_merge($external_css, $plugin['css']); + } + } + } + // Associate each plugin with its button. + if (isset($plugin['buttons'])) { + if (empty($plugin['internal'])) { + foreach ($plugin['buttons'] as $button_name => &$button) { + $button['plugin'] = $plugin; + $button['plugin']['name'] = $plugin_name; + unset($button['plugin']['buttons']); + } + } + $all_buttons = array_merge($all_buttons, $plugin['buttons']); + } + } + + // Change the toolbar separators into groups and record needed plugins based + // on use in the toolbar. + $toolbar = array(); + foreach ($editor->settings['toolbar']['buttons'] as $row_number => $row) { + $button_group = array(); + foreach ($row as $button_name) { + if ($button_name === '|') { + $toolbar[] = $button_group; + $button_group = array(); + } + else { + // Sanity check that the button exists in our installation. + if (isset($all_buttons[$button_name])) { + $button_group['items'][] = $button_name; + + // Keep track of the needed plugin for this button, if any. + if (isset($all_buttons[$button_name]['plugin']['path'])) { + $plugin_name = $all_buttons[$button_name]['plugin']['name']; + $external_plugin = $all_buttons[$button_name]['plugin']; + $external_plugins[$plugin_name]['file'] = $external_plugin['file']; + $external_plugins[$plugin_name]['path'] = $external_plugin['path']; + if (isset($external_plugin['css'])) { + $external_css = array_merge($external_css, $external_plugin['css']); + } + } + } + } + } + $toolbar[] = $button_group; + $toolbar[] = '/'; + } + + // Collect a list of CSS files to be added to the editor instance. + $css = array( + drupal_get_path('module', 'ckeditor') . '/css/ckeditor.css', + drupal_get_path('module', 'ckeditor') . '/css/ckeditor-iframe.css', + ); + $css = array_merge($css, $external_css, _ckeditor_theme_css()); + drupal_alter('ckeditor_css', $css, $editor, $format); + + // Convert all paths to be relative to root. + foreach ($css as $key => $css_path) { + $css[$key] = base_path() . $css_path; + } + + // Initialize reasonable defaults that provide expected basic behavior. + $settings = array( + 'toolbar' => $toolbar, + 'extraPlugins' => implode(',', array_keys($external_plugins)), + 'contentsCss' => array_values($css), + 'pasteFromWordPromptCleanup' => TRUE, + 'indentClasses' => array('indent1', 'indent2', 'indent3'), + 'justifyClasses' => array('align-left', 'align-center', 'align-right', 'align-justify'), + 'coreStyles_underline' => array('element' => 'span', 'attributes' => array('class' => 'underline')), + 'format_tags' => implode(';', $editor->settings['toolbar']['format_list']), + 'removeDialogTabs' => 'image:Link;image:advanced;link:advanced', + 'language' => isset($language->language) ? $language->language : '', + 'resize_dir' => 'vertical', + ); + + // These settings are used specifically by Drupal. + $settings['externalPlugins'] = $external_plugins; + + return $settings; + } + + /** + * Implements \Drupal\editor\Plugin\EditorInterface::getLibraries(). + */ + function getLibraries(Editor $editor) { + return array( + array('ckeditor', 'drupal.ckeditor'), + ); + } +}