TYPO3 v15 dev-main snapshot ()
@@ -0,0 +1 @@
|
|||||||
|
/vendor/
|
||||||
@@ -0,0 +1,418 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the TYPO3 CMS project.
|
||||||
|
*
|
||||||
|
* It is free software; you can redistribute it and/or modify it under
|
||||||
|
* the terms of the GNU General Public License, either version 2
|
||||||
|
* of the License, or any later version.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please read the
|
||||||
|
* LICENSE.txt file that was distributed with this source code.
|
||||||
|
*
|
||||||
|
* The TYPO3 project - inspiring people to share!
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace TYPO3\CMS\RteCKEditor\Controller;
|
||||||
|
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
|
||||||
|
use TYPO3\CMS\Backend\Controller\AbstractLinkBrowserController;
|
||||||
|
use TYPO3\CMS\Core\Configuration\Richtext;
|
||||||
|
use TYPO3\CMS\Core\LinkHandling\Exception\UnknownLinkHandlerException;
|
||||||
|
use TYPO3\CMS\Core\LinkHandling\LinkService;
|
||||||
|
use TYPO3\CMS\Core\Localization\LanguageService;
|
||||||
|
use TYPO3\CMS\Core\Localization\LanguageServiceFactory;
|
||||||
|
use TYPO3\CMS\Core\Messaging\FlashMessage;
|
||||||
|
use TYPO3\CMS\Core\Messaging\FlashMessageService;
|
||||||
|
use TYPO3\CMS\Core\Page\JavaScriptModuleInstruction;
|
||||||
|
use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity;
|
||||||
|
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||||
|
use TYPO3\CMS\Core\View\ViewInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extended controller for link browser
|
||||||
|
* @internal This is a specific Backend Controller implementation and is not considered part of the Public TYPO3 API.
|
||||||
|
*/
|
||||||
|
#[Autoconfigure(public: true, shared: false)]
|
||||||
|
class BrowseLinksController extends AbstractLinkBrowserController
|
||||||
|
{
|
||||||
|
protected string $editorId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TYPO3 language code of the content language
|
||||||
|
*/
|
||||||
|
protected string $contentsLanguage;
|
||||||
|
protected ?LanguageService $contentLanguageService;
|
||||||
|
protected array $buttonConfig = [];
|
||||||
|
protected array $thisConfig = [];
|
||||||
|
protected array $classesAnchorDefault = [];
|
||||||
|
protected array $classesAnchorDefaultTarget = [];
|
||||||
|
protected array $classesAnchorJSOptions = [];
|
||||||
|
protected string $defaultLinkTarget = '';
|
||||||
|
protected string $siteUrl = '';
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
protected readonly LinkService $linkService,
|
||||||
|
protected readonly Richtext $richtext,
|
||||||
|
protected readonly LanguageServiceFactory $languageServiceFactory,
|
||||||
|
protected readonly FlashMessageService $flashMessageService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is only used by RTE currently.
|
||||||
|
*/
|
||||||
|
public function getConfiguration(): array
|
||||||
|
{
|
||||||
|
return $this->buttonConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array{act: string, P: array, editorId: string, contentsLanguage: string} Array of parameters which have to be added to URLs
|
||||||
|
*/
|
||||||
|
public function getUrlParameters(?array $overrides = null): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'act' => $overrides['act'] ?? $this->displayedLinkHandlerId,
|
||||||
|
'P' => $overrides['P'] ?? $this->parameters,
|
||||||
|
'editorId' => $this->editorId,
|
||||||
|
'contentsLanguage' => $this->contentsLanguage,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function initDocumentTemplate(): void
|
||||||
|
{
|
||||||
|
$this->pageRenderer->getJavaScriptRenderer()->addJavaScriptModuleInstruction(
|
||||||
|
JavaScriptModuleInstruction::create('@typo3/rte-ckeditor/rte-link-browser.js')
|
||||||
|
->invoke('initialize', $this->editorId)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getCurrentPageId(): int
|
||||||
|
{
|
||||||
|
return (int)$this->parameters['pid'];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function initVariables(ServerRequestInterface $request): void
|
||||||
|
{
|
||||||
|
parent::initVariables($request);
|
||||||
|
$queryParameters = $request->getQueryParams();
|
||||||
|
$this->siteUrl = $request->getAttribute('normalizedParams')->getSiteUrl();
|
||||||
|
$this->currentLinkParts = $queryParameters['P']['curUrl'] ?? [];
|
||||||
|
$this->editorId = $queryParameters['editorId'] ?? '';
|
||||||
|
$this->contentsLanguage = $queryParameters['contentsLanguage'] ?? '';
|
||||||
|
$this->contentLanguageService = $this->languageServiceFactory->create($this->contentsLanguage);
|
||||||
|
$tcaFieldConf = [
|
||||||
|
'enableRichtext' => true,
|
||||||
|
'richtextConfiguration' => $this->parameters['richtextConfigurationName'] ?: null,
|
||||||
|
];
|
||||||
|
$this->thisConfig = $this->richtext->getConfiguration(
|
||||||
|
$this->parameters['table'],
|
||||||
|
$this->parameters['fieldName'],
|
||||||
|
(int)$this->parameters['pid'],
|
||||||
|
$this->parameters['recordType'],
|
||||||
|
$tcaFieldConf
|
||||||
|
);
|
||||||
|
$this->buttonConfig = $this->thisConfig['buttons']['link'] ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function initCurrentUrl(): void
|
||||||
|
{
|
||||||
|
if (empty($this->currentLinkParts)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!empty($this->currentLinkParts['url'])) {
|
||||||
|
try {
|
||||||
|
$data = $this->linkService->resolve($this->currentLinkParts['url']);
|
||||||
|
$this->currentLinkParts['type'] = $data['type'];
|
||||||
|
unset($data['type']);
|
||||||
|
$this->currentLinkParts['url'] = $data;
|
||||||
|
if (!empty($this->currentLinkParts['url']['parameters'])) {
|
||||||
|
$this->currentLinkParts['params'] = '&' . $this->currentLinkParts['url']['parameters'];
|
||||||
|
}
|
||||||
|
} catch (UnknownLinkHandlerException $e) {
|
||||||
|
$this->flashMessageService->getMessageQueueByIdentifier()->enqueue(
|
||||||
|
new FlashMessage(message: $e->getMessage(), severity: ContextualFeedbackSeverity::ERROR)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parent::initCurrentUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function renderLinkAttributeFields(ViewInterface $view): string
|
||||||
|
{
|
||||||
|
// Processing the classes configuration
|
||||||
|
if (!empty($this->buttonConfig['properties']['class']['allowedClasses'])) {
|
||||||
|
$classesAnchorArray = is_array($this->buttonConfig['properties']['class']['allowedClasses'])
|
||||||
|
? $this->buttonConfig['properties']['class']['allowedClasses']
|
||||||
|
: GeneralUtility::trimExplode(',', $this->buttonConfig['properties']['class']['allowedClasses'], true);
|
||||||
|
// Collecting allowed classes and configured default values
|
||||||
|
$classesAnchor = [
|
||||||
|
'all' => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
if (is_array($this->thisConfig['classesAnchor'] ?? null)) {
|
||||||
|
foreach ($this->thisConfig['classesAnchor'] as $conf) {
|
||||||
|
if (in_array($conf['class'] ?? null, $classesAnchorArray, true)) {
|
||||||
|
$classesAnchor['all'][] = $conf['class'];
|
||||||
|
if ($conf['type'] === $this->displayedLinkHandlerId) {
|
||||||
|
$classesAnchor[$conf['type']][] = $conf['class'];
|
||||||
|
if (($this->buttonConfig[$conf['type']]['properties']['class']['default'] ?? null) === $conf['class']) {
|
||||||
|
$this->classesAnchorDefault[$conf['type']] = $conf['class'];
|
||||||
|
if (isset($conf['target'])) {
|
||||||
|
$this->classesAnchorDefaultTarget[$conf['type']] = trim((string)$conf['target']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$linkClass = $this->linkAttributeValues['class'] ?? '';
|
||||||
|
if ($linkClass !== '') {
|
||||||
|
$currentLinkClassIsAllowed = true;
|
||||||
|
if (!in_array($linkClass, $classesAnchorArray, true)) {
|
||||||
|
// Current class is not a globally allowed class
|
||||||
|
$currentLinkClassIsAllowed = false;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
isset($classesAnchor[$this->displayedLinkHandlerId])
|
||||||
|
&& in_array($linkClass, $classesAnchor['all'], true)
|
||||||
|
&& !in_array($linkClass, $classesAnchor[$this->displayedLinkHandlerId], true)
|
||||||
|
) {
|
||||||
|
// Current class is limited to specific link types but not available in current link type
|
||||||
|
$currentLinkClassIsAllowed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$currentLinkClassIsAllowed) {
|
||||||
|
$this->classesAnchorJSOptions[$this->displayedLinkHandlerId] ??= '';
|
||||||
|
// Add a dummy option that preserved the current class value (despite being invalid)
|
||||||
|
// in order to prevent unintentional modification of assigned classes.
|
||||||
|
$this->classesAnchorJSOptions[$this->displayedLinkHandlerId] .= sprintf(
|
||||||
|
'<option selected="selected" value="%s">%s</option>',
|
||||||
|
htmlspecialchars($linkClass),
|
||||||
|
htmlspecialchars(
|
||||||
|
@sprintf(
|
||||||
|
'[ ' . $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.noMatchingValue') . ' ]',
|
||||||
|
$linkClass
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructing the class selector options
|
||||||
|
foreach ($classesAnchorArray as $class) {
|
||||||
|
if (
|
||||||
|
!in_array($class, $classesAnchor['all'], true)
|
||||||
|
|| (
|
||||||
|
in_array($class, $classesAnchor['all'], true)
|
||||||
|
&& is_array($classesAnchor[$this->displayedLinkHandlerId] ?? null)
|
||||||
|
&& in_array($class, $classesAnchor[$this->displayedLinkHandlerId])
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
$selected = '';
|
||||||
|
if (
|
||||||
|
(($this->linkAttributeValues['class'] ?? false) === $class)
|
||||||
|
|| ($this->classesAnchorDefault[$this->displayedLinkHandlerId] ?? false) === $class
|
||||||
|
) {
|
||||||
|
$selected = 'selected="selected"';
|
||||||
|
}
|
||||||
|
$classLabel = !empty($this->thisConfig['classes'][$class]['name'])
|
||||||
|
? $this->getPageConfigLabel($this->thisConfig['classes'][$class]['name'], false)
|
||||||
|
: $class;
|
||||||
|
$classStyle = !empty($this->thisConfig['classes'][$class]['value'])
|
||||||
|
? $this->thisConfig['classes'][$class]['value']
|
||||||
|
: '';
|
||||||
|
|
||||||
|
$this->classesAnchorJSOptions[$this->displayedLinkHandlerId] ??= '';
|
||||||
|
$this->classesAnchorJSOptions[$this->displayedLinkHandlerId] .= '<option ' . $selected . ' value="' . htmlspecialchars($class) . '"'
|
||||||
|
. ($classStyle ? ' style="' . htmlspecialchars($classStyle) . '"' : '')
|
||||||
|
. '>' . htmlspecialchars($classLabel)
|
||||||
|
. '</option>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
($this->classesAnchorJSOptions[$this->displayedLinkHandlerId] ?? false)
|
||||||
|
&& !(
|
||||||
|
($this->buttonConfig['properties']['class']['required'] ?? false)
|
||||||
|
|| ($this->buttonConfig[$this->displayedLinkHandlerId]['properties']['class']['required'] ?? false)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
$selected = '';
|
||||||
|
if (!($this->linkAttributeValues['class'] ?? false) && !($this->classesAnchorDefault[$this->displayedLinkHandlerId] ?? false)) {
|
||||||
|
$selected = 'selected="selected"';
|
||||||
|
}
|
||||||
|
$this->classesAnchorJSOptions[$this->displayedLinkHandlerId] = '<option ' . $selected . ' value=""></option>' . $this->classesAnchorJSOptions[$this->displayedLinkHandlerId];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Default target
|
||||||
|
$this->defaultLinkTarget = ($this->classesAnchorDefault[$this->displayedLinkHandlerId] ?? false) && ($this->classesAnchorDefaultTarget[$this->displayedLinkHandlerId] ?? false)
|
||||||
|
? $this->classesAnchorDefaultTarget[$this->displayedLinkHandlerId]
|
||||||
|
: ($this->buttonConfig[$this->displayedLinkHandlerId]['properties']['target']['default'] ?? $this->buttonConfig['properties']['target']['default'] ?? '');
|
||||||
|
|
||||||
|
return parent::renderLinkAttributeFields($view);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Localize a label obtained from Page TSConfig
|
||||||
|
*
|
||||||
|
* @param string $string The label to be localized
|
||||||
|
* @param bool $JScharCode If it needs to be converted to an array of char numbers
|
||||||
|
* @return string Localized string
|
||||||
|
*/
|
||||||
|
protected function getPageConfigLabel(string $string, bool $JScharCode = true): string
|
||||||
|
{
|
||||||
|
$label = $this->getLanguageService()->sL(trim($string));
|
||||||
|
$label = str_replace(['\\\'', '"'], ['\'', '\\"'], $label);
|
||||||
|
return $JScharCode ? GeneralUtility::quoteJSvalue($label) : $label;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function renderCurrentUrl(ViewInterface $view): void
|
||||||
|
{
|
||||||
|
$view->assign('removeCurrentLink', true);
|
||||||
|
parent::renderCurrentUrl($view);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
protected function getAllowedItems(): array
|
||||||
|
{
|
||||||
|
$allowedItems = parent::getAllowedItems();
|
||||||
|
|
||||||
|
if (isset($this->thisConfig['allowedTypes'])) {
|
||||||
|
$allowedItems = array_intersect($allowedItems, GeneralUtility::trimExplode(',', $this->thisConfig['allowedTypes'], true));
|
||||||
|
} elseif (isset($this->thisConfig['blindLinkOptions'])) {
|
||||||
|
// @todo Deprecate this option
|
||||||
|
$allowedItems = array_diff($allowedItems, GeneralUtility::trimExplode(',', $this->thisConfig['blindLinkOptions'], true));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($this->buttonConfig['options'] ?? null) && !empty($this->buttonConfig['options']['removeItems'])) {
|
||||||
|
$allowedItems = array_diff($allowedItems, GeneralUtility::trimExplode(',', $this->buttonConfig['options']['removeItems'], true));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $allowedItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
protected function getAllowedLinkAttributes(): array
|
||||||
|
{
|
||||||
|
$allowedLinkAttributes = parent::getAllowedLinkAttributes();
|
||||||
|
|
||||||
|
if (isset($this->thisConfig['allowedOptions'])) {
|
||||||
|
$allowedLinkAttributes = array_intersect($allowedLinkAttributes, GeneralUtility::trimExplode(',', $this->thisConfig['allowedOptions'], true));
|
||||||
|
} elseif (isset($this->thisConfig['blindLinkFields'])) {
|
||||||
|
// @todo Deprecate this option
|
||||||
|
$allowedLinkAttributes = array_diff($allowedLinkAttributes, GeneralUtility::trimExplode(',', $this->thisConfig['blindLinkFields'], true));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $allowedLinkAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an array of link attribute field rendering definitions
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
protected function getLinkAttributeFieldDefinitions(): array
|
||||||
|
{
|
||||||
|
$fieldRenderingDefinitions = parent::getLinkAttributeFieldDefinitions();
|
||||||
|
$fieldRenderingDefinitions['class'] = $this->getClassField();
|
||||||
|
$fieldRenderingDefinitions['target'] = $this->getTargetField();
|
||||||
|
$fieldRenderingDefinitions['rel'] = $this->getRelField();
|
||||||
|
if (empty($this->buttonConfig['queryParametersSelector']['enabled'])) {
|
||||||
|
unset($fieldRenderingDefinitions['params']);
|
||||||
|
}
|
||||||
|
return $fieldRenderingDefinitions;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getRelField(): string
|
||||||
|
{
|
||||||
|
if (empty($this->buttonConfig['relAttribute']['enabled'])) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$currentRel = '';
|
||||||
|
if ($this->displayedLinkHandler === $this->currentLinkHandler
|
||||||
|
&& !empty($this->currentLinkParts)
|
||||||
|
&& is_string($this->linkAttributeValues['rel'] ?? null)
|
||||||
|
) {
|
||||||
|
$currentRel = $this->linkAttributeValues['rel'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return '
|
||||||
|
<div class="element-browser-form-group">
|
||||||
|
<label for="lrel" class="form-label">'
|
||||||
|
. htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_browse_links.xlf:linkRelationship'))
|
||||||
|
. '</label>
|
||||||
|
<input type="text" name="lrel" class="form-control" value="' . htmlspecialchars($currentRel) . '" />
|
||||||
|
</div>
|
||||||
|
';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getTargetField(): string
|
||||||
|
{
|
||||||
|
$targetSelectorConfig = [];
|
||||||
|
if (is_array($this->buttonConfig['targetSelector'] ?? null)) {
|
||||||
|
$targetSelectorConfig = $this->buttonConfig['targetSelector'];
|
||||||
|
}
|
||||||
|
$target = !empty($this->linkAttributeValues['target']) ? $this->linkAttributeValues['target'] : $this->defaultLinkTarget;
|
||||||
|
$lang = $this->getLanguageService();
|
||||||
|
|
||||||
|
$disabled = $targetSelectorConfig['disabled'] ?? false;
|
||||||
|
if ($disabled) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '
|
||||||
|
<div class="element-browser-form-group">
|
||||||
|
<label for="ltarget" class="form-label">
|
||||||
|
' . htmlspecialchars($lang->sL('LLL:EXT:backend/Resources/Private/Language/locallang_browse_links.xlf:target')) . '
|
||||||
|
</label>
|
||||||
|
<typo3-backend-combobox>
|
||||||
|
<input id="ltarget" type="text" name="ltarget" class="form-control" value="' . htmlspecialchars($this->linkAttributeValues['target'] ?? '') . '" />
|
||||||
|
<typo3-backend-combobox-choice value="_top" icon="actions-window">' . $lang->sL('LLL:EXT:backend/Resources/Private/Language/locallang_browse_links.xlf:top') . '</typo3-backend-combobox-choice>
|
||||||
|
<typo3-backend-combobox-choice value="_blank" icon="actions-window-open">' . $lang->sL('LLL:EXT:backend/Resources/Private/Language/locallang_browse_links.xlf:newWindow') . '</typo3-backend-combobox-choice>
|
||||||
|
</typo3-backend-combobox>
|
||||||
|
</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return html code for the class selector
|
||||||
|
*
|
||||||
|
* @return string the html code to be added to the form
|
||||||
|
*/
|
||||||
|
protected function getClassField(): string
|
||||||
|
{
|
||||||
|
if (!isset($this->classesAnchorJSOptions[$this->displayedLinkHandlerId])) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '
|
||||||
|
<div class="element-browser-form-group">
|
||||||
|
<label for="lclass" class="form-label">
|
||||||
|
' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_browse_links.xlf:class')) . '
|
||||||
|
</label>
|
||||||
|
<select id="lclass" name="lclass" class="t3js-class-selector form-select">
|
||||||
|
' . $this->classesAnchorJSOptions[$this->displayedLinkHandlerId] . '
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string[] Array of body-tag attributes
|
||||||
|
*/
|
||||||
|
protected function getBodyTagAttributes(): array
|
||||||
|
{
|
||||||
|
$parameters = parent::getBodyTagAttributes();
|
||||||
|
$parameters['data-site-url'] = $this->siteUrl;
|
||||||
|
$parameters['data-default-link-target'] = $this->defaultLinkTarget;
|
||||||
|
return $parameters;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the TYPO3 CMS project.
|
||||||
|
*
|
||||||
|
* It is free software; you can redistribute it and/or modify it under
|
||||||
|
* the terms of the GNU General Public License, either version 2
|
||||||
|
* of the License, or any later version.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please read the
|
||||||
|
* LICENSE.txt file that was distributed with this source code.
|
||||||
|
*
|
||||||
|
* The TYPO3 project - inspiring people to share!
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace TYPO3\CMS\RteCKEditor\EventListener;
|
||||||
|
|
||||||
|
use TYPO3\CMS\Core\Attribute\AsEventListener;
|
||||||
|
use TYPO3\CMS\Core\Configuration\Event\AfterRichtextConfigurationPreparedEvent;
|
||||||
|
use TYPO3\CMS\RteCKEditor\Configuration\CKEditor5Migrator;
|
||||||
|
|
||||||
|
final readonly class AfterRichtextConfigurationPreparedEventListener
|
||||||
|
{
|
||||||
|
#[AsEventListener('typo3/cms-rte-ckeditor/migrate-ckeditor4-configuration')]
|
||||||
|
public function __invoke(AfterRichtextConfigurationPreparedEvent $event)
|
||||||
|
{
|
||||||
|
$event->setConfiguration((new CKEditor5Migrator($event->getConfiguration()))->get());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the TYPO3 CMS project.
|
||||||
|
*
|
||||||
|
* It is free software; you can redistribute it and/or modify it under
|
||||||
|
* the terms of the GNU General Public License, either version 2
|
||||||
|
* of the License, or any later version.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please read the
|
||||||
|
* LICENSE.txt file that was distributed with this source code.
|
||||||
|
*
|
||||||
|
* The TYPO3 project - inspiring people to share!
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace TYPO3\CMS\RteCKEditor\Form\Element\Event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This event is fired after processing external plugin configuration.
|
||||||
|
*/
|
||||||
|
final class AfterGetExternalPluginsEvent
|
||||||
|
{
|
||||||
|
public function __construct(private array $configuration, private readonly array $data) {}
|
||||||
|
|
||||||
|
public function getData(): array
|
||||||
|
{
|
||||||
|
return $this->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getConfiguration(): array
|
||||||
|
{
|
||||||
|
return $this->configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setConfiguration(array $configuration): void
|
||||||
|
{
|
||||||
|
$this->configuration = $configuration;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the TYPO3 CMS project.
|
||||||
|
*
|
||||||
|
* It is free software; you can redistribute it and/or modify it under
|
||||||
|
* the terms of the GNU General Public License, either version 2
|
||||||
|
* of the License, or any later version.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please read the
|
||||||
|
* LICENSE.txt file that was distributed with this source code.
|
||||||
|
*
|
||||||
|
* The TYPO3 project - inspiring people to share!
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace TYPO3\CMS\RteCKEditor\Form\Element\Event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This event is fired after preparing the editor configuration.
|
||||||
|
*/
|
||||||
|
final class AfterPrepareConfigurationForEditorEvent
|
||||||
|
{
|
||||||
|
public function __construct(private array $configuration, private readonly array $data) {}
|
||||||
|
|
||||||
|
public function getData(): array
|
||||||
|
{
|
||||||
|
return $this->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getConfiguration(): array
|
||||||
|
{
|
||||||
|
return $this->configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setConfiguration(array $configuration): void
|
||||||
|
{
|
||||||
|
$this->configuration = $configuration;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the TYPO3 CMS project.
|
||||||
|
*
|
||||||
|
* It is free software; you can redistribute it and/or modify it under
|
||||||
|
* the terms of the GNU General Public License, either version 2
|
||||||
|
* of the License, or any later version.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please read the
|
||||||
|
* LICENSE.txt file that was distributed with this source code.
|
||||||
|
*
|
||||||
|
* The TYPO3 project - inspiring people to share!
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace TYPO3\CMS\RteCKEditor\Form\Element\Event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This event is fired before processing external plugin configuration.
|
||||||
|
*/
|
||||||
|
final class BeforeGetExternalPluginsEvent
|
||||||
|
{
|
||||||
|
public function __construct(private array $configuration, private readonly array $data) {}
|
||||||
|
|
||||||
|
public function getData(): array
|
||||||
|
{
|
||||||
|
return $this->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getConfiguration(): array
|
||||||
|
{
|
||||||
|
return $this->configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setConfiguration(array $configuration): void
|
||||||
|
{
|
||||||
|
$this->configuration = $configuration;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the TYPO3 CMS project.
|
||||||
|
*
|
||||||
|
* It is free software; you can redistribute it and/or modify it under
|
||||||
|
* the terms of the GNU General Public License, either version 2
|
||||||
|
* of the License, or any later version.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please read the
|
||||||
|
* LICENSE.txt file that was distributed with this source code.
|
||||||
|
*
|
||||||
|
* The TYPO3 project - inspiring people to share!
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace TYPO3\CMS\RteCKEditor\Form\Element\Event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This event is fired before starting the prepare of the editor configuration.
|
||||||
|
*/
|
||||||
|
final class BeforePrepareConfigurationForEditorEvent
|
||||||
|
{
|
||||||
|
public function __construct(private array $configuration, private readonly array $data) {}
|
||||||
|
|
||||||
|
public function getData(): array
|
||||||
|
{
|
||||||
|
return $this->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getConfiguration(): array
|
||||||
|
{
|
||||||
|
return $this->configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setConfiguration(array $configuration): void
|
||||||
|
{
|
||||||
|
$this->configuration = $configuration;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,462 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the TYPO3 CMS project.
|
||||||
|
*
|
||||||
|
* It is free software; you can redistribute it and/or modify it under
|
||||||
|
* the terms of the GNU General Public License, either version 2
|
||||||
|
* of the License, or any later version.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please read the
|
||||||
|
* LICENSE.txt file that was distributed with this source code.
|
||||||
|
*
|
||||||
|
* The TYPO3 project - inspiring people to share!
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace TYPO3\CMS\RteCKEditor\Form\Element;
|
||||||
|
|
||||||
|
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||||
|
use TYPO3\CMS\Backend\Form\Element\AbstractFormElement;
|
||||||
|
use TYPO3\CMS\Backend\Routing\UriBuilder;
|
||||||
|
use TYPO3\CMS\Core\Core\Environment;
|
||||||
|
use TYPO3\CMS\Core\Localization\Locales;
|
||||||
|
use TYPO3\CMS\Core\Page\JavaScriptModuleInstruction;
|
||||||
|
use TYPO3\CMS\Core\SystemResource\Publishing\SystemResourcePublisherInterface;
|
||||||
|
use TYPO3\CMS\Core\SystemResource\SystemResourceFactory;
|
||||||
|
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||||
|
use TYPO3\CMS\Core\Utility\PathUtility;
|
||||||
|
use TYPO3\CMS\RteCKEditor\Form\Element\Event\AfterGetExternalPluginsEvent;
|
||||||
|
use TYPO3\CMS\RteCKEditor\Form\Element\Event\AfterPrepareConfigurationForEditorEvent;
|
||||||
|
use TYPO3\CMS\RteCKEditor\Form\Element\Event\BeforeGetExternalPluginsEvent;
|
||||||
|
use TYPO3\CMS\RteCKEditor\Form\Element\Event\BeforePrepareConfigurationForEditorEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render rich text editor in FormEngine
|
||||||
|
* @internal This is a specific Backend FormEngine implementation and is not considered part of the Public TYPO3 API.
|
||||||
|
*/
|
||||||
|
class RichTextElement extends AbstractFormElement
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Default field wizards enabled for this element.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $defaultFieldWizard = [
|
||||||
|
'localizationStateSelector' => [
|
||||||
|
'renderType' => 'localizationStateSelector',
|
||||||
|
],
|
||||||
|
'otherLanguageContent' => [
|
||||||
|
'renderType' => 'otherLanguageContent',
|
||||||
|
'after' => [
|
||||||
|
'localizationStateSelector',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'defaultLanguageDifferences' => [
|
||||||
|
'renderType' => 'defaultLanguageDifferences',
|
||||||
|
'after' => [
|
||||||
|
'otherLanguageContent',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This property contains configuration related to the RTE
|
||||||
|
* But only the .editor configuration part
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $rteConfiguration = [];
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private readonly EventDispatcherInterface $eventDispatcher,
|
||||||
|
private readonly UriBuilder $uriBuilder,
|
||||||
|
private readonly Locales $locales,
|
||||||
|
private readonly SystemResourcePublisherInterface $resourcePublisher,
|
||||||
|
private readonly SystemResourceFactory $systemResourceFactory,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the ckeditor element
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function render(): array
|
||||||
|
{
|
||||||
|
$languageService = $this->getLanguageService();
|
||||||
|
|
||||||
|
$resultArray = $this->initializeResultArray();
|
||||||
|
$parameterArray = $this->data['parameterArray'];
|
||||||
|
$config = $parameterArray['fieldConf']['config'];
|
||||||
|
|
||||||
|
$fieldId = $this->sanitizeFieldId($parameterArray['itemFormElName']);
|
||||||
|
$itemFormElementName = $this->data['parameterArray']['itemFormElName'];
|
||||||
|
|
||||||
|
$value = $this->data['parameterArray']['itemFormElValue'] ?? null;
|
||||||
|
|
||||||
|
$fieldInformationResult = $this->renderFieldInformation();
|
||||||
|
$fieldInformationHtml = $fieldInformationResult['html'];
|
||||||
|
$resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldInformationResult, false);
|
||||||
|
|
||||||
|
$fieldControlResult = $this->renderFieldControl();
|
||||||
|
$fieldControlHtml = $fieldControlResult['html'];
|
||||||
|
$resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldControlResult, false);
|
||||||
|
|
||||||
|
$fieldWizardResult = $this->renderFieldWizard();
|
||||||
|
$fieldWizardHtml = $fieldWizardResult['html'];
|
||||||
|
$resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldWizardResult, false);
|
||||||
|
|
||||||
|
$this->rteConfiguration = $config['richtextConfiguration']['editor'] ?? [];
|
||||||
|
$ckeditorConfiguration = $this->resolveCkEditorConfiguration();
|
||||||
|
|
||||||
|
$ckeditorAttributes = GeneralUtility::implodeAttributes([
|
||||||
|
'id' => $fieldId . 'ckeditor5',
|
||||||
|
'options' => GeneralUtility::jsonEncodeForHtmlAttribute($ckeditorConfiguration, false),
|
||||||
|
], true);
|
||||||
|
|
||||||
|
$textareaAttributes = GeneralUtility::implodeAttributes([
|
||||||
|
'slot' => 'textarea',
|
||||||
|
'id' => $fieldId,
|
||||||
|
'name' => $itemFormElementName,
|
||||||
|
'rows' => '18',
|
||||||
|
'class' => 'form-control',
|
||||||
|
'data-formengine-validation-rules' => $this->getValidationDataAsJsonString($config),
|
||||||
|
], true);
|
||||||
|
|
||||||
|
$html = [];
|
||||||
|
$html[] = $fieldInformationHtml;
|
||||||
|
$html[] = '<div class="form-control-wrap">';
|
||||||
|
$html[] = '<div class="form-wizards-wrap">';
|
||||||
|
$html[] = '<div class="form-wizards-item-element">';
|
||||||
|
$html[] = '<typo3-rte-ckeditor-ckeditor5 ' . $ckeditorAttributes . '>';
|
||||||
|
$html[] = '<textarea ' . $textareaAttributes . '>';
|
||||||
|
$html[] = htmlspecialchars((string)$value);
|
||||||
|
$html[] = '</textarea>';
|
||||||
|
$html[] = '</typo3-rte-ckeditor-ckeditor5>';
|
||||||
|
$html[] = '</div>';
|
||||||
|
if (!empty($fieldControlHtml)) {
|
||||||
|
$html[] = '<div class="form-wizards-item-aside form-wizards-item-aside--field-control">';
|
||||||
|
$html[] = '<div class="btn-group">';
|
||||||
|
$html[] = $fieldControlHtml;
|
||||||
|
$html[] = '</div>';
|
||||||
|
$html[] = '</div>';
|
||||||
|
}
|
||||||
|
if (!empty($fieldWizardHtml)) {
|
||||||
|
$html[] = '<div class="form-wizards-item-bottom">';
|
||||||
|
$html[] = $fieldWizardHtml;
|
||||||
|
$html[] = '</div>';
|
||||||
|
}
|
||||||
|
$html[] = '</div>';
|
||||||
|
$html[] = '</div>';
|
||||||
|
|
||||||
|
$nullControlNameEscaped = htmlspecialchars('control[active][' . $this->data['tableName'] . '][' . $this->data['databaseRow']['uid'] . '][' . $this->data['fieldName'] . ']');
|
||||||
|
|
||||||
|
$fullElement = $html;
|
||||||
|
// @todo - The logic for hasNullCheckboxButNoPlaceholder() / hasNullCheckboxWithPlaceholder() wants to be streamlined here;
|
||||||
|
// Ideally, a placeholder should only be an instructive placeholder and not conflict with usage of a "default fallback".
|
||||||
|
// Instead of "[x] Set value (Default: …)" it might better be to use "[x] Set value (Fallback: …)", because what is shown as "default" here is not really the final value
|
||||||
|
// of the saved element, but what is inerhited as fallback values from a possible rendering chain. Looking at you, sys_file_reference IRRE.
|
||||||
|
if ($this->hasNullCheckboxButNoPlaceholder()) {
|
||||||
|
$checked = $value !== null ? ' checked="checked"' : '';
|
||||||
|
$fullElement = [];
|
||||||
|
$fullElement[] = '<div class="t3-form-field-disable"></div>';
|
||||||
|
$fullElement[] = '<div class="form-check t3-form-field-eval-null-checkbox">';
|
||||||
|
$fullElement[] = '<input type="hidden" name="' . $nullControlNameEscaped . '" value="0" />';
|
||||||
|
$fullElement[] = '<input type="checkbox" class="form-check-input" name="' . $nullControlNameEscaped . '" id="' . $nullControlNameEscaped . '" value="1"' . $checked . ' />';
|
||||||
|
$fullElement[] = '<label class="form-check-label" for="' . $nullControlNameEscaped . '">';
|
||||||
|
$fullElement[] = $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.nullCheckbox');
|
||||||
|
$fullElement[] = '</label>';
|
||||||
|
$fullElement[] = '</div>';
|
||||||
|
$fullElement[] = implode(LF, $html);
|
||||||
|
} elseif ($this->hasNullCheckboxWithPlaceholder()) {
|
||||||
|
$checked = $value !== null ? ' checked="checked"' : '';
|
||||||
|
// Note that we draw the raw placeholder from $config instead of $ckeditorConfiguration so it
|
||||||
|
// contains the full HTML markup. $ckeditorConfiguration['placeholder'] has strip_tags() applied.
|
||||||
|
// The full HTML is only emitted with htmlspecialchars(), and later parsed by CKEditor.
|
||||||
|
// The HTML-stripped placeholder is used for the label of the nullable checkbox.
|
||||||
|
|
||||||
|
$placeholder = trim((string)($ckeditorConfiguration['placeholder'] ?? ''));
|
||||||
|
$defaultValue = '';
|
||||||
|
$rawPlaceholder = trim((string)($config['placeholder'] ?? ''));
|
||||||
|
if ($rawPlaceholder !== '') {
|
||||||
|
$defaultValue = $rawPlaceholder;
|
||||||
|
}
|
||||||
|
if ($placeholder !== '') {
|
||||||
|
$shortenedPlaceholder = GeneralUtility::fixed_lgd_cs($placeholder, 20);
|
||||||
|
if ($placeholder !== $shortenedPlaceholder) {
|
||||||
|
$overrideLabel = sprintf(
|
||||||
|
$languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.placeholder.override'),
|
||||||
|
'<span title="' . htmlspecialchars($placeholder) . '">' . htmlspecialchars($shortenedPlaceholder) . '</span>'
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$overrideLabel = sprintf(
|
||||||
|
$languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.placeholder.override'),
|
||||||
|
htmlspecialchars($placeholder)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$overrideLabel = $languageService->sL(
|
||||||
|
'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.placeholder.override_not_available'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$placeholderCkeditorAttributes = GeneralUtility::implodeAttributes([
|
||||||
|
'id' => $fieldId . '-placeholder-ckeditor5',
|
||||||
|
'options' => GeneralUtility::jsonEncodeForHtmlAttribute([
|
||||||
|
...$ckeditorConfiguration,
|
||||||
|
'readOnly' => true,
|
||||||
|
], false),
|
||||||
|
], true);
|
||||||
|
|
||||||
|
$placeholderTextareaAttributes = GeneralUtility::implodeAttributes([
|
||||||
|
'slot' => 'textarea',
|
||||||
|
'id' => $fieldId . '-placeholder',
|
||||||
|
'rows' => '18',
|
||||||
|
'class' => 'form-control',
|
||||||
|
], true);
|
||||||
|
|
||||||
|
$fullElement = [];
|
||||||
|
$fullElement[] = '<div class="form-check t3js-form-field-eval-null-placeholder-checkbox">';
|
||||||
|
$fullElement[] = '<input type="hidden" name="' . $nullControlNameEscaped . '" value="0" />';
|
||||||
|
$fullElement[] = '<input type="checkbox" class="form-check-input" name="' . $nullControlNameEscaped . '" id="' . $nullControlNameEscaped . '" value="1"' . $checked . ' />';
|
||||||
|
$fullElement[] = '<label class="form-check-label" for="' . $nullControlNameEscaped . '">';
|
||||||
|
$fullElement[] = $overrideLabel;
|
||||||
|
$fullElement[] = '</label>';
|
||||||
|
$fullElement[] = '</div>';
|
||||||
|
$fullElement[] = '<div class="t3js-formengine-placeholder-placeholder">';
|
||||||
|
$fullElement[] = '<div class="form-control-wrap">';
|
||||||
|
$fullElement[] = '<typo3-rte-ckeditor-ckeditor5 ' . $placeholderCkeditorAttributes . '>';
|
||||||
|
$fullElement[] = '<textarea ' . $placeholderTextareaAttributes . '>';
|
||||||
|
$fullElement[] = htmlspecialchars($defaultValue);
|
||||||
|
$fullElement[] = '</textarea>';
|
||||||
|
$fullElement[] = '</typo3-rte-ckeditor-ckeditor5>';
|
||||||
|
$fullElement[] = '</div>';
|
||||||
|
$fullElement[] = '</div>';
|
||||||
|
$fullElement[] = '<div class="t3js-formengine-placeholder-formfield">';
|
||||||
|
$fullElement[] = implode(LF, $html);
|
||||||
|
$fullElement[] = '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$fullElement = '<div class="formengine-field-item t3js-formengine-field-item">' . implode(LF, $fullElement) . '</div>';
|
||||||
|
|
||||||
|
$resultArray['html'] = $this->wrapWithFieldsetAndLegend($fullElement);
|
||||||
|
$resultArray['javaScriptModules'][] = JavaScriptModuleInstruction::create('@typo3/rte-ckeditor/ckeditor5.js');
|
||||||
|
|
||||||
|
$uiLanguage = $ckeditorConfiguration['language']['ui'];
|
||||||
|
if ($this->translationExists($uiLanguage)) {
|
||||||
|
$resultArray['javaScriptModules'][] = JavaScriptModuleInstruction::create('@typo3/ckeditor5/translations/' . $uiLanguage . '.js');
|
||||||
|
}
|
||||||
|
|
||||||
|
$contentLanguage = $ckeditorConfiguration['language']['content'];
|
||||||
|
if ($this->translationExists($contentLanguage)) {
|
||||||
|
$resultArray['javaScriptModules'][] = JavaScriptModuleInstruction::create('@typo3/ckeditor5/translations/' . $contentLanguage . '.js');
|
||||||
|
}
|
||||||
|
|
||||||
|
$resultArray['stylesheetFiles'][] = 'EXT:rte_ckeditor/Resources/Public/Css/editor.css';
|
||||||
|
|
||||||
|
return $resultArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the contents language iso code
|
||||||
|
*/
|
||||||
|
protected function getLanguageIsoCodeOfContent(): string
|
||||||
|
{
|
||||||
|
$currentLanguageUid = ($this->data['databaseRow']['language_tag'] ?? 0);
|
||||||
|
if (is_array($currentLanguageUid)) {
|
||||||
|
$currentLanguageUid = $currentLanguageUid[0];
|
||||||
|
}
|
||||||
|
$contentLanguageUid = (int)max($currentLanguageUid, 0);
|
||||||
|
if ($contentLanguageUid) {
|
||||||
|
// the language rows might not be fully initialized, so we fall back to en-US in this case
|
||||||
|
$contentLanguage = $this->data['systemLanguageRows'][$currentLanguageUid]['iso'] ?? 'en-US';
|
||||||
|
} else {
|
||||||
|
$contentLanguage = $this->rteConfiguration['config']['defaultContentLanguage'] ?? 'en-US';
|
||||||
|
}
|
||||||
|
$languageCodeParts = explode('_', $contentLanguage);
|
||||||
|
$contentLanguage = strtolower($languageCodeParts[0]) . (!empty($languageCodeParts[1]) ? '_' . strtoupper($languageCodeParts[1]) : '');
|
||||||
|
// Find the configured language in the list of localization locales, if not found, default to 'en'.
|
||||||
|
if ($contentLanguage === 'default' || !$this->locales->isValidLanguageKey($contentLanguage)) {
|
||||||
|
$contentLanguage = 'en';
|
||||||
|
}
|
||||||
|
return $contentLanguage;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function resolveCkEditorConfiguration(): array
|
||||||
|
{
|
||||||
|
$configuration = $this->prepareConfigurationForEditor();
|
||||||
|
|
||||||
|
foreach ($this->getExtraPlugins() as $extraPluginName => $extraPluginConfig) {
|
||||||
|
$configName = $extraPluginConfig['configName'] ?? $extraPluginName;
|
||||||
|
if (!empty($extraPluginConfig['config']) && is_array($extraPluginConfig['config'])) {
|
||||||
|
if (empty($configuration[$configName])) {
|
||||||
|
$configuration[$configName] = $extraPluginConfig['config'];
|
||||||
|
} elseif (is_array($configuration[$configName])) {
|
||||||
|
$configuration[$configName] = array_replace_recursive($extraPluginConfig['config'], $configuration[$configName]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isset($this->data['parameterArray']['fieldConf']['config']['placeholder'])) {
|
||||||
|
// Note that HTML tags are stripped here, because CKEditor does not parse placeholder text.
|
||||||
|
// Without it, the HTML code would be displayed as-is.
|
||||||
|
$configuration['placeholder'] = strip_tags((string)$this->data['parameterArray']['fieldConf']['config']['placeholder']);
|
||||||
|
}
|
||||||
|
return $configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get configuration of external/additional plugins
|
||||||
|
*/
|
||||||
|
protected function getExtraPlugins(): array
|
||||||
|
{
|
||||||
|
$externalPlugins = $this->rteConfiguration['externalPlugins'] ?? [];
|
||||||
|
$externalPlugins = $this->eventDispatcher
|
||||||
|
->dispatch(new BeforeGetExternalPluginsEvent($externalPlugins, $this->data))
|
||||||
|
->getConfiguration();
|
||||||
|
|
||||||
|
$urlParameters = [
|
||||||
|
'P' => [
|
||||||
|
'table' => $this->data['tableName'],
|
||||||
|
'uid' => $this->data['databaseRow']['uid'],
|
||||||
|
'fieldName' => $this->data['fieldName'],
|
||||||
|
'recordType' => $this->data['recordTypeValue'],
|
||||||
|
'pid' => $this->data['effectivePid'],
|
||||||
|
'richtextConfigurationName' => $this->data['parameterArray']['fieldConf']['config']['richtextConfigurationName'],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$pluginConfiguration = [];
|
||||||
|
foreach ($externalPlugins as $pluginName => $configuration) {
|
||||||
|
$pluginConfiguration[$pluginName] = [
|
||||||
|
'configName' => $configuration['configName'] ?? $pluginName,
|
||||||
|
];
|
||||||
|
unset($configuration['configName']);
|
||||||
|
// CKEditor 4 style config, unused in CKEditor 5 and not forwarded to the resutling plugin config
|
||||||
|
unset($configuration['resource']);
|
||||||
|
|
||||||
|
if ($configuration['route'] ?? null) {
|
||||||
|
$configuration['routeUrl'] = (string)$this->uriBuilder->buildUriFromRoute($configuration['route'], $urlParameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
$pluginConfiguration[$pluginName]['config'] = $configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pluginConfiguration = $this->eventDispatcher
|
||||||
|
->dispatch(new AfterGetExternalPluginsEvent($pluginConfiguration, $this->data))
|
||||||
|
->getConfiguration();
|
||||||
|
return $pluginConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add configuration to replace LLL: references with the translated value
|
||||||
|
*/
|
||||||
|
protected function replaceLanguageFileReferences(array $configuration): array
|
||||||
|
{
|
||||||
|
foreach ($configuration as $key => $value) {
|
||||||
|
if (is_array($value)) {
|
||||||
|
$configuration[$key] = $this->replaceLanguageFileReferences($value);
|
||||||
|
} elseif (is_string($value)) {
|
||||||
|
$configuration[$key] = $this->getLanguageService()->sL($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add configuration to replace absolute EXT: paths with relative ones
|
||||||
|
*/
|
||||||
|
protected function replaceAbsolutePathsToRelativeResourcesPath(array $configuration): array
|
||||||
|
{
|
||||||
|
foreach ($configuration as $key => $value) {
|
||||||
|
if (is_array($value)) {
|
||||||
|
$configuration[$key] = $this->replaceAbsolutePathsToRelativeResourcesPath($value);
|
||||||
|
} elseif (is_string($value)
|
||||||
|
&& $value !== ''
|
||||||
|
// @todo: this check should vanish, once not every config key is iterated over
|
||||||
|
&& PathUtility::isExtensionPath(strtoupper($value), true)
|
||||||
|
) {
|
||||||
|
$configuration[$key] = $this->resolveUrlPath($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves system resources an absolute web URL
|
||||||
|
*/
|
||||||
|
protected function resolveUrlPath(string $value): string
|
||||||
|
{
|
||||||
|
$resource = $this->systemResourceFactory->createPublicResource($value);
|
||||||
|
return (string)$this->resourcePublisher->generateUri($resource, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compiles the configuration set from the outside
|
||||||
|
* to have it easily injected into the CKEditor.
|
||||||
|
*
|
||||||
|
* @return array the configuration
|
||||||
|
*/
|
||||||
|
protected function prepareConfigurationForEditor(): array
|
||||||
|
{
|
||||||
|
// Ensure custom config is empty so nothing additional is loaded
|
||||||
|
// Of course this can be overridden by the editor configuration below
|
||||||
|
$configuration = [
|
||||||
|
'customConfig' => '',
|
||||||
|
'label' => $this->data['parameterArray']['fieldConf']['label'] ?? '',
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($this->data['parameterArray']['fieldConf']['config']['readOnly'] ?? false) {
|
||||||
|
$configuration['readOnly'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($this->rteConfiguration['config'] ?? null)) {
|
||||||
|
$configuration = array_replace_recursive($configuration, $this->rteConfiguration['config']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$configuration = $this->eventDispatcher
|
||||||
|
->dispatch(new BeforePrepareConfigurationForEditorEvent($configuration, $this->data))
|
||||||
|
->getConfiguration();
|
||||||
|
|
||||||
|
// Set the UI language of the editor if not hard-coded by the existing configuration
|
||||||
|
if (empty($configuration['language'])
|
||||||
|
|| (is_array($configuration['language']) && empty($configuration['language']['ui']))
|
||||||
|
) {
|
||||||
|
$userLang = (string)($this->getBackendUser()->user['lang'] ?: 'en');
|
||||||
|
$configuration['language']['ui'] = $userLang === 'default' ? 'en' : $userLang;
|
||||||
|
} elseif (!is_array($configuration['language'])) {
|
||||||
|
$configuration['language'] = [
|
||||||
|
'ui' => $configuration['language'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
$configuration['language']['content'] = $this->getLanguageIsoCodeOfContent();
|
||||||
|
|
||||||
|
// Replace all label references
|
||||||
|
$configuration = $this->replaceLanguageFileReferences($configuration);
|
||||||
|
// Replace all paths
|
||||||
|
$configuration = $this->replaceAbsolutePathsToRelativeResourcesPath($configuration);
|
||||||
|
|
||||||
|
// unless explicitly set, the debug mode is enabled in development context
|
||||||
|
if (!isset($configuration['debug'])) {
|
||||||
|
$configuration['debug'] = ($GLOBALS['TYPO3_CONF_VARS']['BE']['debug'] ?? false) && Environment::getContext()->isDevelopment();
|
||||||
|
}
|
||||||
|
|
||||||
|
$configuration = $this->eventDispatcher
|
||||||
|
->dispatch(new AfterPrepareConfigurationForEditorEvent($configuration, $this->data))
|
||||||
|
->getConfiguration();
|
||||||
|
|
||||||
|
return $configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function sanitizeFieldId(string $itemFormElementName): string
|
||||||
|
{
|
||||||
|
$fieldId = (string)preg_replace('/[^a-zA-Z0-9_:-]/', '_', $itemFormElementName);
|
||||||
|
return htmlspecialchars((string)preg_replace('/^[^a-zA-Z]/', 'x', $fieldId));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function translationExists(string $language): bool
|
||||||
|
{
|
||||||
|
$fileName = GeneralUtility::getFileAbsFileName('EXT:rte_ckeditor/Resources/Public/Contrib/translations/' . $language . '.js');
|
||||||
|
return file_exists($fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the TYPO3 CMS project.
|
||||||
|
*
|
||||||
|
* It is free software; you can redistribute it and/or modify it under
|
||||||
|
* the terms of the GNU General Public License, either version 2
|
||||||
|
* of the License, or any later version.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please read the
|
||||||
|
* LICENSE.txt file that was distributed with this source code.
|
||||||
|
*
|
||||||
|
* The TYPO3 project - inspiring people to share!
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace TYPO3\CMS\RteCKEditor\Form\Resolver;
|
||||||
|
|
||||||
|
use TYPO3\CMS\Backend\Form\NodeResolverInterface;
|
||||||
|
use TYPO3\CMS\RteCKEditor\Form\Element\RichTextElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This resolver will return the RichTextElement render class if RTE is enabled for this field.
|
||||||
|
*
|
||||||
|
* @internal This is a specific Backend FormEngine implementation and is not considered part of the Public TYPO3 API.
|
||||||
|
*/
|
||||||
|
class RichTextNodeResolver implements NodeResolverInterface
|
||||||
|
{
|
||||||
|
protected array $data;
|
||||||
|
|
||||||
|
public function setData(array $data): void
|
||||||
|
{
|
||||||
|
$this->data = $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns RichTextElement as class name if RTE widget should be rendered.
|
||||||
|
*
|
||||||
|
* @return string|null New class name or null if this resolver does not change current class name.
|
||||||
|
*/
|
||||||
|
public function resolve(): ?string
|
||||||
|
{
|
||||||
|
$parameterArray = $this->data['parameterArray'];
|
||||||
|
if (// If RTE is enabled for field
|
||||||
|
(bool)($parameterArray['fieldConf']['config']['enableRichtext'] ?? false) === true
|
||||||
|
// If RTE config is found (prepared by TcaText data provider)
|
||||||
|
&& is_array($parameterArray['fieldConf']['config']['richtextConfiguration'] ?? null)
|
||||||
|
// If RTE is not disabled on configuration level
|
||||||
|
&& !($parameterArray['fieldConf']['config']['richtextConfiguration']['disabled'] ?? false)
|
||||||
|
) {
|
||||||
|
return RichTextElement::class;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use TYPO3\CMS\RteCKEditor\Controller\BrowseLinksController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Definitions of routes for rte-ckeditor.
|
||||||
|
*/
|
||||||
|
|
||||||
|
return [
|
||||||
|
// Register RTE browse links wizard
|
||||||
|
'rteckeditor_wizard_browse_links' => [
|
||||||
|
'path' => '/rte/wizard/browselinks',
|
||||||
|
'target' => BrowseLinksController::class . '::mainAction',
|
||||||
|
],
|
||||||
|
];
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'dependencies' => [
|
||||||
|
'backend',
|
||||||
|
],
|
||||||
|
'imports' => [
|
||||||
|
'@typo3/rte-ckeditor/' => [
|
||||||
|
'path' => 'EXT:rte_ckeditor/Resources/Public/JavaScript/',
|
||||||
|
'exclude' => [
|
||||||
|
'EXT:core/Resources/Public/JavaScript/legacy/',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'@typo3/ckeditor5/translations/' => 'EXT:rte_ckeditor/Resources/Public/Contrib/translations/',
|
||||||
|
'@ckeditor/ckeditor5-alignment' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-alignment.js',
|
||||||
|
'@ckeditor/ckeditor5-autoformat' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-autoformat.js',
|
||||||
|
'@ckeditor/ckeditor5-basic-styles' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-basic-styles.js',
|
||||||
|
'@ckeditor/ckeditor5-block-quote' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-block-quote.js',
|
||||||
|
'@ckeditor/ckeditor5-clipboard' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-clipboard.js',
|
||||||
|
'@ckeditor/ckeditor5-code-block' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-code-block.js',
|
||||||
|
'@ckeditor/ckeditor5-core' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-core.js',
|
||||||
|
'@ckeditor/ckeditor5-editor-classic' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-editor-classic.js',
|
||||||
|
'@ckeditor/ckeditor5-editor-decoupled' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-editor-decoupled.js',
|
||||||
|
'@ckeditor/ckeditor5-editor-multi-root' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-editor-multi-root.js',
|
||||||
|
'@ckeditor/ckeditor5-engine' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-engine.js',
|
||||||
|
'@ckeditor/ckeditor5-enter' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-enter.js',
|
||||||
|
'@ckeditor/ckeditor5-essentials' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-essentials.js',
|
||||||
|
'@ckeditor/ckeditor5-find-and-replace' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-find-and-replace.js',
|
||||||
|
'@ckeditor/ckeditor5-font' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-font.js',
|
||||||
|
'@ckeditor/ckeditor5-fullscreen' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-fullscreen.js',
|
||||||
|
'@ckeditor/ckeditor5-heading' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-heading.js',
|
||||||
|
'@ckeditor/ckeditor5-highlight' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-highlight.js',
|
||||||
|
'@ckeditor/ckeditor5-horizontal-line' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-horizontal-line.js',
|
||||||
|
'@ckeditor/ckeditor5-html-support' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-html-support.js',
|
||||||
|
'@ckeditor/ckeditor5-icons' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-icons.js',
|
||||||
|
'@ckeditor/ckeditor5-image' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-image.js',
|
||||||
|
'@ckeditor/ckeditor5-indent' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-indent.js',
|
||||||
|
'@ckeditor/ckeditor5-inspector' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-inspector.js',
|
||||||
|
'@ckeditor/ckeditor5-language' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-language.js',
|
||||||
|
'@ckeditor/ckeditor5-link' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-link.js',
|
||||||
|
'@ckeditor/ckeditor5-list' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-list.js',
|
||||||
|
'@ckeditor/ckeditor5-mention' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-mention.js',
|
||||||
|
'@ckeditor/ckeditor5-paragraph' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-paragraph.js',
|
||||||
|
'@ckeditor/ckeditor5-paste-from-office' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-paste-from-office.js',
|
||||||
|
'@ckeditor/ckeditor5-remove-format' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-remove-format.js',
|
||||||
|
'@ckeditor/ckeditor5-select-all' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-select-all.js',
|
||||||
|
'@ckeditor/ckeditor5-show-blocks' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-show-blocks.js',
|
||||||
|
'@ckeditor/ckeditor5-source-editing' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-source-editing.js',
|
||||||
|
'@ckeditor/ckeditor5-special-characters' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-special-characters.js',
|
||||||
|
'@ckeditor/ckeditor5-style' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-style.js',
|
||||||
|
'@ckeditor/ckeditor5-table' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-table.js',
|
||||||
|
'@ckeditor/ckeditor5-theme-lark' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-theme-lark.js',
|
||||||
|
'@ckeditor/ckeditor5-typing' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-typing.js',
|
||||||
|
'@ckeditor/ckeditor5-ui' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-ui.js',
|
||||||
|
'@ckeditor/ckeditor5-undo' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-undo.js',
|
||||||
|
'@ckeditor/ckeditor5-upload' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-upload.js',
|
||||||
|
'@ckeditor/ckeditor5-utils' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-utils.js',
|
||||||
|
'@ckeditor/ckeditor5-watchdog' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-watchdog.js',
|
||||||
|
'@ckeditor/ckeditor5-widget' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-widget.js',
|
||||||
|
'@ckeditor/ckeditor5-word-count' => 'EXT:rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-word-count.js',
|
||||||
|
],
|
||||||
|
];
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
# Load default processing options
|
||||||
|
imports:
|
||||||
|
- { resource: 'EXT:rte_ckeditor/Configuration/RTE/Processing.yaml' }
|
||||||
|
- { resource: 'EXT:rte_ckeditor/Configuration/RTE/Editor/Base.yaml' }
|
||||||
|
- { resource: 'EXT:rte_ckeditor/Configuration/RTE/Editor/Plugins.yaml' }
|
||||||
|
#- { resource: 'EXT:rte_ckeditor/Configuration/RTE/Editor/LinkBrowser.yaml' }
|
||||||
|
# Additional optional TYPO3 specific configuration is available via ./Editor/LinkBrowser.yaml
|
||||||
|
# See https://docs.typo3.org/c/typo3/cms-rte-ckeditor/main/en-us/Configuration/Reference.html
|
||||||
|
#
|
||||||
|
# The keys typing.transformations.extra.from and htmlSupport.allow.name allow
|
||||||
|
# to have a special array with a key `pattern` that allows to specify Regular Expressions:
|
||||||
|
# - { from: { pattern: '(typoscript|TYPOScript|typo3script)$' }, to: 'TypoScript' }
|
||||||
|
# see https://docs.typo3.org/c/typo3/cms-core/main/en-us/Changelog/12.4.x/Important-104827-AllowToUseRegularExpressionsInCKEditorYAML.html
|
||||||
|
#
|
||||||
|
# Add configuration for the editor
|
||||||
|
# For complete documentation see https://ckeditor.com/docs/ckeditor5/latest/features/index.html
|
||||||
|
editor:
|
||||||
|
config:
|
||||||
|
toolbar:
|
||||||
|
items:
|
||||||
|
- style
|
||||||
|
- heading
|
||||||
|
# grouping separator
|
||||||
|
- '|'
|
||||||
|
- bold
|
||||||
|
- italic
|
||||||
|
- subscript
|
||||||
|
- superscript
|
||||||
|
- softhyphen
|
||||||
|
- '|'
|
||||||
|
- bulletedList
|
||||||
|
- numberedList
|
||||||
|
- blockQuote
|
||||||
|
- alignment
|
||||||
|
- '|'
|
||||||
|
- findAndReplace
|
||||||
|
- link
|
||||||
|
- '|'
|
||||||
|
- removeFormat
|
||||||
|
- undo
|
||||||
|
- redo
|
||||||
|
- '|'
|
||||||
|
- insertTable
|
||||||
|
- '|'
|
||||||
|
- specialCharacters
|
||||||
|
- horizontalLine
|
||||||
|
- sourceEditing
|
||||||
|
|
||||||
|
heading:
|
||||||
|
options:
|
||||||
|
- { model: 'paragraph', title: 'Paragraph' }
|
||||||
|
- { model: 'heading2', view: 'h2', title: 'Heading 2' }
|
||||||
|
- { model: 'heading3', view: 'h3', title: 'Heading 3' }
|
||||||
|
- { model: 'formatted', view: 'pre', title: 'Pre-Formatted Text' }
|
||||||
|
|
||||||
|
style:
|
||||||
|
definitions:
|
||||||
|
- { name: "Lead", element: "p", classes: ['lead'] }
|
||||||
|
- { name: "Small", element: "small" }
|
||||||
|
- { name: "Muted", element: "span", classes: ['text-muted'] }
|
||||||
|
|
||||||
|
alignment:
|
||||||
|
options:
|
||||||
|
- { name: 'left', className: 'text-start' }
|
||||||
|
- { name: 'center', className: 'text-center' }
|
||||||
|
- { name: 'right', className: 'text-end' }
|
||||||
|
- { name: 'justify', className: 'text-justify' }
|
||||||
|
|
||||||
|
table:
|
||||||
|
defaultHeadings: { rows: 1 }
|
||||||
|
contentToolbar:
|
||||||
|
- tableColumn
|
||||||
|
- tableRow
|
||||||
|
- mergeTableCells
|
||||||
|
- tableProperties
|
||||||
|
- tableCellProperties
|
||||||
|
- toggleTableCaption
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# Add configuration for the editor for any configuration
|
||||||
|
# For complete documentation see http://docs.ckeditor.com/#!/api/CKEDITOR.config
|
||||||
|
editor:
|
||||||
|
config:
|
||||||
|
# the CSS file to be used inside the editor
|
||||||
|
contentsCss:
|
||||||
|
- 'EXT:rte_ckeditor/Resources/Public/Css/contents.css'
|
||||||
|
height: 300
|
||||||
|
width: 'auto'
|
||||||
|
ui:
|
||||||
|
poweredBy:
|
||||||
|
position: 'inside'
|
||||||
|
side: 'right'
|
||||||
|
label: ''
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
# Additional TYPO3 specific configuration comes here.
|
||||||
|
# See https://docs.typo3.org/c/typo3/cms-rte-ckeditor/main/en-us/Configuration/Reference.html
|
||||||
|
# This is unrelated to CKEditor itself, but applies to Link Browser usage:
|
||||||
|
|
||||||
|
# Which fields can be shown with additional attributes for a link? (comma separated list)
|
||||||
|
# - target: Link target (allows to use "_blank", "_self", "_top" or a framename)
|
||||||
|
# - title: Link title attribute (not the link text itself)
|
||||||
|
# - class: Custom CSS class selection
|
||||||
|
# - params: Additional URL query arguments (see "queryParametersSelector" below when used)
|
||||||
|
# - rel: Link relation attribute ("rel", see "relAttribute" below when used)
|
||||||
|
allowedOptions: 'target,title,class,params,rel'
|
||||||
|
|
||||||
|
# Comma-separated list of allowed Link Types
|
||||||
|
# Valid LinkTypes:
|
||||||
|
# - page: Internal TYPO3 page
|
||||||
|
# - url: External URL
|
||||||
|
# - file: TYPO3 file relation
|
||||||
|
# - folder: TYPO3 folder relation
|
||||||
|
# - email: Mail address
|
||||||
|
# - ...: Custom Link Types, when implemented, with their name.
|
||||||
|
allowedTypes: 'page,url,file,folder,telephone,email'
|
||||||
|
|
||||||
|
# Which class definitions and targets are allowed per LinkType (array)
|
||||||
|
# Each array element has attributes:
|
||||||
|
# - "class": Default CSS class to apply to this LinkType
|
||||||
|
# - "type": LinkType as listed above
|
||||||
|
# - "target": Default link target to apply to this LinkType
|
||||||
|
classesAnchor:
|
||||||
|
- { class: "customPageCssClass", type: "page", target: "" }
|
||||||
|
- { class: "customUrlCssClass", type: "url", target: "_blank" }
|
||||||
|
- { class: "customFileCssClass", type: "file", target: "_parent" }
|
||||||
|
- { class: "customFolderCssClass", type: "folder" }
|
||||||
|
- { class: "customTelephoneCssClass", type: "telephone" }
|
||||||
|
- { class: "customEmailCssClass", type: "email" }
|
||||||
|
|
||||||
|
buttons:
|
||||||
|
link:
|
||||||
|
# Global Link Browser options
|
||||||
|
options:
|
||||||
|
# Optional comma-separated list of Link Types to be specifically removed
|
||||||
|
removeItems: 'telephone'
|
||||||
|
# Specifically enable the "rel" attribute entry, needed when set in "allowedOptions"
|
||||||
|
relAttribute:
|
||||||
|
enabled: true
|
||||||
|
# Specifically enable the "params" (URL query arguments) attribute entry, needed when set in "allowedOptions"
|
||||||
|
queryParametersSelector:
|
||||||
|
enabled: true
|
||||||
|
# Optionally disable showing the "target" field (even though when set in "allowedOptions")
|
||||||
|
targetSelector:
|
||||||
|
disabled: false
|
||||||
|
|
||||||
|
# Define general CSS options for the Link Browser
|
||||||
|
# This is needed to perform any of the link classing below!
|
||||||
|
properties:
|
||||||
|
class:
|
||||||
|
# If set to "true", a CSS class must be selected
|
||||||
|
required: false
|
||||||
|
# Available CSS classes
|
||||||
|
allowedClasses: 'globalCss1,globalCss2,customPageCssClass,customUrlCssClass,customFileCssClass,customFolderCssClass,customTelephoneCssClass,customEmailCssClass'
|
||||||
|
|
||||||
|
# Define default CSS classes per LinkType. The CSS class name listed here
|
||||||
|
# must also be part of the "allowedClasses" attribute above.
|
||||||
|
# The attribute "required" is also available for each block.
|
||||||
|
page:
|
||||||
|
properties:
|
||||||
|
class:
|
||||||
|
default: "customPageCssClass"
|
||||||
|
url:
|
||||||
|
properties:
|
||||||
|
class:
|
||||||
|
default: "customUrlCssClass"
|
||||||
|
file:
|
||||||
|
properties:
|
||||||
|
class:
|
||||||
|
default: "customFileCssClass"
|
||||||
|
folder:
|
||||||
|
properties:
|
||||||
|
class:
|
||||||
|
default: "customFolderCssClass"
|
||||||
|
telephone:
|
||||||
|
properties:
|
||||||
|
class:
|
||||||
|
default: "customTelephoneCssClass"
|
||||||
|
email:
|
||||||
|
properties:
|
||||||
|
class:
|
||||||
|
default: "customEmailCssClass"
|
||||||
|
|
||||||
|
# Optionally define labels for all classes listed in buttons.link.properties.class.allowedClasses
|
||||||
|
# Also the style of a CSS class choice can be applied via "value" (deprecated, does not work in all browsers).
|
||||||
|
# Any label missing in this setup just uses the CSS classname as a label, without special formatting.
|
||||||
|
classes:
|
||||||
|
globalCss1:
|
||||||
|
name: Label for globalCss1
|
||||||
|
customPageCssClass:
|
||||||
|
name: Label for customPageCssClass
|
||||||
|
value: "color: red; background-color: blue"
|
||||||
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Register custom plugins for ckeditor
|
||||||
|
editor:
|
||||||
|
config:
|
||||||
|
# Explicitly remove (default or external) plugins
|
||||||
|
# removeImportModules: []
|
||||||
|
|
||||||
|
# load modules for plugins when CKEditor is initialized
|
||||||
|
# see CKEditor plugin API for details
|
||||||
|
importModules:
|
||||||
|
# Plugin for whitespace control like soft hypens and non breaking spaces
|
||||||
|
- { module: '@typo3/rte-ckeditor/plugin/whitespace.js', exports: [ 'Whitespace' ] }
|
||||||
|
- { module: '@typo3/rte-ckeditor/plugin/typo3-link.js', exports: [ 'Typo3Link' ] }
|
||||||
|
|
||||||
|
# Configure global wordCount plugin defaults
|
||||||
|
# Overwrite them in your RTE presets as it is necessary
|
||||||
|
# wordCount:
|
||||||
|
# displayWords: true
|
||||||
|
# displayCharacters: true
|
||||||
|
|
||||||
|
# configuration for external plugins
|
||||||
|
externalPlugins:
|
||||||
|
typo3link: { route: 'rteckeditor_wizard_browse_links' }
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
####
|
||||||
|
# Example of what CKEditor can all bring
|
||||||
|
###
|
||||||
|
|
||||||
|
# Load default processing options
|
||||||
|
imports:
|
||||||
|
- { resource: 'EXT:rte_ckeditor/Configuration/RTE/Processing.yaml' }
|
||||||
|
- { resource: 'EXT:rte_ckeditor/Configuration/RTE/Editor/Base.yaml' }
|
||||||
|
- { resource: 'EXT:rte_ckeditor/Configuration/RTE/Editor/Plugins.yaml' }
|
||||||
|
|
||||||
|
# Add configuration for the editor
|
||||||
|
# For complete documentation see https://ckeditor.com/docs/ckeditor5/latest/features/index.html
|
||||||
|
editor:
|
||||||
|
config:
|
||||||
|
style:
|
||||||
|
definitions:
|
||||||
|
# block level styles
|
||||||
|
- { name: 'Orange title H2', element: 'h2', classes: ['orange'] }
|
||||||
|
- { name: 'Orange title H3', element: 'h3', classes: ['orange'] }
|
||||||
|
- { name: 'Quote / Citation', element: 'blockquote' }
|
||||||
|
- { name: 'Code block', element: 'code' }
|
||||||
|
# Inline styles
|
||||||
|
- { name: 'Yellow marker', element: 'span', classes: ['yellow-marker'] }
|
||||||
|
|
||||||
|
heading:
|
||||||
|
options:
|
||||||
|
- { model: 'paragraph', title: 'Paragraph' }
|
||||||
|
- { model: 'heading2', view: 'h2', title: 'Heading 2' }
|
||||||
|
- { model: 'heading3', view: 'h3', title: 'Heading 3' }
|
||||||
|
- { model: 'formatted', view: 'pre', title: 'Pre-Formatted Text' }
|
||||||
|
|
||||||
|
toolbar:
|
||||||
|
items:
|
||||||
|
- removeFormat
|
||||||
|
- undo
|
||||||
|
- redo
|
||||||
|
# grouping separator
|
||||||
|
- '|'
|
||||||
|
- findAndReplace
|
||||||
|
- link
|
||||||
|
- '|'
|
||||||
|
- insertTable
|
||||||
|
- tableColumn
|
||||||
|
- tableRow
|
||||||
|
- mergeTableCells
|
||||||
|
- TableProperties
|
||||||
|
- TableCellProperties
|
||||||
|
- '|'
|
||||||
|
- selectAll
|
||||||
|
- '|'
|
||||||
|
- sourceEditing
|
||||||
|
- showBlocks
|
||||||
|
- horizontalLine
|
||||||
|
# line break
|
||||||
|
- '-'
|
||||||
|
- bold
|
||||||
|
- italic
|
||||||
|
- underline
|
||||||
|
- strikethrough
|
||||||
|
- subscript
|
||||||
|
- superscript
|
||||||
|
- softhyphen
|
||||||
|
- '|'
|
||||||
|
- bulletedList
|
||||||
|
- numberedList
|
||||||
|
- blockQuote
|
||||||
|
- indent
|
||||||
|
- outdent
|
||||||
|
- alignment
|
||||||
|
- '|'
|
||||||
|
- specialCharacters
|
||||||
|
- '-'
|
||||||
|
- style
|
||||||
|
- heading
|
||||||
|
- fontFamily
|
||||||
|
- fontBackgroundColor
|
||||||
|
- fontColor
|
||||||
|
- fontSize
|
||||||
|
- textPartLanguage
|
||||||
|
- highlight
|
||||||
|
- highlight:greenMarker
|
||||||
|
- '|'
|
||||||
|
- fullscreen
|
||||||
|
|
||||||
|
alignment:
|
||||||
|
options:
|
||||||
|
- { name: 'left', className: 'text-start' }
|
||||||
|
- { name: 'center', className: 'text-center' }
|
||||||
|
- { name: 'right', className: 'text-end' }
|
||||||
|
- { name: 'justify', className: 'text-justify' }
|
||||||
|
|
||||||
|
table:
|
||||||
|
defaultHeadings: { rows: 1 }
|
||||||
|
contentToolbar:
|
||||||
|
- tableColumn
|
||||||
|
- tableRow
|
||||||
|
- mergeTableCells
|
||||||
|
- tableProperties
|
||||||
|
- tableCellProperties
|
||||||
|
- toggleTableCaption
|
||||||
|
|
||||||
|
fontColor:
|
||||||
|
colors:
|
||||||
|
- { label: 'Orange', color: '#ff8700' }
|
||||||
|
- { label: 'Blue', color: '#0080c9' }
|
||||||
|
- { label: 'Green', color: '#209d44' }
|
||||||
|
|
||||||
|
fontBackgroundColor:
|
||||||
|
colors:
|
||||||
|
- { label: 'Stage orange light', color: '#fab85c' }
|
||||||
|
|
||||||
|
fontFamily:
|
||||||
|
options:
|
||||||
|
- 'default'
|
||||||
|
- 'Arial, sans-serif'
|
||||||
|
|
||||||
|
fontSize:
|
||||||
|
options:
|
||||||
|
- 'default'
|
||||||
|
- 18
|
||||||
|
- 21
|
||||||
|
|
||||||
|
list:
|
||||||
|
properties:
|
||||||
|
styles: true
|
||||||
|
startIndex: true
|
||||||
|
reversed: true
|
||||||
|
|
||||||
|
indentBlock:
|
||||||
|
classes:
|
||||||
|
- 'ps-2'
|
||||||
|
- 'ps-3'
|
||||||
|
- 'ps-4'
|
||||||
|
|
||||||
|
language:
|
||||||
|
textPartLanguage: [
|
||||||
|
{ title: 'English', languageCode: 'en' },
|
||||||
|
{ title: 'French', languageCode: 'fr' },
|
||||||
|
{ title: 'German', languageCode: 'de' }
|
||||||
|
]
|
||||||
|
|
||||||
|
highlight:
|
||||||
|
options:
|
||||||
|
- { model: 'yellowMarker', class: 'marker-yellow', title: 'Yellow marker', type: 'marker', color: 'var(--ck-content--highlight-marker-yellow)' }
|
||||||
|
- { model: 'greenMarker', class: 'marker-green', title: 'Green marker', type: 'marker', color: 'var(--ck-content-highlight-marker-green)' }
|
||||||
|
- { model: 'redPen', class: 'pen-red', title: 'Red pen', type: 'pen', color: 'var(--ck-content-highlight-pen-red)' }
|
||||||
|
|
||||||
|
mention:
|
||||||
|
feeds:
|
||||||
|
-
|
||||||
|
marker: '@'
|
||||||
|
feed:
|
||||||
|
- '@TYPO3'
|
||||||
|
minimumCharacters: 1
|
||||||
|
|
||||||
|
importModules:
|
||||||
|
- { module: '@ckeditor/ckeditor5-word-count', exports: ['WordCount'] }
|
||||||
|
# Provides fontFamily, fontSize, fontColor, and fontBackgroundColor toolbar items
|
||||||
|
- { module: '@ckeditor/ckeditor5-font', exports: ['Font'] }
|
||||||
|
# Provides showBlocks toolbar item
|
||||||
|
- { module: '@ckeditor/ckeditor5-show-blocks', exports: ['ShowBlocks'] }
|
||||||
|
# Provides textPartLanguage toolbar item
|
||||||
|
- { module: '@ckeditor/ckeditor5-language', exports: ['TextPartLanguage'] }
|
||||||
|
- { module: '@ckeditor/ckeditor5-mention', exports: ['Mention'] }
|
||||||
|
- { module: '@ckeditor/ckeditor5-highlight', exports: ['Highlight'] }
|
||||||
|
- { module: '@ckeditor/ckeditor5-list', exports: ['ListProperties'] }
|
||||||
|
- { module: '@ckeditor/ckeditor5-fullscreen', exports: [ 'Fullscreen' ] }
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# Load default processing options
|
||||||
|
imports:
|
||||||
|
- { resource: 'EXT:rte_ckeditor/Configuration/RTE/Processing.yaml' }
|
||||||
|
- { resource: 'EXT:rte_ckeditor/Configuration/RTE/Editor/Base.yaml' }
|
||||||
|
|
||||||
|
# Minimal configuration for the editor
|
||||||
|
editor:
|
||||||
|
config:
|
||||||
|
toolbar:
|
||||||
|
items:
|
||||||
|
- bold
|
||||||
|
- italic
|
||||||
|
- '|'
|
||||||
|
- clipboard
|
||||||
|
- undo
|
||||||
|
- redo
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
# ********************************************************
|
||||||
|
# Sets the proc options for all default configurations
|
||||||
|
# https://docs.typo3.org/permalink/t3tsref:rte-config-proc
|
||||||
|
# ********************************************************
|
||||||
|
|
||||||
|
processing:
|
||||||
|
mode: default
|
||||||
|
# Tags that are allowed in the content in general
|
||||||
|
# Note that some tags like <mark> require you to style them via custom CSS directives in your frontend.
|
||||||
|
allowTags:
|
||||||
|
- a
|
||||||
|
- abbr
|
||||||
|
- acronym
|
||||||
|
- address
|
||||||
|
- article
|
||||||
|
- big
|
||||||
|
- blockquote
|
||||||
|
- br
|
||||||
|
- caption
|
||||||
|
- cite
|
||||||
|
- code
|
||||||
|
- col
|
||||||
|
- colgroup
|
||||||
|
- dd
|
||||||
|
- del
|
||||||
|
- dfn
|
||||||
|
- dl
|
||||||
|
- div
|
||||||
|
- dt
|
||||||
|
- em
|
||||||
|
- figure
|
||||||
|
- figcaption
|
||||||
|
- footer
|
||||||
|
- header
|
||||||
|
- h1
|
||||||
|
- h2
|
||||||
|
- h3
|
||||||
|
- h4
|
||||||
|
- h5
|
||||||
|
- h6
|
||||||
|
- hr
|
||||||
|
- i
|
||||||
|
- img
|
||||||
|
- ins
|
||||||
|
- kbd
|
||||||
|
- label
|
||||||
|
- li
|
||||||
|
- mark
|
||||||
|
- nav
|
||||||
|
- ol
|
||||||
|
- p
|
||||||
|
- pre
|
||||||
|
- q
|
||||||
|
- s
|
||||||
|
- samp
|
||||||
|
- section
|
||||||
|
- small
|
||||||
|
- span
|
||||||
|
- strong
|
||||||
|
- sub
|
||||||
|
- sup
|
||||||
|
- table
|
||||||
|
- thead
|
||||||
|
- tbody
|
||||||
|
- tfoot
|
||||||
|
- td
|
||||||
|
- th
|
||||||
|
- tr
|
||||||
|
- tt
|
||||||
|
- u
|
||||||
|
- ul
|
||||||
|
- var
|
||||||
|
|
||||||
|
## Tags that are allowed outside of paragraphs
|
||||||
|
allowTagsOutside: [address, article, aside, blockquote, figure, figcaption, footer, header, hr, mark, nav, section, div]
|
||||||
|
|
||||||
|
## allowed default attributes
|
||||||
|
allowAttributes: [class, id, title, dir, lang, xml:lang, itemscope, itemtype, itemprop]
|
||||||
|
|
||||||
|
## CONTENT TO DATABASE
|
||||||
|
HTMLparser_db:
|
||||||
|
## STRIP ALL ATTRIBUTES FROM THESE TAGS
|
||||||
|
## If this list of tags is not set, it will default to: b,i,u,br,center,hr,sub,sup,strong,em,li,ul,ol,blockquote,strike,mark,s.
|
||||||
|
## However, we want to keep xml:lang attribute on most tags and tags from the default list were cleaned on entry.
|
||||||
|
noAttrib: br
|
||||||
|
# Can be disabled if you trust ckeditor (If Automatic Content Formatting is enabled, this should be OK)
|
||||||
|
# allowTags: %default%
|
||||||
|
denyTags: img
|
||||||
|
tags:
|
||||||
|
hr:
|
||||||
|
allowedAttribs:
|
||||||
|
- class
|
||||||
|
|
||||||
|
## REMOVE OPEN OFFICE META DATA TAGS, WORD 2003 TAGS, LINK, META, STYLE AND TITLE TAGS, AND DEPRECATED HTML TAGS
|
||||||
|
## We use this rule instead of the denyTags rule so that we can protect custom tags without protecting these unwanted tags.
|
||||||
|
removeTags: [link, meta, o:p, sdfield, style, title, font, center]
|
||||||
|
|
||||||
|
## PROTECT CUSTOM TAGS
|
||||||
|
keepNonMatchedTags: protect
|
||||||
|
|
||||||
|
# HTML Sanitizer
|
||||||
|
# `htmlSanitize = false | null` to disable individually
|
||||||
|
htmlSanitize:
|
||||||
|
# either preset name as declared in `$GLOBALS['TYPO3_CONF_VARS']['SYS']['htmlSanitizer']`
|
||||||
|
# or class-name implementing interface `\TYPO3\HtmlSanitizer\Builder\BuilderInterface`
|
||||||
|
build: default
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
services:
|
||||||
|
_defaults:
|
||||||
|
autowire: true
|
||||||
|
autoconfigure: true
|
||||||
|
public: false
|
||||||
|
|
||||||
|
TYPO3\CMS\RteCKEditor\:
|
||||||
|
resource: '../Classes/*'
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
.. include:: /Includes.rst.txt
|
||||||
|
|
||||||
|
|
||||||
|
.. _config-best-practices:
|
||||||
|
|
||||||
|
============================
|
||||||
|
Configuration Best Practices
|
||||||
|
============================
|
||||||
|
|
||||||
|
.. _best-practice-sitepackage:
|
||||||
|
|
||||||
|
Use a Sitepackage extension
|
||||||
|
===========================
|
||||||
|
|
||||||
|
It is generally recommended to use a sitepackage extension to
|
||||||
|
customize a TYPO3 website. The sitepackage contains configuration files
|
||||||
|
for that site.
|
||||||
|
|
||||||
|
See the :doc:`TYPO3 Sitepackage Tutorial <t3sitepackage:Index>` on how
|
||||||
|
to create a sitepackage. We assume here your sitepackage extension has the
|
||||||
|
key `my_sitepackage`.
|
||||||
|
|
||||||
|
The YAML preset files should be kept in folder
|
||||||
|
:file:`EXT:my_sitepackage/Configuration/RTE/`.
|
||||||
|
|
||||||
|
RTE configurations need to be registered in your sitepackages
|
||||||
|
:file:`ext_localconf.php`:
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
:caption: EXT:my_sitepackage/ext_localconf.php
|
||||||
|
|
||||||
|
$GLOBALS['TYPO3_CONF_VARS']['RTE']['Presets']['myconfig']
|
||||||
|
= 'EXT:my_sitepackage/Configuration/RTE/MyConfiguration.yaml';
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
It is possible but not recommended to define this setting in the projects
|
||||||
|
:file:`system/settings.php` or :file:`system/additional.php`
|
||||||
|
|
||||||
|
.. _best-practice-boilerplate:
|
||||||
|
|
||||||
|
Use TYPO3’s Core Default.yaml as boilerplate
|
||||||
|
============================================
|
||||||
|
|
||||||
|
It is recommended to start by copying the file
|
||||||
|
:file:`typo3/sysext/rte_ckeditor/Configuration/RTE/Default.yaml` into your
|
||||||
|
sitepackage to the file
|
||||||
|
:file:`EXT:my_sitepackage/Configuration/RTE/MyConfiguration.yaml`.
|
||||||
|
|
||||||
|
|
||||||
|
Check TYPO3's Core Full.yaml to gain insight into a more extensive configuration
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
This preset shows more configured options and plugins. It is not intended for real use.
|
||||||
|
It acts as an example.
|
||||||
|
|
||||||
|
:file:`typo3/sysext/rte_ckeditor/Configuration/RTE/Full.yaml`
|
||||||
|
|
||||||
|
|
||||||
|
Use Core includes
|
||||||
|
=================
|
||||||
|
|
||||||
|
It is recommended to use the following includes at the top of your custom
|
||||||
|
configuration:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
:caption: EXT:my_sitepackage/Configuration/RTE/MyConfiguration.yaml
|
||||||
|
|
||||||
|
imports:
|
||||||
|
- { resource: "EXT:rte_ckeditor/Configuration/RTE/Processing.yaml" }
|
||||||
|
- { resource: "EXT:rte_ckeditor/Configuration/RTE/Editor/Base.yaml" }
|
||||||
|
- { resource: "EXT:rte_ckeditor/Configuration/RTE/Editor/Plugins.yaml" }
|
||||||
|
|
||||||
|
If you started out by copying this extensions
|
||||||
|
:ref:`Default.yaml as boilerplate <best-practice-boilerplate>` the imports
|
||||||
|
should already be there.
|
||||||
|
|
||||||
|
The include files are already split up so the processing transformations can
|
||||||
|
just be included or even completely disabled (by removing the line for importing).
|
||||||
|
|
||||||
|
.. attention::
|
||||||
|
Please be aware that removing the :file:`Processing.yaml` removes
|
||||||
|
security measures. In that case you have to take care of keeping the ckeditor
|
||||||
|
safe yourself.
|
||||||
@@ -0,0 +1,350 @@
|
|||||||
|
.. include:: /Includes.rst.txt
|
||||||
|
|
||||||
|
.. _config-concepts:
|
||||||
|
|
||||||
|
======================
|
||||||
|
Configuration Concepts
|
||||||
|
======================
|
||||||
|
|
||||||
|
|
||||||
|
Configuration Overview
|
||||||
|
======================
|
||||||
|
|
||||||
|
The main principles of configuring a Rich Text Editor in TYPO3
|
||||||
|
apply to editing with any Rich Text Editor (`rte_ckeditor`, ...).
|
||||||
|
|
||||||
|
Some of the functionality (for example the RTE transformations) is
|
||||||
|
embedded in the TYPO3 core and not specific to `rte_ckeditor`.
|
||||||
|
|
||||||
|
There are three main parts relevant for rich text editing with TYPO3:
|
||||||
|
|
||||||
|
Editor configuration
|
||||||
|
This covers how the actual editor (in this case CKEditor) should behave,
|
||||||
|
what buttons should be shown, what options are available.
|
||||||
|
|
||||||
|
RTE transformations
|
||||||
|
This defines how the information is processed when saved from the Rich Text Editor to the database.
|
||||||
|
And when loaded from the database into the Rich Text Editor.
|
||||||
|
|
||||||
|
Frontend output configuration
|
||||||
|
The information fetched from the database may need to be processed for the frontend.
|
||||||
|
The configuration of the frontend output is configured via TypoScript.
|
||||||
|
|
||||||
|
.. todo: diagram: overview with DB <-> RTE, DB -> FE etc.
|
||||||
|
|
||||||
|
This section mainly covers editor configuration and RTE transformations, as for
|
||||||
|
TypoScript the TypoScript reference handles output of HTML content and
|
||||||
|
has everything preset (see :ref:`t3tsref:parsefunc`).
|
||||||
|
|
||||||
|
|
||||||
|
.. tip::
|
||||||
|
Before you start, have a look at the :ref:`config-best-practices`.
|
||||||
|
|
||||||
|
|
||||||
|
.. _config-editor:
|
||||||
|
|
||||||
|
Editor Configuration
|
||||||
|
====================
|
||||||
|
|
||||||
|
YAML
|
||||||
|
----
|
||||||
|
|
||||||
|
TYPO3 is using a custom :ref:`YAML API <t3coreapi:yaml-api>` for handling YAML
|
||||||
|
in TYPO3 based on the Symfony YAML package. Therefore environment variables
|
||||||
|
can be used.
|
||||||
|
|
||||||
|
YAML Basics
|
||||||
|
~~~~~~~~~~~
|
||||||
|
|
||||||
|
* YAML is case sensitive
|
||||||
|
* Indenting level reflects hierarchy level and indenting must be used consistently
|
||||||
|
(indent with 2 spaces in `rte_ckeditor` configuration).
|
||||||
|
* Comments begin with a `#`.
|
||||||
|
* White space is important, use a space after `:`.
|
||||||
|
|
||||||
|
This is a dictionary (associative array):
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
key1: value
|
||||||
|
key2: value
|
||||||
|
|
||||||
|
A dictionary can be nested, for example:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
key1:
|
||||||
|
key1-2: value
|
||||||
|
|
||||||
|
This is a list:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
- list item 1
|
||||||
|
- list item 2
|
||||||
|
|
||||||
|
A dictionary can be combined with a list:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
key:
|
||||||
|
key2:
|
||||||
|
- item 1
|
||||||
|
- item 2
|
||||||
|
|
||||||
|
|
||||||
|
.. _configuration-presets:
|
||||||
|
|
||||||
|
Configuration Presets
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Presets are the heart of having custom configuration per record type, or
|
||||||
|
page area. A preset consists of a name and a reference to the location
|
||||||
|
of a YAML file.
|
||||||
|
|
||||||
|
TYPO3 ships with three RTE presets, “default”, “minimal” and “full”. The
|
||||||
|
"default" configuration is active by default.
|
||||||
|
|
||||||
|
It is possible for extensions to ship their own preset like “news”, or “site_xyz”.
|
||||||
|
|
||||||
|
Registration of a preset happens within :file:`system/config.php`,
|
||||||
|
:file:`system/additional.php` or within
|
||||||
|
:file:`ext_localconf.php` of an extension:
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
$GLOBALS['TYPO3_CONF_VARS']['RTE']['Presets']['default']
|
||||||
|
= 'EXT:rte_ckeditor/Configuration/RTE/Default.yaml';
|
||||||
|
|
||||||
|
This way, it is possible to override the default preset, for example by using
|
||||||
|
the configuration defined in a custom extension:
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
$GLOBALS['TYPO3_CONF_VARS']['RTE']['Presets']['default']
|
||||||
|
= 'EXT:my_extension/Configuration/RTE/Default.yaml';
|
||||||
|
|
||||||
|
|
||||||
|
TYPO3 uses the “default” preset for all Rich-Text-Element fields. To use
|
||||||
|
a different preset throughout an installation or a branch of the website,
|
||||||
|
see :ref:`override-configuration-via-page-tsconfig`.
|
||||||
|
|
||||||
|
Selecting a specific preset for bullet lists can be done via TCA
|
||||||
|
configuration of a field. The following example shows the TCA configuration
|
||||||
|
for the sys_news database table, which can be found in
|
||||||
|
:file:`EXT:core/Configuration/TCA/sys_news.php`.
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
'content' => [
|
||||||
|
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.text',
|
||||||
|
'config' => [
|
||||||
|
'type' => 'text',
|
||||||
|
'cols' => 48,
|
||||||
|
'rows' => 5,
|
||||||
|
'enableRichtext' => true,
|
||||||
|
'richtextConfiguration' => 'default',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
Enabling Rich Text Parsing itself is done via :ref:`t3tca:confval-text-enablerichtext`,
|
||||||
|
and a specific configuration
|
||||||
|
can be set via :ref:`t3tca:confval-text-richtextConfiguration`, setting it to for example
|
||||||
|
“news”.
|
||||||
|
|
||||||
|
.. _override-configuration-via-page-tsconfig:
|
||||||
|
|
||||||
|
Overriding Configuration via page TSconfig
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
Instead of overriding all TCA fields to use a custom preset, it is possible
|
||||||
|
to override this information via page TSconfig.
|
||||||
|
|
||||||
|
The option :typoscript:`RTE.default.preset = news` can also be set on a per-field
|
||||||
|
and per-type basis:
|
||||||
|
|
||||||
|
.. code-block:: tsconfig
|
||||||
|
:caption: EXT:my_sitepackage/Configuration/page.tsconfig
|
||||||
|
:linenos:
|
||||||
|
|
||||||
|
# per-field
|
||||||
|
RTE.config.tt_content.bodytext.preset = minimal
|
||||||
|
|
||||||
|
# per-type
|
||||||
|
RTE.config.tt_content.bodytext.types.bullets.preset = bullets
|
||||||
|
|
||||||
|
line #2
|
||||||
|
This sets the "minimal" preset for all bodytext fields of content elements.
|
||||||
|
|
||||||
|
line #4
|
||||||
|
This sets the "bullets" preset for all bodytext fields of content elements,
|
||||||
|
with Content Type “Bullet list” (CType=bullets).
|
||||||
|
|
||||||
|
Of course, any other specific option set via YAML can be overridden via Page TSconfig as well:
|
||||||
|
|
||||||
|
Specific options set via YAML can be overridden via page TSconfig as well - but
|
||||||
|
be aware that boolean values can not be set, and arrays are not merged but
|
||||||
|
overridden.
|
||||||
|
|
||||||
|
.. code-block:: tsconfig
|
||||||
|
:caption: EXT:my_sitepackage/Configuration/page.tsconfig
|
||||||
|
|
||||||
|
# Restrict format_tags to h2 in bodytext field of content elements
|
||||||
|
RTE.config.tt_content.bodytext.editor.config.format_tags = h2
|
||||||
|
|
||||||
|
The loading order for configuration is:
|
||||||
|
|
||||||
|
#. ``preset`` defined for a specific field via PageTS
|
||||||
|
#. ``richtextConfiguration`` defined for a specific field via TCA
|
||||||
|
#. general preset defined via page TSconfig
|
||||||
|
#. ``default``
|
||||||
|
|
||||||
|
|
||||||
|
For more examples, see :ref:`t3tsref:pageTsRte` in "TSconfig Reference".
|
||||||
|
|
||||||
|
|
||||||
|
.. _config-rte-transformations:
|
||||||
|
|
||||||
|
RTE Transformations
|
||||||
|
===================
|
||||||
|
|
||||||
|
Transformations are directives for parsing HTML markup. They are executed by the
|
||||||
|
TYPO3 Core every time a RTE-based field is saved to the TYPO3 database or fetched
|
||||||
|
from the database for the Rich Text Editor to render. This way, there are always
|
||||||
|
two ways / two transformations applied.
|
||||||
|
|
||||||
|
There are several advantages for transformations, the most prominent reason is to
|
||||||
|
not inject bad HTML code into the database which in turn would be used for output.
|
||||||
|
Transformations from the RTE towards the database can filter out HTML tags or attributes.
|
||||||
|
|
||||||
|
You can read more about
|
||||||
|
:ref:`RTE Transformations in TYPO3 Explained <t3coreapi:transformations-introduction>`.
|
||||||
|
|
||||||
|
.. todo: diagram rte -> DB -> RTE
|
||||||
|
|
||||||
|
A Brief Dive Into History
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
Back in the very old days of TYPO3, there was an RTE which only worked inside Microsoft
|
||||||
|
Internet Explorer 4 (within the system extension “`rte`”). All other editors of TYPO3 had
|
||||||
|
to write HTML by hand, which was very complicated with all the table-based layouts available.
|
||||||
|
Links were not set with a :html:`<a>` tag, but with a so-called :html:`<typolink 23,13 _blank>`
|
||||||
|
tag. Further tags were :html:`<typolist>` and :html:`<typohead>`, which were stored in the database
|
||||||
|
1:1. Since RTEs did not understand these special tags, they had to transform these special tags into
|
||||||
|
valid HTML tags. Additionally, TYPO3 did not store regular :html:`<p>` or :html:`<div>` tags but
|
||||||
|
treated every line without a surrounding HTML block element as :html:`<p>` tag. The frontend rendering
|
||||||
|
then added `<p>` tags for each line when parsing (see below).
|
||||||
|
|
||||||
|
Transformations were later used to allow :html:`<em>`/:html:`<strong>` tags instead of :html:`<b>`/:html:`<i>`
|
||||||
|
tags, while staying backwards-compatible.
|
||||||
|
|
||||||
|
A lot of transformation options have been dropped for TYPO3 v8, and the default configuration
|
||||||
|
for these transformations acts as a solid base. CKEditor itself includes features that work as
|
||||||
|
another security layer for disallowing injecting of certain HTML tags in the database.
|
||||||
|
|
||||||
|
For TYPO3 v8, the :html:`<typolink>` tag was migrated to proper :html:`<a>` tags with a special
|
||||||
|
:html:`<a href="t3://page?id=23">` syntax when linking to pages to ensure HTML valid output.
|
||||||
|
Additionally, all records that are edited and stored to the database now contain proper
|
||||||
|
<p> tags, and transformations for paragraph tags are only applied when not set yet.
|
||||||
|
|
||||||
|
Transformations for invalid links and images (still available in HtmlArea) are still in place.
|
||||||
|
|
||||||
|
Most logic related to transformations can be found within :php:`TYPO3\CMS\Core\Html\RteHtmlParser`.
|
||||||
|
|
||||||
|
|
||||||
|
.. _transformations-vs-acf:
|
||||||
|
|
||||||
|
Transformations vs. CKEditor’s Advanced Content Filter
|
||||||
|
------------------------------------------------------
|
||||||
|
|
||||||
|
TYPO3’s HtmlParser transformations were used to transform readable semi-HTML
|
||||||
|
code to a full-blown HTML rendering ready for the RTE and vice versa. Since
|
||||||
|
TYPO3 v8, magically adding :html:`<p>` tags or transforming :html:`<typolink>`
|
||||||
|
tags is not necessary anymore, which leaves transformations almost obsolete.
|
||||||
|
|
||||||
|
However, they can act as an extra fallback layer of security to filter out
|
||||||
|
disallowed tags when saving. TYPO3 v8 configuration ships with a generic
|
||||||
|
transformation configuration, which is mainly based on legacy functionality
|
||||||
|
shipped with TYPO3 nowadays.
|
||||||
|
|
||||||
|
However, CKEditor comes with a separate strategy of allowing which HTML tags
|
||||||
|
and attributes are allowed, and can be configured on an editor-level.
|
||||||
|
This configuration option is called “allowedContent”, the feature itself is
|
||||||
|
named `Advanced Content Filter <http://docs.ckeditor.com/#!/guide/dev_advanced_content_filter>`__
|
||||||
|
(ACF).
|
||||||
|
|
||||||
|
Activating CKEditor’s table plugin allows to add :html:`<table>`, :html:`<tr>`
|
||||||
|
tags etc. Enabling the link picker enables the usage of :html:`<a>` tags. CKEditor
|
||||||
|
cleans content right away which was e.g. copy-pasted from MS Word and does not
|
||||||
|
match the allowed tags.
|
||||||
|
|
||||||
|
|
||||||
|
.. _config-frontend:
|
||||||
|
|
||||||
|
Frontend Output Configuration
|
||||||
|
=============================
|
||||||
|
|
||||||
|
Mostly due to historical reasons, the frontend output added :html:`<p>` tags to each
|
||||||
|
line which is not wrapped in HTML. Additionally the :html:`<typolink>` tag was replaced
|
||||||
|
by :html:`<a>` tags and checked if e.g. if a link was set to a specific page within
|
||||||
|
TYPO3 is actually accessible for this specific visitor.
|
||||||
|
|
||||||
|
The latter part is still necessary, so the :html:`<a href="t3://page?id23">` HTML snippet
|
||||||
|
is replaced by a speaking URL which the power of typolink will still take care of.
|
||||||
|
There are, of course, more options to it, like default “target” attributes for
|
||||||
|
external links or spam-protecting links to email addresses, which all happens within the
|
||||||
|
typolink logic, the master for generating a link in the TYPO3 Frontend rendering process.
|
||||||
|
|
||||||
|
.. todo: [DIAGRAM DB => FE]
|
||||||
|
|
||||||
|
|
||||||
|
TypoScript
|
||||||
|
----------
|
||||||
|
|
||||||
|
As with every content that is rendered via TYPO3, this processing for the frontend
|
||||||
|
output of Rich-Text-Editing fields is done via TypoScript, more specifically within
|
||||||
|
the stdWrap property :ref:`t3tsref:parsefunc`. With Fluid Styled Content and CSS Styled
|
||||||
|
Content comes :typoscript:`lib.parseFunc` and :typoscript:`lib.parseFunc_RTE` which add
|
||||||
|
support for parsing :html:`<a>` and :html:`<link>` tags and dumping them into the typolink
|
||||||
|
functionality. The shipped TypoScript code looks like this:
|
||||||
|
|
||||||
|
.. code-block:: typoscript
|
||||||
|
|
||||||
|
lib.parseFunc.tags {
|
||||||
|
a = TEXT
|
||||||
|
a {
|
||||||
|
current = 1
|
||||||
|
typolink {
|
||||||
|
parameter.data = parameters:href
|
||||||
|
title.data = parameters:title
|
||||||
|
ATagParams.data = parameters:allParams
|
||||||
|
target.data = parameters:target
|
||||||
|
extTarget = {$styles.content.links.extTarget}
|
||||||
|
extTarget.override.data = parameters:target
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
If you already use Fluid Styled Content and CSS Styled Content and
|
||||||
|
you haven’t touched that area of TypoScript yet, you’re good to go
|
||||||
|
by including the TypoScript file.
|
||||||
|
|
||||||
|
Fluid
|
||||||
|
-----
|
||||||
|
|
||||||
|
Outputting the contents of a RTE-enabled database field within Fluid can
|
||||||
|
be achieved by adding :html:`{record.myfield -> f:format.html()}`
|
||||||
|
which in turn calls :typoscript:`stdWrap.parseFunc` with :typoscript:`lib.parseFunc_RTE`
|
||||||
|
thus applying the same logic. Just ensure that the :typoscript:`lib.parseFunc_RTE`
|
||||||
|
functionality is available.
|
||||||
|
|
||||||
|
You can check if this TypoScript snippet is loaded by using
|
||||||
|
:guilabel:`Sites > TypoScript` and use the TypoScript Tree (Setup)
|
||||||
|
to see if :typoscript:`lib.parseFunc_RTE` is filled.
|
||||||
|
|
||||||
|
.. todo: [SCREENSHOT of TSOB having lib.parseFunc_RTE open]
|
||||||
|
|
||||||
|
.. important::
|
||||||
|
Take care of where you add opening and closing tags, if you don't use the fluid inline notation.
|
||||||
|
If they are on an own line, the rendered output includes empty paragraphs at beginning and end.
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
.. include:: /Includes.rst.txt
|
||||||
|
.. highlight:: typoscript
|
||||||
|
|
||||||
|
.. _config-typo3:
|
||||||
|
|
||||||
|
==========================
|
||||||
|
TYPO3 Configuration Basics
|
||||||
|
==========================
|
||||||
|
|
||||||
|
Just in case you are not familiar with how to configure TYPO3, we will
|
||||||
|
give you a very brief introduction. Otherwise, you can safely
|
||||||
|
skip this part and continue reading
|
||||||
|
:ref:`config-concepts`.
|
||||||
|
|
||||||
|
We only cover configuration methods that are used to configure `rte_ckeditor`.
|
||||||
|
|
||||||
|
.. _config-typo3-page-tsconfig:
|
||||||
|
|
||||||
|
Page TSconfig
|
||||||
|
=============
|
||||||
|
|
||||||
|
We recommend you to put all configurations for the preset in the
|
||||||
|
:ref:`YAML <config-typo3-yaml>` configuration. However, it is still possible to
|
||||||
|
override these settings through the page TSconfig.
|
||||||
|
|
||||||
|
You can find a list of configuration properties in the :ref:`Page TSconfig
|
||||||
|
reference, chapter RTE <t3tsref:pageTsRte>`.
|
||||||
|
|
||||||
|
Relevant Settings for `rte_ckeditor`
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
Page TSconfig can be used to change:
|
||||||
|
|
||||||
|
#. Default preset:
|
||||||
|
|
||||||
|
.. code-block:: tsconfig
|
||||||
|
|
||||||
|
RTE.default.preset = full
|
||||||
|
|
||||||
|
#. Override for one field (:typoscript:`RTE.config.[tableName].[fieldName].preset`):
|
||||||
|
|
||||||
|
.. code-block:: tsconfig
|
||||||
|
|
||||||
|
RTE.config.tt_content.bodytext.preset = myCustomPreset
|
||||||
|
RTE.config.tx_news_domain_model_news.bodytext.preset = minimal
|
||||||
|
|
||||||
|
#. Override for one field defined in flexform (:typoscript:`RTE.config.[tableName].[flexForm\.field\.name].preset`):
|
||||||
|
|
||||||
|
.. code-block:: tsconfig
|
||||||
|
|
||||||
|
RTE.config.tt_content.settings\.notifications\.emailText.preset = myCustomPreset
|
||||||
|
|
||||||
|
#. Override for one field, if type matches (:typoscript:`RTE.config.[tableName].[fieldName].types.[type].preset`):
|
||||||
|
|
||||||
|
.. code-block:: tsconfig
|
||||||
|
|
||||||
|
RTE.config.tt_content.bodytext.types.textmedia.preset = minimal
|
||||||
|
|
||||||
|
|
||||||
|
How to change values
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
See the :ref:`Page TSconfig reference,
|
||||||
|
chapter Setting Page TSconfig <t3tsref:setting-page-tsconfig>`. This chapter
|
||||||
|
also explains how to verify the settings.
|
||||||
|
|
||||||
|
.. _config-typo3-global-configuration:
|
||||||
|
|
||||||
|
Global Configuration
|
||||||
|
====================
|
||||||
|
|
||||||
|
Global Configuration is a system-wide general configuration.
|
||||||
|
|
||||||
|
Relevant Settings for `rte_ckeditor`
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
The setting :php:`$GLOBALS['TYPO3_CONF_VARS']['RTE']['Presets']` is used to configure
|
||||||
|
the available presets for rich text editing.
|
||||||
|
|
||||||
|
By default, the presets "minimal", "default" and "full" are defined.
|
||||||
|
|
||||||
|
If you add a new preset, you must add it to this array.
|
||||||
|
|
||||||
|
|
||||||
|
How to change values
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Usually, Global Configuration can be configured in the backend in
|
||||||
|
:guilabel:`System > Settings > Configure Installation-Wide Options`.
|
||||||
|
|
||||||
|
However, the settings relevant for rich text editing, :php:`$GLOBALS['TYPO3_CONF_VARS']['RTE']['Presets']`
|
||||||
|
cannot be configured in the backend.
|
||||||
|
|
||||||
|
You must either configure this in:
|
||||||
|
|
||||||
|
#. The file :file:`%config-dir%/system/additional.php`
|
||||||
|
#. Or in an extension in the file :file:`EXT:<extkey>/ext_localconf.php`
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
if (empty($GLOBALS['TYPO3_CONF_VARS']['RTE']['Presets']['myCustomPreset'])) {
|
||||||
|
$GLOBALS['TYPO3_CONF_VARS']['RTE']['Presets']['myCustomPreset']
|
||||||
|
= 'EXT:<extkey>/Configuration/RTE/MyCustomPreset.yaml';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
How to view settings
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
You can view the Global Configuration in
|
||||||
|
:guilabel:`System > Configuration > $GLOBAL['TYPO3_CONF_VARS'] (Global Configuration) > RTE`.
|
||||||
|
|
||||||
|
.. figure:: images/global-configuration-rte.png
|
||||||
|
:class: with-shadow
|
||||||
|
|
||||||
|
Global Configuration: RTE > Presets
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.. _config-typo3-yaml:
|
||||||
|
|
||||||
|
YAML
|
||||||
|
====
|
||||||
|
|
||||||
|
Most of the configuration of `rte_ckeditor` will be done in a YAML file.
|
||||||
|
|
||||||
|
|
||||||
|
Relevant Settings for `rte_ckeditor`
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
See :ref:`config-ref`
|
||||||
|
|
||||||
|
How to change values
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
This is done directly in the file. The YAML file should be included in a
|
||||||
|
sitepackage extension, see :ref:`best-practice-sitepackage`.
|
||||||
|
|
||||||
|
|
||||||
|
.. _config-typo3-tca:
|
||||||
|
|
||||||
|
CKEditor related TCA configuration
|
||||||
|
==================================
|
||||||
|
|
||||||
|
The :abbr:`table configuration array (TCA)` is used to configure database fields and how they will behave in the
|
||||||
|
backend when edited. It is for example used to define that ``tt_content.bodytext`` should be edited
|
||||||
|
with a rich text editor.
|
||||||
|
|
||||||
|
|
||||||
|
Relevant Settings for `rte_ckeditor`
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
* :ref:`t3tca:confval-text-enablerichtext`
|
||||||
|
* :ref:`t3tca:confval-text-richtextConfiguration`
|
||||||
|
|
||||||
|
How to change values
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
This must be done in an extension in :file:`Configuration/TCA`. Usually this is done within a custom sitepackage
|
||||||
|
extension, see :ref:`best-practice-sitepackage`.
|
||||||
|
|
||||||
|
How to view settings
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
You can view TCA in the backend:
|
||||||
|
:guilabel:`System > Configuration > $GLOBAL['TCA'] (Table configuration array)`.
|
||||||
|
|
||||||
|
For example, look at :guilabel:`tt_content > columns > bodytext`.
|
||||||
|
|
||||||
|
However, you will
|
||||||
|
find that neither `enableRichtext`, nor `richtextConfiguration` is set here. They
|
||||||
|
are configured in :guilabel:`tt_content > types` for various content types, for example
|
||||||
|
look at :guilabel:`tt_content > types > text > columnsOverrides`.
|
||||||
|
|
||||||
|
.. figure:: images/column_overrides.png
|
||||||
|
:class: with-shadow
|
||||||
|
|
||||||
|
TCA: tt_content > types > text > columnsOverrides > bodytext
|
||||||
@@ -0,0 +1,256 @@
|
|||||||
|
.. include:: /Includes.rst.txt
|
||||||
|
|
||||||
|
.. _config-examples:
|
||||||
|
|
||||||
|
======================
|
||||||
|
Configuration Examples
|
||||||
|
======================
|
||||||
|
|
||||||
|
How do I use a different preset?
|
||||||
|
================================
|
||||||
|
|
||||||
|
Instead of using the default "default" preset, you can change this, for example
|
||||||
|
to "full", using **page TSconfig**:
|
||||||
|
|
||||||
|
.. code-block:: tsconfig
|
||||||
|
:caption: EXT:my_sitepackage/Configuration/page.tsconfig
|
||||||
|
|
||||||
|
RTE.default.preset = full
|
||||||
|
|
||||||
|
Of course, the preset must already exist, or you must define it. `rte_ckeditor`
|
||||||
|
ships with presets "minimal", "default" and "full".
|
||||||
|
|
||||||
|
Additionally, you can set specific presets for specific types of textfields.
|
||||||
|
|
||||||
|
For example to use preset "full" for the field "bodytext" of all content elements:
|
||||||
|
|
||||||
|
.. code-block:: tsconfig
|
||||||
|
:caption: EXT:my_sitepackage/Configuration/page.tsconfig
|
||||||
|
|
||||||
|
RTE.config.tt_content.bodytext.preset = full
|
||||||
|
|
||||||
|
To use preset "minimal" for the field "bodytext" of only content elements
|
||||||
|
with ctype="text":
|
||||||
|
|
||||||
|
.. code-block:: tsconfig
|
||||||
|
:caption: EXT:my_sitepackage/Configuration/page.tsconfig
|
||||||
|
|
||||||
|
RTE.config.tt_content.bodytext.types.text.preset = minimal
|
||||||
|
|
||||||
|
For more examples, see :ref:`t3tsref:pageTsRte` in "TSconfig Reference".
|
||||||
|
|
||||||
|
|
||||||
|
How do I create my own preset?
|
||||||
|
==============================
|
||||||
|
|
||||||
|
In your sitepackage extension:
|
||||||
|
|
||||||
|
In :file:`ext_localconf.php`, replace `my_extension` with your extension key, replace `my_preset` and `MyPreset.yaml`
|
||||||
|
with the name of your preset.
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
:caption: EXT:my_sitepackage/ext_localconf.php
|
||||||
|
|
||||||
|
$GLOBALS['TYPO3_CONF_VARS']['RTE']['Presets']['my_preset']
|
||||||
|
= 'EXT:my_extension/Configuration/RTE/MyPreset.yaml';
|
||||||
|
|
||||||
|
In :file:`Configuration/RTE/MyPreset.yaml`, create your configuration, for example:
|
||||||
|
|
||||||
|
.. literalinclude:: _Examples/_MyPreset.yaml
|
||||||
|
:language: yaml
|
||||||
|
:caption: EXT:my_sitepackage/Configuration/RTE/MyPreset.yaml
|
||||||
|
|
||||||
|
See also the note for :option:`editor.config.contentsCss`.
|
||||||
|
|
||||||
|
How do I customize the toolbar?
|
||||||
|
===============================
|
||||||
|
|
||||||
|
The toolbar can be customized individually by configuring required toolbar
|
||||||
|
items in the YAML configuration. The following configuration shows the toolbar
|
||||||
|
configuration of the minimal editor setup included in file
|
||||||
|
:file:`EXT:rte_ckeditor/Configuration/RTE/Minimal.yaml`:
|
||||||
|
|
||||||
|
.. literalinclude:: _Examples/_CustomizeToolbar.yaml
|
||||||
|
:language: yaml
|
||||||
|
:caption: EXT:my_sitepackage/Configuration/RTE/MyPreset.yaml
|
||||||
|
|
||||||
|
The :yaml:`'|'` can be used as a separator between groups of toolbar items.
|
||||||
|
|
||||||
|
Additional configuration options are available in the official CKEditor 5
|
||||||
|
`Toolbar documentation <https://ckeditor.com/docs/ckeditor5/latest/features/toolbar/toolbar.html>`__
|
||||||
|
|
||||||
|
.. _config-example-toolbargrouping:
|
||||||
|
|
||||||
|
Grouping toolbar items in drop-downs
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
To save space in the toolbar or to arrange the features thematically, it is
|
||||||
|
possible to group several items into a dropdown as shown in the following
|
||||||
|
example:
|
||||||
|
|
||||||
|
.. literalinclude:: _Examples/_GroupingToolbarItems.yaml
|
||||||
|
:language: yaml
|
||||||
|
:caption: EXT:my_sitepackage/Configuration/RTE/MyPreset.yaml
|
||||||
|
|
||||||
|
|
||||||
|
How do I allow a specific tag?
|
||||||
|
==============================
|
||||||
|
|
||||||
|
Allowed content in CKEditor 5 is to be configured via the General HTML Support
|
||||||
|
plugin option :yaml:`config.htmlSupport`.
|
||||||
|
|
||||||
|
.. literalinclude:: _Examples/_AllowSpecificTag.yaml
|
||||||
|
:language: yaml
|
||||||
|
:caption: EXT:my_sitepackage/Configuration/RTE/MyPreset.yaml
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
:yaml:`config.htmlSupport` only applies to elements that are "known" to
|
||||||
|
CKEditor 5. Tags like :html:`<svg>` or custom elements like
|
||||||
|
:html:`<my-element>` are not configurable this way as
|
||||||
|
:yaml:`htmlSupport.allow` can only handle
|
||||||
|
elements that are defined in the `CKEditor 5 schema`_.
|
||||||
|
|
||||||
|
.. _CKEditor 5 schema: https://ckeditor.com/docs/ckeditor5/latest/features/html/general-html-support.html#enabling-custom-elements
|
||||||
|
|
||||||
|
|
||||||
|
.. _config-example-fontplugin:
|
||||||
|
|
||||||
|
How do I configure the font plugin?
|
||||||
|
===================================
|
||||||
|
|
||||||
|
.. versionadded:: 12.4.12
|
||||||
|
|
||||||
|
In order to use the font plugin, the RTE configuration needs to be adapted:
|
||||||
|
|
||||||
|
.. literalinclude:: _Examples/_FontPlugin.yaml
|
||||||
|
:language: yaml
|
||||||
|
:caption: EXT:my_sitepackage/Configuration/RTE/MyPreset.yaml
|
||||||
|
|
||||||
|
More information can be found in the
|
||||||
|
`official documentation of CKEditor <https://ckeditor.com/docs/ckeditor5/latest/features/font.html>`__.
|
||||||
|
|
||||||
|
How do I enable the fullscreen plugin?
|
||||||
|
======================================
|
||||||
|
|
||||||
|
.. versionadded:: 13.4.16
|
||||||
|
|
||||||
|
In order to use the fullscreen plugin, the RTE configuration needs to be adapted:
|
||||||
|
|
||||||
|
.. literalinclude:: _Examples/_FullscreenPlugin.yaml
|
||||||
|
:language: yaml
|
||||||
|
:caption: EXT:my_sitepackage/Configuration/RTE/MyPreset.yaml
|
||||||
|
|
||||||
|
More information can be found in the
|
||||||
|
`official documentation of CKEditor <https://ckeditor.com/docs/ckeditor5/latest/features/fullscreen.html>`__.
|
||||||
|
|
||||||
|
.. _config-example-customplugin:
|
||||||
|
|
||||||
|
How do I configure the Link Browser?
|
||||||
|
====================================
|
||||||
|
|
||||||
|
The TYPO3 Link Browser can be utilized in both the RTE and for FormEngine TCA fields. The latter
|
||||||
|
is configured through `TCA` settings, and the RTE editor itself is configured via the central
|
||||||
|
YAML file.
|
||||||
|
|
||||||
|
There are several configuration options available. Please see :ref:`config-linkbrowser` for
|
||||||
|
the detailed reference, and :t3src:`rte_ckeditor/Configuration/RTE/Editor/LinkBrowser.yaml`
|
||||||
|
for an example configuration.
|
||||||
|
|
||||||
|
How do I create a custom plugin?
|
||||||
|
================================
|
||||||
|
|
||||||
|
With CKEditor 5 the plugin architecture has changed and CKEditor 4 plugins
|
||||||
|
are not compatible with CKEditor 5. It is advised to read the
|
||||||
|
`CKEditor 4 to 5 migration <https://ckeditor.com/docs/ckeditor5/latest/installation/getting-started/migration-from-ckeditor-4.html#plugins>`__
|
||||||
|
to understand the conceptual changes, also related to plugins.
|
||||||
|
|
||||||
|
Writing a custom plugin for CKEditor 5 can be done in TypeScript or JavaScript,
|
||||||
|
using the `CKEditor 5 plugin system <https://ckeditor.com/docs/ckeditor5/latest/installation/advanced/plugins.html>`__.
|
||||||
|
|
||||||
|
In this example, we integrate a simple timestamp plugin to CKEditor 5.
|
||||||
|
Make sure to replace `<my_extension>` with your extension key.
|
||||||
|
|
||||||
|
.. rst-class:: bignums
|
||||||
|
|
||||||
|
1. Create the plugin file
|
||||||
|
|
||||||
|
Add the following ES6 JavaScript code:
|
||||||
|
|
||||||
|
.. code-block:: javascript
|
||||||
|
:caption: EXT:<my_extension>/Resources/Public/JavaScript/Ckeditor/timestamp-plugin.js
|
||||||
|
|
||||||
|
import { Plugin } from '@ckeditor/ckeditor5-core';
|
||||||
|
import { ButtonView } from '@ckeditor/ckeditor5-ui';
|
||||||
|
|
||||||
|
export class Timestamp extends Plugin {
|
||||||
|
static pluginName = 'Timestamp';
|
||||||
|
|
||||||
|
init() {
|
||||||
|
const editor = this.editor;
|
||||||
|
|
||||||
|
// The button must be registered among the UI components of the editor
|
||||||
|
// to be displayed in the toolbar.
|
||||||
|
editor.ui.componentFactory.add(Timestamp.pluginName, () => {
|
||||||
|
// The button will be an instance of ButtonView.
|
||||||
|
const button = new ButtonView();
|
||||||
|
|
||||||
|
button.set({
|
||||||
|
label: 'Timestamp',
|
||||||
|
withText: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// Execute a callback function when the button is clicked
|
||||||
|
button.on('execute', () => {
|
||||||
|
const now = new Date();
|
||||||
|
|
||||||
|
// Change the model using the model writer
|
||||||
|
editor.model.change(writer => {
|
||||||
|
|
||||||
|
// Insert the text at the user's current position
|
||||||
|
editor.model.insertContent(writer.createText(now.toString()));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return button;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
2. Register the ES6 JavaScript
|
||||||
|
|
||||||
|
.. literalinclude:: _Examples/_timestamp-plugin_JavaScriptModules.php
|
||||||
|
:language: php
|
||||||
|
:caption: EXT:<my_extension>/Configuration/JavaScriptModules.php
|
||||||
|
|
||||||
|
3. Include the plugin in the CKEditor configuration
|
||||||
|
|
||||||
|
.. literalinclude:: _Examples/_timestamp-plugin.yaml
|
||||||
|
:language: yaml
|
||||||
|
:caption: EXT:<my_extension>/Configuration/RTE/MyPreset.yaml
|
||||||
|
:emphasize-lines: 4,14
|
||||||
|
:linenos:
|
||||||
|
|
||||||
|
The :yaml:`importModules` item in line 4 imports the previously registered ES6
|
||||||
|
module. The :yaml:`timestamp` item in line 14 adds the plugin to the toolbar.
|
||||||
|
|
||||||
|
4. Use the plugin
|
||||||
|
|
||||||
|
.. figure:: images/timestamp-plugin.png
|
||||||
|
:class: with-shadow
|
||||||
|
:alt: The custom timestamp plugin in the editor
|
||||||
|
|
||||||
|
The custom timestamp plugin in the editor
|
||||||
|
|
||||||
|
.. -------------------------------------
|
||||||
|
.. todo: additional questions
|
||||||
|
What are stylesets?
|
||||||
|
Some configuration can be done with Page TSconfig, some with TCA and some with YAML and some with either 2 or more of these. Why and what should be configured where?
|
||||||
|
How can I configure classes to anchor tags?
|
||||||
|
What is the contents.css?
|
||||||
|
How can I set specific classes for anchors?
|
||||||
|
How can I extend custom tags?
|
||||||
|
How can I add images?
|
||||||
|
How can I configure tables?
|
||||||
|
How can I add more attributes to anchor tags?
|
||||||
|
How can I allow / deny specific tags?
|
||||||
|
How to add custom styles for ul tags?
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
.. include:: /Includes.rst.txt
|
||||||
|
|
||||||
|
|
||||||
|
.. _configuration:
|
||||||
|
|
||||||
|
=============
|
||||||
|
Configuration
|
||||||
|
=============
|
||||||
|
|
||||||
|
You can use the shipped configuration and everything will work as preconfigured
|
||||||
|
(using the "default" preset).
|
||||||
|
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
|
||||||
|
QuickStart
|
||||||
|
ConfigureTypo3
|
||||||
|
Concepts
|
||||||
|
BestPractices
|
||||||
|
Examples
|
||||||
|
Reference
|
||||||
|
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
.. include:: /Includes.rst.txt
|
||||||
|
|
||||||
|
|
||||||
|
.. _config-quickstart:
|
||||||
|
|
||||||
|
========================
|
||||||
|
Configuration Quickstart
|
||||||
|
========================
|
||||||
|
|
||||||
|
Here we explain, how to modify the existing configuration in a few simple steps.
|
||||||
|
|
||||||
|
View Existing Configuration
|
||||||
|
===========================
|
||||||
|
|
||||||
|
To familiarize yourself with the configuration, look at the existing configuration
|
||||||
|
in your TYPO3 website:
|
||||||
|
|
||||||
|
To view the existing RTE presets in the "Global Configuration", go to
|
||||||
|
:guilabel:`System > Configuration` in the backend, choose
|
||||||
|
:guilabel:`$GLOBALS['TYPO3_CONF_VARS'] (Global Configuration)` and select
|
||||||
|
:guilabel:`RTE`:
|
||||||
|
|
||||||
|
.. figure:: images/global-configuration-rte.png
|
||||||
|
:class: with-shadow
|
||||||
|
|
||||||
|
Global Configuration: RTE > Presets
|
||||||
|
|
||||||
|
By default, TYPO3 is shipped with three configuration presets:
|
||||||
|
|
||||||
|
* default
|
||||||
|
* full
|
||||||
|
* minimal
|
||||||
|
|
||||||
|
Minimal Example
|
||||||
|
===============
|
||||||
|
|
||||||
|
Here is a very minimal example of changing the default configuration. All
|
||||||
|
configuration is done in a custom sitepackage extension, see also
|
||||||
|
:ref:`best-practice-sitepackage`.
|
||||||
|
|
||||||
|
Override the configuration preset "default" by adding this in :file:`<my_extension>/ext_localconf.php`
|
||||||
|
(replace `my_extension` with your extension key):
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
$GLOBALS['TYPO3_CONF_VARS']['RTE']['Presets']['default'] = 'EXT:my_extension/Configuration/RTE/Default.yaml';
|
||||||
|
|
||||||
|
Add the file :file:`Configuration/RTE/Default.yaml` to your extension, use the file
|
||||||
|
:t3src:`rte_ckeditor/Configuration/RTE/Full.yaml` as example.
|
||||||
|
|
||||||
|
We explain the example :file:`Minimal.yaml` from the Core:
|
||||||
|
|
||||||
|
.. literalinclude:: _Quickstart/_Minimal.yaml
|
||||||
|
:language: yaml
|
||||||
|
:caption: EXT:rte_ckeditor/Configuration/RTE/Minimal.yaml
|
||||||
|
:linenos:
|
||||||
|
|
||||||
|
line #2
|
||||||
|
Imports existing files to make basic parts reusable and improve structure of configuration
|
||||||
|
|
||||||
|
line #9 toolbar
|
||||||
|
See `toolbar <https://ckeditor.com/docs/ckeditor5/latest/features/toolbar/toolbar.html>`__
|
||||||
@@ -0,0 +1,463 @@
|
|||||||
|
.. include:: /Includes.rst.txt
|
||||||
|
|
||||||
|
|
||||||
|
.. _config-ref:
|
||||||
|
|
||||||
|
=======================
|
||||||
|
Configuration Reference
|
||||||
|
=======================
|
||||||
|
|
||||||
|
.. _config-ref-yaml:
|
||||||
|
|
||||||
|
YAML Configuration Reference
|
||||||
|
============================
|
||||||
|
|
||||||
|
When configuring the CKEditor using YAML, these are the property
|
||||||
|
names that are currently used:
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
:local:
|
||||||
|
:depth: 1
|
||||||
|
|
||||||
|
processing
|
||||||
|
----------
|
||||||
|
|
||||||
|
Configuring transformations kicks in the RteHtmlParser API of TYPO3, to
|
||||||
|
only allow certain HTML tags and attributes when saving the database or
|
||||||
|
leaving the database to the RTE. However, defining transformations towards
|
||||||
|
RTE is not really necessary anymore. Defining more strict processing options
|
||||||
|
when storing content in the database also needs to be ensured that CKEditor
|
||||||
|
allows this functionality too.
|
||||||
|
|
||||||
|
This configuration option was previously built within `RTE.proc` and can
|
||||||
|
still be overridden via Page TSconfig. Everything defined via “processing”
|
||||||
|
is available in RTE.proc and triggers RteHtmlParser options.
|
||||||
|
|
||||||
|
editor
|
||||||
|
------
|
||||||
|
|
||||||
|
Editor contains all RTE-specific options. All CKEditor-specific options, which one
|
||||||
|
could imagine are available under “config” property and handed over to CKEditor’s
|
||||||
|
instance-specific config array.
|
||||||
|
|
||||||
|
All other sub-properties are usually handled via TYPO3 and then injected in the
|
||||||
|
CKEditor instance at runtime. This is useful for registering extra plugins, like
|
||||||
|
the TYPO3 core does with a custom :file:`typo3-link.js` plugin, or adding
|
||||||
|
third-party plugins like handling images.
|
||||||
|
|
||||||
|
editor.config
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. option:: editor.config
|
||||||
|
|
||||||
|
Configuration options For a list of all options see
|
||||||
|
https://ckeditor.com/docs/ckeditor5/latest/api/module_core_editor_editorconfig-EditorConfig.html
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
Some configuration options from the official CKEditor 5 documentation
|
||||||
|
do not apply to TYPO3, since they are related to specific plugins
|
||||||
|
(for example: CKBox, CloudServices) which are not bundled in TYPO3's
|
||||||
|
CKEditor build.
|
||||||
|
|
||||||
|
.. option:: editor.config.language
|
||||||
|
|
||||||
|
defines the editor’s UI language, and is dynamically calculated (if not set otherwise) by
|
||||||
|
the backend users’ preference.
|
||||||
|
|
||||||
|
.. option:: editor.config.contentsLanguage
|
||||||
|
|
||||||
|
defines the language of the data, which is fetched from the
|
||||||
|
sys_language information, but can be overridden by this option as well.
|
||||||
|
For referencing files, TYPO3's internal "EXT:" syntax can be used, for
|
||||||
|
using language labels, TYPO3's "LLL:" language functionality can be used.
|
||||||
|
|
||||||
|
.. option:: editor.config.contentsCss
|
||||||
|
|
||||||
|
defines the location of one or multiple CSS file(s) of the editor, containing the style
|
||||||
|
definitions that will be applied to the backend editor RTE element.
|
||||||
|
|
||||||
|
Example with single file:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
:caption: MyCKPreset.yaml
|
||||||
|
|
||||||
|
editor.config.contentsCss:
|
||||||
|
- "EXT:rte_ckeditor/Resources/Public/Css/contents.css"
|
||||||
|
|
||||||
|
This is the default, as defined in :t3src:`rte_ckeditor/Configuration/RTE/Editor/Base.yaml`.
|
||||||
|
|
||||||
|
Example with multiple files:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
:caption: MyCKPreset.yaml
|
||||||
|
|
||||||
|
editor.config.contentsCss:
|
||||||
|
- "EXT:rte_ckeditor/Resources/Public/Css/contents.css"
|
||||||
|
- "EXT:my_sitepackage/Resources/Public/Css/contents.css?v=2"
|
||||||
|
|
||||||
|
Since the CKEditor element is rendered within the page content of the TYPO3 backend
|
||||||
|
(and not in an iframe or web-component), all CSS declarations in that file
|
||||||
|
must refer to an actual element hierarchy ending like
|
||||||
|
:css:`#data_tt_content__2687__bodytext_ckeditor5 .ck-content`. To achieve this,
|
||||||
|
TYPO3 automatically parses the contents of the CSS file with a process called
|
||||||
|
"auto-prefixing" (via JavaScript, client-side) and converts all references to
|
||||||
|
that "virtual" root hierarchy.
|
||||||
|
|
||||||
|
A CSS declaration like :css:`:root { background-color: green }` gets turned into
|
||||||
|
:css:`#data_tt_content__2687__bodytext_ckeditor5 .ck-content { background-color: green; }`.
|
||||||
|
|
||||||
|
You can use a :css:`:root { ... }` declaration, for example to reset
|
||||||
|
relative/absolute sizes to ensure the CKEditor area being compatible to your
|
||||||
|
usual frontend CSS. Also using `body {...}` is viable.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
Referenced CSS stylesheets need to
|
||||||
|
be downloadable via :js:`fetch()` in order for the JavaScript-based
|
||||||
|
prefixing to work.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
Also note that the generated CSS file is cached by your browser. If you change
|
||||||
|
the contents of your CSS file, be sure to either reload the browser cache,
|
||||||
|
or use a directive like
|
||||||
|
:yaml:`editor.config.contentsCss: "EXT:my_sitepackage/Resources/Public/Css/contents.css?v=2"`
|
||||||
|
where you change the `?v=` URI string after any file modification to enforce
|
||||||
|
requesting an updated version of the file.
|
||||||
|
|
||||||
|
.. option:: editor.config.heading
|
||||||
|
|
||||||
|
Defines headings available in the heading dropdown.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
:caption: MyCKPreset.yaml
|
||||||
|
|
||||||
|
heading:
|
||||||
|
options:
|
||||||
|
- { model: 'heading2', view: 'h2', title: 'Heading 2' }
|
||||||
|
- { model: 'heading3', view: 'h3', title: 'Heading 3' }
|
||||||
|
- { model: 'heading4', view: 'h4', title: 'Heading 4' }
|
||||||
|
|
||||||
|
It is also possible to set a class for a heading by default
|
||||||
|
(for example, :html:`<h2 class="h2">`):
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
:caption: MyCKPreset.yaml
|
||||||
|
|
||||||
|
heading:
|
||||||
|
options:
|
||||||
|
- { model: 'heading2', view: { name: 'h2', classes: 'h2' }, title: 'Heading 2' }
|
||||||
|
- { model: 'heading3', view: { name: 'h3', classes: 'h3' }, title: 'Heading 3' }
|
||||||
|
- { model: 'heading4', view: { name: 'h4', classes: 'h4' }, title: 'Heading 4' }
|
||||||
|
|
||||||
|
To be able to reset a heading to a paragraph, add also the :yaml:`paragraph`
|
||||||
|
option:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
:caption: MyCKPreset.yaml
|
||||||
|
:emphasize-lines: 3
|
||||||
|
|
||||||
|
heading:
|
||||||
|
options:
|
||||||
|
- { model: 'paragraph', title: 'Paragraph' }
|
||||||
|
- { model: 'heading2', view 'h2', title: 'Heading 2' }
|
||||||
|
# ...
|
||||||
|
|
||||||
|
A title can also be localized with `LLL:EXT:...`.
|
||||||
|
|
||||||
|
.. option:: editor.config.style
|
||||||
|
|
||||||
|
Defines styles available in the style dropdown.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
:caption: MyCKPreset.yaml
|
||||||
|
|
||||||
|
style:
|
||||||
|
definitions:
|
||||||
|
- { name: "Lead", element: "p", classes: ['lead'] }
|
||||||
|
- { name: "Multiple", element: "p", classes: ['first', 'second'] }
|
||||||
|
|
||||||
|
.. option:: editor.config.importModules
|
||||||
|
|
||||||
|
Imports custom CKEditor plugins. See :t3src:`rte_ckeditor/Configuration/RTE/Editor/Plugins.yaml`
|
||||||
|
or :ref:`How do I create a custom plugin? <config-example-customplugin>`
|
||||||
|
for examples.
|
||||||
|
|
||||||
|
.. _config-linkbrowser:
|
||||||
|
Link Browser specific options
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
There are more configuration options that can be defined in the YAML file of an RTE preset
|
||||||
|
related to the Link Browser, when managing hyperlinks inside the CKEditor.
|
||||||
|
|
||||||
|
Note that the Link Browser can also be displayed based on FormEngine TCA definitions. These
|
||||||
|
use similar configuration, but from their TCA PHP configuration, and unrelated to the YAML
|
||||||
|
definition.
|
||||||
|
|
||||||
|
The additional example file :t3src:`rte_ckeditor/Configuration/RTE/Editor/LinkBrowser.yaml`
|
||||||
|
lists all of the following options as an example.
|
||||||
|
|
||||||
|
These options are a bit fragmented, it is important to watch for the proper indentation as well
|
||||||
|
the proper option relation.
|
||||||
|
|
||||||
|
.. important::
|
||||||
|
Please note that these options are set at the topmost level, and **not** nested inside
|
||||||
|
the `editor` YAML structure.
|
||||||
|
|
||||||
|
A short overview:
|
||||||
|
|
||||||
|
* `allowedOptions` - allowed list of additional attribute boxes
|
||||||
|
* `allowedTypes` - list of allowed Link Types inside the RTE
|
||||||
|
* `classesAnchor` - list of default CSS and link target values per Link Type
|
||||||
|
* `buttons` - Additional sub-configuration array for specific dropdowns
|
||||||
|
* `buttons.link.options` - Global options for the Link Browser
|
||||||
|
* `buttons.link.relAttribute` - Configuration for the `rel` attribute block
|
||||||
|
* `buttons.link.queryParametersSelector` - Configuration for the `queryParameter` (URI arguments) attribute block
|
||||||
|
* `buttons.link.targetSelector` - Configuration for the `target` attribute block
|
||||||
|
* `buttons.link.properties.class.allowedClasses` - Allowed additional CSS classes in the `CSS` attribute block
|
||||||
|
* `buttons.link.[LinkType].properties.class.default` - Default CSS class per Link Type
|
||||||
|
* `classes` - Label definitions for CSS class names
|
||||||
|
|
||||||
|
allowedOptions
|
||||||
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
This string contains a comma separated list of additional attributes used in the Link Browser.
|
||||||
|
Available field lists can be found in :t3src:`backend/Classes/Controller/AbstractLinkBrowserController.php`,
|
||||||
|
method :php:`getLinkAttributeFieldDefinitions()`.
|
||||||
|
|
||||||
|
Note that the attributes `target`, `class` and `rel` are displayed differently depending on
|
||||||
|
whether the Link Browser was opened for a TCA element, or a RTE element. See
|
||||||
|
:t3src:`rte_ckeditor/Classes/Controller/BrowseLinksController.php` in method
|
||||||
|
`getLinkAttributeFieldDefinitions()`.
|
||||||
|
|
||||||
|
Valid attributes keys are:
|
||||||
|
|
||||||
|
.. option:: target
|
||||||
|
|
||||||
|
If set, an input box for link target (for example "_blank") is available.
|
||||||
|
|
||||||
|
.. option:: title
|
||||||
|
|
||||||
|
If set, entering the link title is available.
|
||||||
|
|
||||||
|
.. option:: class
|
||||||
|
|
||||||
|
If set, allowing to enter a CSS class name for the link is available.
|
||||||
|
This needs to match the CSS classes made available to the CKEDitor instance.
|
||||||
|
|
||||||
|
.. option:: params
|
||||||
|
|
||||||
|
If set, additional parameters are allowed to be set for a link.
|
||||||
|
|
||||||
|
.. option:: rel
|
||||||
|
|
||||||
|
If set, relations (:html:`rel` attribute) for links can be set.
|
||||||
|
|
||||||
|
To set all of them, you can use:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
:caption: MyCKPreset.yaml
|
||||||
|
|
||||||
|
allowedOptions: 'target,title,class,params,rel'
|
||||||
|
|
||||||
|
To remove all options you can use an empty string:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
:caption: MyCKPreset.yaml
|
||||||
|
|
||||||
|
allowedOptions: ''
|
||||||
|
|
||||||
|
allowedTypes
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
This string contains a comma-separated list of all allowed Link Types
|
||||||
|
for the Link Browser. These are currently:
|
||||||
|
|
||||||
|
* `page`
|
||||||
|
* `url`
|
||||||
|
* `file`
|
||||||
|
* `folder`
|
||||||
|
* `email`
|
||||||
|
* `...` any custom Link Type
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
:caption: MyCKPreset.yaml
|
||||||
|
|
||||||
|
allowedTypes: 'page,url,file,folder,email,customType'
|
||||||
|
|
||||||
|
To remove all types you can use an empty string:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
:caption: MyCKPreset.yaml
|
||||||
|
|
||||||
|
allowedTypes: ''
|
||||||
|
|
||||||
|
classesAnchor
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
This is a sub-array of default CSS classes and target attributes, per Link Type:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
:caption: MyCKPreset.yaml
|
||||||
|
|
||||||
|
classesAnchor:
|
||||||
|
- { class: "customPageCssClass", type: "page", target: "" }
|
||||||
|
- { class: "customUrlCssClass", type: "url", target: "_blank" }
|
||||||
|
- { class: "customFileCssClass", type: "file", target: "_parent" }
|
||||||
|
- { class: "customFolderCssClass", type: "folder" }
|
||||||
|
- { class: "customTelephoneCssClass", type: "telephone" }
|
||||||
|
- { class: "customEmailCssClass", type: "email" }
|
||||||
|
|
||||||
|
Note that the available CSS class here must also be part of the
|
||||||
|
`buttons.link.properties.class.allowedClasses` definition.
|
||||||
|
|
||||||
|
buttons.link
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
This structure defines both global options as well as Link Type-specific
|
||||||
|
options:
|
||||||
|
|
||||||
|
buttons.link.options.removeItems
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Can be set to exclude certain Link Types:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
:caption: MyCKPreset.yaml
|
||||||
|
|
||||||
|
buttons:
|
||||||
|
link:
|
||||||
|
options:
|
||||||
|
removeItems: 'telephone'
|
||||||
|
|
||||||
|
buttons.link.relAttribute.enabled
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
If the `allowedOptions` string list contains `rel` for setting relation
|
||||||
|
attributes, this option must also be enabled:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
:caption: MyCKPreset.yaml
|
||||||
|
|
||||||
|
buttons:
|
||||||
|
link:
|
||||||
|
relAttribute:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
buttons.link.queryParametersSelector.enabled
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
If the `allowedOptions` string list contains `params` for setting URI argument
|
||||||
|
attributes, this option must also be enabled:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
:caption: MyCKPreset.yaml
|
||||||
|
|
||||||
|
buttons:
|
||||||
|
link:
|
||||||
|
queryParametersSelector:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
|
||||||
|
buttons.link.targetSelector.disabled
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
If the `allowedOptions` string list contains `target`, a dropdown is displayed by
|
||||||
|
default. If you want to hide it, you must set this option to `true`:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
:caption: MyCKPreset.yaml
|
||||||
|
|
||||||
|
buttons:
|
||||||
|
link:
|
||||||
|
targetSelector:
|
||||||
|
disabled: true
|
||||||
|
|
||||||
|
buttons.link.properties.class.required
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
A CSS class selection can be forced, so that it may not be empty:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
:caption: MyCKPreset.yaml
|
||||||
|
|
||||||
|
buttons:
|
||||||
|
link:
|
||||||
|
properties:
|
||||||
|
class:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
buttons.link.properties.class.allowedClasses
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
This is the most vital CSS class selection list, based on a comma-separated
|
||||||
|
string naming all CSS classes that are allowed. Default CSS classes per Link Type
|
||||||
|
can only be selected, if they are part of this list.
|
||||||
|
|
||||||
|
The names of the CSS classes can be adjusted via the `classes` top-level configuration
|
||||||
|
hierarchy (see below)
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
:caption: MyCKPreset.yaml
|
||||||
|
|
||||||
|
buttons:
|
||||||
|
link:
|
||||||
|
properties:
|
||||||
|
class:
|
||||||
|
allowedClasses: 'globalCss1,globalCss1,CustomPageCssClass'
|
||||||
|
|
||||||
|
buttons.link.[linkType].properties.class.default
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
For each Link Type, a default CSS class can be defined, using the name of the
|
||||||
|
Link Type as a key:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
:caption: MyCKPreset.yaml
|
||||||
|
|
||||||
|
buttons:
|
||||||
|
link:
|
||||||
|
telephone:
|
||||||
|
class:
|
||||||
|
default: "customTelephoneCssClass"
|
||||||
|
email:
|
||||||
|
class:
|
||||||
|
default: "customEmailCssClass"
|
||||||
|
|
||||||
|
Note that the CSS class listed here must also be contained in
|
||||||
|
`buttons.link.properties.class.allowedClasses`.
|
||||||
|
|
||||||
|
classes.[CssClassName]
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The list of CSS classes defined in `buttons.link.properties.class.allowedClasses`
|
||||||
|
can set a custom label as well as a styling the select option. Note that styling
|
||||||
|
select options does not work in every browser, and is not suggested to use.
|
||||||
|
|
||||||
|
The name of the structure key must match the CSS class name, with a sub-structure
|
||||||
|
defining `name` (the actual label) and `value` (the possible CSS styling of the option
|
||||||
|
inside the dropdown):
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
:caption: MyCKPreset.yaml
|
||||||
|
|
||||||
|
classes:
|
||||||
|
globalCss1:
|
||||||
|
name: "A Label for globalCss1"
|
||||||
|
value: "color: red"
|
||||||
|
customEmailCssClass:
|
||||||
|
name: "An email-specific class for VIPs"
|
||||||
|
|
||||||
|
.. _config-ref-tsconfig:
|
||||||
|
|
||||||
|
Page TSconfig
|
||||||
|
=============
|
||||||
|
|
||||||
|
We recommend you to put all configurations for the preset in the
|
||||||
|
:ref:`YAML <config-typo3-yaml>` configuration. However, it is still possible to
|
||||||
|
override these settings through the page TSconfig.
|
||||||
|
|
||||||
|
You can find a list of configuration properties in the :ref:`Page TSconfig
|
||||||
|
reference, chapter RTE <t3tsref:pageTsRte>`.
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# Allow the <iframe> tag with all attributes, all classes and all styles,
|
||||||
|
# as well as demonstrating class restrictions to the <i> tag
|
||||||
|
editor:
|
||||||
|
config:
|
||||||
|
htmlSupport:
|
||||||
|
# if you want to allow that an inline tag like `<i>` can also be empty
|
||||||
|
allowEmpty: ['i']
|
||||||
|
allow:
|
||||||
|
- { name: 'iframe', attributes: true, classes: true, styles: true }
|
||||||
|
# multiple definitions for the same tag name are possible
|
||||||
|
- { name: 'i', classes: [ 'fa-brands', 'fa-typo3' ] }
|
||||||
|
# allows any repetitive class name, that starts with `fa-`
|
||||||
|
# (the regular expression has to be defined in `pattern`)
|
||||||
|
- { name: 'i', classes: { pattern: '^((fa-[^\h]+)(\h+|$))+' } }
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# Minimal configuration for the editor
|
||||||
|
editor:
|
||||||
|
config:
|
||||||
|
toolbar:
|
||||||
|
items:
|
||||||
|
- bold
|
||||||
|
- italic
|
||||||
|
- '|'
|
||||||
|
- clipboard
|
||||||
|
- undo
|
||||||
|
- redo
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
editor:
|
||||||
|
config:
|
||||||
|
toolbar:
|
||||||
|
items:
|
||||||
|
# add button to select font family
|
||||||
|
- fontFamily
|
||||||
|
# add button to select font size
|
||||||
|
- fontSize
|
||||||
|
# add button to select font color
|
||||||
|
- fontColor
|
||||||
|
# add button to select font background color
|
||||||
|
- fontBackgroundColor
|
||||||
|
|
||||||
|
fontColor:
|
||||||
|
colors:
|
||||||
|
- { label: 'Orange', color: '#ff8700' }
|
||||||
|
- { label: 'Blue', color: '#0080c9' }
|
||||||
|
- { label: 'Green', color: '#209d44' }
|
||||||
|
|
||||||
|
fontBackgroundColor:
|
||||||
|
colors:
|
||||||
|
- { label: 'Stage orange light', color: '#fab85c' }
|
||||||
|
|
||||||
|
fontFamily:
|
||||||
|
options:
|
||||||
|
- 'default'
|
||||||
|
- 'Arial, sans-serif'
|
||||||
|
|
||||||
|
fontSize:
|
||||||
|
options:
|
||||||
|
- 'default'
|
||||||
|
- 18
|
||||||
|
- 21
|
||||||
|
|
||||||
|
importModules:
|
||||||
|
- { 'module': '@ckeditor/ckeditor5-font', 'exports': [ 'Font' ] }
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
editor:
|
||||||
|
config:
|
||||||
|
toolbar:
|
||||||
|
items:
|
||||||
|
# add button to enable fullscreen view
|
||||||
|
- fullscreen
|
||||||
|
|
||||||
|
fullscreen:
|
||||||
|
menuBar:
|
||||||
|
# Disable menu bar in fullscreen view
|
||||||
|
isVisible: false
|
||||||
|
|
||||||
|
importModules:
|
||||||
|
- { module: '@ckeditor/ckeditor5-fullscreen', exports: [ 'Fullscreen' ] }
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# Minimal configuration for the editor
|
||||||
|
editor:
|
||||||
|
config:
|
||||||
|
toolbar:
|
||||||
|
items:
|
||||||
|
- bold
|
||||||
|
- italic
|
||||||
|
- { label: 'Additional', icon: 'threeVerticalDots', items: [ 'specialCharacters', 'horizontalLine' ] }
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# Import basic configuration
|
||||||
|
imports:
|
||||||
|
- { resource: "EXT:rte_ckeditor/Configuration/RTE/Processing.yaml" }
|
||||||
|
- { resource: "EXT:rte_ckeditor/Configuration/RTE/Editor/Base.yaml" }
|
||||||
|
- { resource: "EXT:rte_ckeditor/Configuration/RTE/Editor/Plugins.yaml" }
|
||||||
|
# Add configuration for the editor
|
||||||
|
# For complete documentation see http://docs.ckeditor.com/#!/api/CKEDITOR.config
|
||||||
|
editor:
|
||||||
|
config:
|
||||||
|
# Include custom CSS
|
||||||
|
contentsCss:
|
||||||
|
- "EXT:my_extension/Resources/Public/Css/rte.css"
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
editor:
|
||||||
|
config:
|
||||||
|
importModules:
|
||||||
|
- { module: '@my-vendor/my-package/timestamp-plugin.js', exports: [ 'Timestamp' ] }
|
||||||
|
toolbar:
|
||||||
|
items:
|
||||||
|
- bold
|
||||||
|
- italic
|
||||||
|
- '|'
|
||||||
|
- clipboard
|
||||||
|
- undo
|
||||||
|
- redo
|
||||||
|
- '|'
|
||||||
|
- timestamp
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'dependencies' => ['backend'],
|
||||||
|
'imports' => [
|
||||||
|
'@my-vendor/my-package/timestamp-plugin.js' => 'EXT:my_extension/Resources/Public/JavaScript/Ckeditor/timestamp-plugin.js',
|
||||||
|
],
|
||||||
|
];
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# Load default processing options
|
||||||
|
imports:
|
||||||
|
- { resource: 'EXT:rte_ckeditor/Configuration/RTE/Processing.yaml' }
|
||||||
|
- { resource: 'EXT:rte_ckeditor/Configuration/RTE/Editor/Base.yaml' }
|
||||||
|
|
||||||
|
# Minimal configuration for the editor
|
||||||
|
editor:
|
||||||
|
config:
|
||||||
|
toolbar:
|
||||||
|
items:
|
||||||
|
- bold
|
||||||
|
- italic
|
||||||
|
- '|'
|
||||||
|
- clipboard
|
||||||
|
- undo
|
||||||
|
- redo
|
||||||
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 91 KiB |
|
After Width: | Height: | Size: 30 KiB |
@@ -0,0 +1,30 @@
|
|||||||
|
.. include:: /Includes.rst.txt
|
||||||
|
|
||||||
|
|
||||||
|
.. _general-concepts:
|
||||||
|
|
||||||
|
================
|
||||||
|
General Concepts
|
||||||
|
================
|
||||||
|
|
||||||
|
User interfaces
|
||||||
|
===============
|
||||||
|
|
||||||
|
CKEditor has multiple user interfaces, of which TYPO3 uses the following:
|
||||||
|
|
||||||
|
Classic
|
||||||
|
Editing is done within a fixed container.
|
||||||
|
It is possible to customize how the editor behaves and how the content is styled.
|
||||||
|
|
||||||
|
Used in the TYPO3 Backend.
|
||||||
|
|
||||||
|
Inline
|
||||||
|
All formatting styles are reused from the surrounding HTML and CSS styles,
|
||||||
|
allowing for a seamless frontend editing.
|
||||||
|
|
||||||
|
Used by TYPO3’s frontend_editing,
|
||||||
|
which can be found on `GitHub <https://github.com/FriendsOfTYPO3/frontend_editing>`__.
|
||||||
|
frontend_editing is not covered in this document.
|
||||||
|
|
||||||
|
For a demonstration of all user interfaces,
|
||||||
|
see the `CKEditor demo <https://ckeditor.com/ckeditor-5/demo/editor-types/>`__.
|
||||||
|
After Width: | Height: | Size: 20 KiB |
@@ -0,0 +1 @@
|
|||||||
|
.. You can put central messages to display on all pages here
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
.. include:: /Includes.rst.txt
|
||||||
|
|
||||||
|
.. _start:
|
||||||
|
|
||||||
|
=====================
|
||||||
|
TYPO3 RTE by CKEditor
|
||||||
|
=====================
|
||||||
|
|
||||||
|
:Extension key:
|
||||||
|
rte_ckeditor
|
||||||
|
|
||||||
|
:Package name:
|
||||||
|
typo3/cms-rte-ckeditor
|
||||||
|
|
||||||
|
:Version:
|
||||||
|
|release|
|
||||||
|
|
||||||
|
:Language:
|
||||||
|
en
|
||||||
|
|
||||||
|
:Author:
|
||||||
|
TYPO3 contributors
|
||||||
|
|
||||||
|
:License:
|
||||||
|
This document is published under the
|
||||||
|
`Open Content License <https://www.openhub.net/licenses/opl>`__.
|
||||||
|
|
||||||
|
:Rendered:
|
||||||
|
|today|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
This extension integrates the `CKEditor`_ as a rich text editor into the TYPO3
|
||||||
|
backend.
|
||||||
|
|
||||||
|
.. _CKEditor: https://ckeditor.com/
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Table of Contents:**
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
:titlesonly:
|
||||||
|
|
||||||
|
Introduction/Index
|
||||||
|
GeneralConcepts/Index
|
||||||
|
Installation/Index
|
||||||
|
Configuration/Index
|
||||||
|
Usage/Index
|
||||||
|
|
||||||
|
.. Meta Menu
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:hidden:
|
||||||
|
|
||||||
|
Sitemap
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
.. include:: /Includes.rst.txt
|
||||||
|
|
||||||
|
|
||||||
|
.. _installation:
|
||||||
|
|
||||||
|
============
|
||||||
|
Installation
|
||||||
|
============
|
||||||
|
|
||||||
|
This extension is part of the TYPO3 Core.
|
||||||
|
|
||||||
|
.. contents:: Table of contents
|
||||||
|
:local:
|
||||||
|
|
||||||
|
Installation with Composer
|
||||||
|
==========================
|
||||||
|
|
||||||
|
Check whether you are already using the extension with:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
composer show | grep rte
|
||||||
|
|
||||||
|
This should either give you no result or something similar to:
|
||||||
|
|
||||||
|
.. code-block:: none
|
||||||
|
|
||||||
|
typo3/cms-rte-ckeditor v12.4.11
|
||||||
|
|
||||||
|
If it is not installed yet, use the ``composer require`` command to install
|
||||||
|
the extension:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
composer require typo3/cms-rte-ckeditor
|
||||||
|
|
||||||
|
The given version depends on the version of the TYPO3 Core you are using.
|
||||||
|
|
||||||
|
Installation without Composer
|
||||||
|
=============================
|
||||||
|
|
||||||
|
In an installation without Composer, the extension is already shipped but might
|
||||||
|
not be activated yet. Activate it as follows:
|
||||||
|
|
||||||
|
#. In the backend, navigate to the :guilabel:`System > Extensions`
|
||||||
|
module.
|
||||||
|
#. Click the :guilabel:`Activate` icon for the RTE CKEditor extension.
|
||||||
|
|
||||||
|
.. figure:: /Images/InstallActivate.png
|
||||||
|
:class: with-border
|
||||||
|
:alt: Extension manager showing RTE CKEditor extension
|
||||||
|
|
||||||
|
Extension manager showing RTE CKEditor extension
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
.. include:: /Includes.rst.txt
|
||||||
|
|
||||||
|
|
||||||
|
.. _introduction:
|
||||||
|
|
||||||
|
============
|
||||||
|
Introduction
|
||||||
|
============
|
||||||
|
|
||||||
|
|
||||||
|
.. _what-it-does:
|
||||||
|
|
||||||
|
What does it do?
|
||||||
|
================
|
||||||
|
|
||||||
|
The extension `rte_ckeditor` provides a rich text editor (RTE) by integrating
|
||||||
|
`CKEditor 5 <https://ckeditor.com/ckeditor-5/>`__ into TYPO3.
|
||||||
|
|
||||||
|
This makes it possible to
|
||||||
|
use the features of CKEditor when editing rich text fields in the TYPO3
|
||||||
|
backend or frontend (if frontend editing is used). Rich text fields are
|
||||||
|
fields which may contain text with markup, for example for adding a style
|
||||||
|
such as bold, using lists or enumerations, headlines or adding links.
|
||||||
|
|
||||||
|
.. figure:: images/example_textfield.png
|
||||||
|
:class: with-shadow
|
||||||
|
|
||||||
|
Editing a textfield in the backend with rte_ckeditor.
|
||||||
|
|
||||||
|
CKEditor is a :abbr:`WYSIWYG (what you see is what you get)` editor mostly written
|
||||||
|
in JavaScript, and is used in many systems due to its flexibility. There are hundreds
|
||||||
|
of free open-source plugins for CKEditor to enhance the editing experience.
|
||||||
|
|
||||||
|
History
|
||||||
|
=======
|
||||||
|
|
||||||
|
Before TYPO3 v8, a custom fork of "HtmlArea", another open-source WYSIWYG editor
|
||||||
|
was shipped with TYPO3 Core in a separate extension `rtehtmlarea`. "HtmlArea"
|
||||||
|
isn't supported anymore since TYPO3 v9 LTS. You have to migrate to `rte_ckeditor`
|
||||||
|
when upgrading from previous TYPO3 versions (=< v8).
|
||||||
|
|
||||||
|
CKEditor 4 was officially integrated as the default rich text editor in TYPO3 v8 LTS,
|
||||||
|
within an extension called `rte_ckeditor`.
|
||||||
|
|
||||||
|
With TYPO3 v12, CKEditor 4 has been updated to CKEditor 5.
|
||||||
|
|
||||||
|
.. _features:
|
||||||
|
|
||||||
|
Features
|
||||||
|
========
|
||||||
|
|
||||||
|
The extension `rte_ckeditor` incorporates the features of CKEditor and adds
|
||||||
|
additional functionality, configuration presets and plugins.
|
||||||
|
|
||||||
|
Some examples of features:
|
||||||
|
|
||||||
|
* Configurable via YAML files
|
||||||
|
* Configuration presets (minimal, default, full) for TYPO3
|
||||||
|
* Toolbar customization
|
||||||
|
* Link functionality: integration with TYPO3 link wizard
|
||||||
|
* Wordcount: plugin that counts and shows the chars/words/paragraphs in
|
||||||
|
the footer of the editor. It also supports limiting the max. amount of chars/words.
|
||||||
|
After Width: | Height: | Size: 57 KiB |
@@ -0,0 +1,9 @@
|
|||||||
|
:template: sitemap.html
|
||||||
|
|
||||||
|
.. include:: /Includes.rst.txt
|
||||||
|
|
||||||
|
=======
|
||||||
|
Sitemap
|
||||||
|
=======
|
||||||
|
|
||||||
|
.. The sitemap.html template will insert here the page tree automatically.
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
.. include:: /Includes.rst.txt
|
||||||
|
|
||||||
|
|
||||||
|
.. _usage:
|
||||||
|
|
||||||
|
===========
|
||||||
|
Basic Usage
|
||||||
|
===========
|
||||||
|
|
||||||
|
How the editor toolbar looks and what is available will depend on the
|
||||||
|
currently used configuration.
|
||||||
|
|
||||||
|
The CKEditor will be active for RTE fields, for example the Text field
|
||||||
|
of a content element.
|
||||||
|
|
||||||
|
For information about working with content elements, see the section
|
||||||
|
:ref:`t3editors:content-editing` and :ref:`t3editors:rte` in the
|
||||||
|
"Tutorial for Editors".
|
||||||
|
|
||||||
|
The following examples are done using the
|
||||||
|
preset which is installed with the Introduction Package.
|
||||||
|
|
||||||
|
Example: Use bold
|
||||||
|
=================
|
||||||
|
|
||||||
|
Select text and click on the **B** (for bold) button:
|
||||||
|
|
||||||
|
.. image:: images/rte_bold.png
|
||||||
|
:class: with-shadow
|
||||||
|
|
||||||
|
|
||||||
|
Example: Create a link
|
||||||
|
======================
|
||||||
|
|
||||||
|
Select text and click on the Link button:
|
||||||
|
|
||||||
|
.. image:: images/rte_link.png
|
||||||
|
:class: with-shadow
|
||||||
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 18 KiB |
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<guides xmlns="https://www.phpdoc.org/guides" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="https://www.phpdoc.org/guides ../vendor/phpdocumentor/guides-cli/resources/schema/guides.xsd"
|
||||||
|
links-are-relative="true">
|
||||||
|
<extension class="\T3Docs\Typo3DocsTheme\DependencyInjection\Typo3DocsThemeExtension"
|
||||||
|
project-home="https://extensions.typo3.org/extension/rte_ckeditor/"
|
||||||
|
project-contact="https://typo3.slack.com/archives/C025BQLFA"
|
||||||
|
project-repository="https://github.com/typo3/typo3"
|
||||||
|
project-issues="https://forge.typo3.org/projects/typo3cms-core/issues"
|
||||||
|
edit-on-github-branch="main"
|
||||||
|
edit-on-github="typo3/typo3"
|
||||||
|
edit-on-github-directory="typo3/sysext/rte_ckeditor/Documentation/"
|
||||||
|
typo3-core-preferred="main"
|
||||||
|
interlink-shortcode="typo3/cms-rte-ckeditor"
|
||||||
|
/>
|
||||||
|
<project title="RTE by CKEditor"
|
||||||
|
release="main (development)"
|
||||||
|
version="main (development)"
|
||||||
|
copyright="since 2016 by the TYPO3 contributors"
|
||||||
|
/>
|
||||||
|
</guides>
|
||||||
@@ -0,0 +1,339 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
License is intended to guarantee your freedom to share and change free
|
||||||
|
software--to make sure the software is free for all its users. This
|
||||||
|
General Public License applies to most of the Free Software
|
||||||
|
Foundation's software and to any other program whose authors commit to
|
||||||
|
using it. (Some other Free Software Foundation software is covered by
|
||||||
|
the GNU Lesser General Public License instead.) You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
this service if you wish), that you receive source code or can get it
|
||||||
|
if you want it, that you can change the software or use pieces of it
|
||||||
|
in new free programs; and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
anyone to deny you these rights or to ask you to surrender the rights.
|
||||||
|
These restrictions translate to certain responsibilities for you if you
|
||||||
|
distribute copies of the software, or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must give the recipients all the rights that
|
||||||
|
you have. You must make sure that they, too, receive or can get the
|
||||||
|
source code. And you must show them these terms so they know their
|
||||||
|
rights.
|
||||||
|
|
||||||
|
We protect your rights with two steps: (1) copyright the software, and
|
||||||
|
(2) offer you this license which gives you legal permission to copy,
|
||||||
|
distribute and/or modify the software.
|
||||||
|
|
||||||
|
Also, for each author's protection and ours, we want to make certain
|
||||||
|
that everyone understands that there is no warranty for this free
|
||||||
|
software. If the software is modified by someone else and passed on, we
|
||||||
|
want its recipients to know that what they have is not the original, so
|
||||||
|
that any problems introduced by others will not reflect on the original
|
||||||
|
authors' reputations.
|
||||||
|
|
||||||
|
Finally, any free program is threatened constantly by software
|
||||||
|
patents. We wish to avoid the danger that redistributors of a free
|
||||||
|
program will individually obtain patent licenses, in effect making the
|
||||||
|
program proprietary. To prevent this, we have made it clear that any
|
||||||
|
patent must be licensed for everyone's free use or not licensed at all.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License applies to any program or other work which contains
|
||||||
|
a notice placed by the copyright holder saying it may be distributed
|
||||||
|
under the terms of this General Public License. The "Program", below,
|
||||||
|
refers to any such program or work, and a "work based on the Program"
|
||||||
|
means either the Program or any derivative work under copyright law:
|
||||||
|
that is to say, a work containing the Program or a portion of it,
|
||||||
|
either verbatim or with modifications and/or translated into another
|
||||||
|
language. (Hereinafter, translation is included without limitation in
|
||||||
|
the term "modification".) Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running the Program is not restricted, and the output from the Program
|
||||||
|
is covered only if its contents constitute a work based on the
|
||||||
|
Program (independent of having been made by running the Program).
|
||||||
|
Whether that is true depends on what the Program does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Program's
|
||||||
|
source code as you receive it, in any medium, provided that you
|
||||||
|
conspicuously and appropriately publish on each copy an appropriate
|
||||||
|
copyright notice and disclaimer of warranty; keep intact all the
|
||||||
|
notices that refer to this License and to the absence of any warranty;
|
||||||
|
and give any other recipients of the Program a copy of this License
|
||||||
|
along with the Program.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy, and
|
||||||
|
you may at your option offer warranty protection in exchange for a fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Program or any portion
|
||||||
|
of it, thus forming a work based on the Program, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) You must cause the modified files to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
b) You must cause any work that you distribute or publish, that in
|
||||||
|
whole or in part contains or is derived from the Program or any
|
||||||
|
part thereof, to be licensed as a whole at no charge to all third
|
||||||
|
parties under the terms of this License.
|
||||||
|
|
||||||
|
c) If the modified program normally reads commands interactively
|
||||||
|
when run, you must cause it, when started running for such
|
||||||
|
interactive use in the most ordinary way, to print or display an
|
||||||
|
announcement including an appropriate copyright notice and a
|
||||||
|
notice that there is no warranty (or else, saying that you provide
|
||||||
|
a warranty) and that users may redistribute the program under
|
||||||
|
these conditions, and telling the user how to view a copy of this
|
||||||
|
License. (Exception: if the Program itself is interactive but
|
||||||
|
does not normally print such an announcement, your work based on
|
||||||
|
the Program is not required to print an announcement.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Program,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Program, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Program.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Program
|
||||||
|
with the Program (or with a work based on the Program) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may copy and distribute the Program (or a work based on it,
|
||||||
|
under Section 2) in object code or executable form under the terms of
|
||||||
|
Sections 1 and 2 above provided that you also do one of the following:
|
||||||
|
|
||||||
|
a) Accompany it with the complete corresponding machine-readable
|
||||||
|
source code, which must be distributed under the terms of Sections
|
||||||
|
1 and 2 above on a medium customarily used for software interchange; or,
|
||||||
|
|
||||||
|
b) Accompany it with a written offer, valid for at least three
|
||||||
|
years, to give any third party, for a charge no more than your
|
||||||
|
cost of physically performing source distribution, a complete
|
||||||
|
machine-readable copy of the corresponding source code, to be
|
||||||
|
distributed under the terms of Sections 1 and 2 above on a medium
|
||||||
|
customarily used for software interchange; or,
|
||||||
|
|
||||||
|
c) Accompany it with the information you received as to the offer
|
||||||
|
to distribute corresponding source code. (This alternative is
|
||||||
|
allowed only for noncommercial distribution and only if you
|
||||||
|
received the program in object code or executable form with such
|
||||||
|
an offer, in accord with Subsection b above.)
|
||||||
|
|
||||||
|
The source code for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For an executable work, complete source
|
||||||
|
code means all the source code for all modules it contains, plus any
|
||||||
|
associated interface definition files, plus the scripts used to
|
||||||
|
control compilation and installation of the executable. However, as a
|
||||||
|
special exception, the source code distributed need not include
|
||||||
|
anything that is normally distributed (in either source or binary
|
||||||
|
form) with the major components (compiler, kernel, and so on) of the
|
||||||
|
operating system on which the executable runs, unless that component
|
||||||
|
itself accompanies the executable.
|
||||||
|
|
||||||
|
If distribution of executable or object code is made by offering
|
||||||
|
access to copy from a designated place, then offering equivalent
|
||||||
|
access to copy the source code from the same place counts as
|
||||||
|
distribution of the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
4. You may not copy, modify, sublicense, or distribute the Program
|
||||||
|
except as expressly provided under this License. Any attempt
|
||||||
|
otherwise to copy, modify, sublicense or distribute the Program is
|
||||||
|
void, and will automatically terminate your rights under this License.
|
||||||
|
However, parties who have received copies, or rights, from you under
|
||||||
|
this License will not have their licenses terminated so long as such
|
||||||
|
parties remain in full compliance.
|
||||||
|
|
||||||
|
5. You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Program or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Program (or any work based on the
|
||||||
|
Program), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Program or works based on it.
|
||||||
|
|
||||||
|
6. Each time you redistribute the Program (or any work based on the
|
||||||
|
Program), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute or modify the Program subject to
|
||||||
|
these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties to
|
||||||
|
this License.
|
||||||
|
|
||||||
|
7. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot
|
||||||
|
distribute so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you
|
||||||
|
may not distribute the Program at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Program by
|
||||||
|
all those who receive copies directly or indirectly through you, then
|
||||||
|
the only way you could satisfy both it and this License would be to
|
||||||
|
refrain entirely from distribution of the Program.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under
|
||||||
|
any particular circumstance, the balance of the section is intended to
|
||||||
|
apply and the section as a whole is intended to apply in other
|
||||||
|
circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system, which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
8. If the distribution and/or use of the Program is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Program under this License
|
||||||
|
may add an explicit geographical distribution limitation excluding
|
||||||
|
those countries, so that distribution is permitted only in or among
|
||||||
|
countries not thus excluded. In such case, this License incorporates
|
||||||
|
the limitation as if written in the body of this License.
|
||||||
|
|
||||||
|
9. The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Program
|
||||||
|
specifies a version number of this License which applies to it and "any
|
||||||
|
later version", you have the option of following the terms and conditions
|
||||||
|
either of that version or of any later version published by the Free
|
||||||
|
Software Foundation. If the Program does not specify a version number of
|
||||||
|
this License, you may choose any version ever published by the Free Software
|
||||||
|
Foundation.
|
||||||
|
|
||||||
|
10. If you wish to incorporate parts of the Program into other free
|
||||||
|
programs whose distribution conditions are different, write to the author
|
||||||
|
to ask for permission. For software which is copyrighted by the Free
|
||||||
|
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||||
|
make exceptions for this. Our decision will be guided by the two goals
|
||||||
|
of preserving the free status of all derivatives of our free software and
|
||||||
|
of promoting the sharing and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||||
|
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||||
|
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||||
|
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||||
|
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||||
|
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||||
|
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||||
|
REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||||
|
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||||
|
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||||
|
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||||
|
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||||
|
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
convey the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program is interactive, make it output a short notice like this
|
||||||
|
when it starts in an interactive mode:
|
||||||
|
|
||||||
|
Gnomovision version 69, Copyright (C) year name of author
|
||||||
|
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, the commands you use may
|
||||||
|
be called something other than `show w' and `show c'; they could even be
|
||||||
|
mouse-clicks or menu items--whatever suits your program.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your
|
||||||
|
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||||
|
necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||||
|
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||||
|
|
||||||
|
<signature of Ty Coon>, 1 April 1989
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
This General Public License does not permit incorporating your program into
|
||||||
|
proprietary programs. If your program is a subroutine library, you may
|
||||||
|
consider it more useful to permit linking proprietary applications with the
|
||||||
|
library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License.
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
================================
|
||||||
|
TYPO3 extension ``rte_ckeditor``
|
||||||
|
================================
|
||||||
|
|
||||||
|
This extension integrates the `CKEditor`_ as a rich text editor into the TYPO3
|
||||||
|
backend.
|
||||||
|
|
||||||
|
.. _CKEditor: https://ckeditor.com/
|
||||||
|
|
||||||
|
:Repository: https://github.com/typo3/typo3
|
||||||
|
:Issues: https://forge.typo3.org/
|
||||||
|
:Read online: https://docs.typo3.org/c/typo3/cms-rte-ckeditor/main/en-us/
|
||||||
|
:Packagist: https://packagist.org/packages/typo3/cms-rte-ckeditor
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import{Command as T,Plugin as f}from"@ckeditor/ckeditor5-core";import{logWarning as B,CKEditorError as b,first as E}from"@ckeditor/ckeditor5-utils";import{ButtonView as D,createDropdown as k,addToolbarToDropdown as N,MenuBarMenuView as _,MenuBarMenuListView as I,MenuBarMenuListItemView as V,MenuBarMenuListItemButtonView as M}from"@ckeditor/ckeditor5-ui";import{IconAlignLeft as O,IconAlignRight as L,IconAlignCenter as C,IconAlignJustify as P}from"@ckeditor/ckeditor5-icons";/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/const d=["left","right","center","justify"];function h(i){return d.includes(i)}function p(i,e){return e.contentLanguageDirection=="rtl"?i==="right":i==="left"}function A(i){const e=i.map(t=>{let o;return typeof t=="string"?o={name:t}:o=t,o}).filter(t=>{const o=d.includes(t.name);return o||B("alignment-config-name-not-recognized",{option:t}),o}),n=e.filter(t=>!!t.className).length;if(n&&n<e.length)throw new b("alignment-config-classnames-are-missing",{configuredOptions:i});return e.forEach((t,o,a)=>{const s=a.slice(o+1);if(s.some(r=>r.name==t.name))throw new b("alignment-config-name-already-defined",{option:t,configuredOptions:i});if(t.className&&s.some(l=>l.className==t.className))throw new b("alignment-config-classname-already-defined",{option:t,configuredOptions:i})}),e}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/const w="alignment";class v extends T{refresh(){const n=this.editor.locale,t=E(this.editor.model.document.selection.getSelectedBlocks());this.isEnabled=!!t&&this._canBeAligned(t),this.isEnabled&&t.hasAttribute("alignment")?this.value=t.getAttribute("alignment"):this.value=n.contentLanguageDirection==="rtl"?"right":"left"}execute(e={}){const n=this.editor,t=n.locale,o=n.model,a=o.document,s=e.value;o.change(c=>{const r=Array.from(a.selection.getSelectedBlocks()).filter(g=>this._canBeAligned(g)),l=r[0].getAttribute("alignment");p(s,t)||l===s||!s?S(r,c):z(r,c,s)})}_canBeAligned(e){return this.editor.model.schema.checkAttribute(e,w)}}function S(i,e){for(const n of i)e.removeAttribute(w,n)}function z(i,e,n){for(const t of i)e.setAttribute(w,n,t)}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class y extends f{static get pluginName(){return"AlignmentEditing"}static get isOfficialPlugin(){return!0}constructor(e){super(e),e.config.define("alignment",{options:d.map(n=>({name:n}))})}init(){const e=this.editor,n=e.locale,t=e.model.schema,a=A(e.config.get("alignment.options")).filter(l=>h(l.name)&&!p(l.name,n)),s=a.some(l=>!!l.className);t.extend("$block",{allowAttributes:"alignment"}),e.model.schema.setAttributeProperties("alignment",{isFormatting:!0}),s?e.conversion.attributeToAttribute(G(a)):e.conversion.for("downcast").attributeToAttribute(F(a));const c=U(a);for(const l of c)e.conversion.for("upcast").attributeToAttribute(l);const r=j(a);for(const l of r)e.conversion.for("upcast").attributeToAttribute(l);e.commands.add("alignment",new v(e))}}function F(i){const e={};for(const{name:t}of i)e[t]={key:"style",value:{"text-align":t}};return{model:{key:"alignment",values:i.map(t=>t.name)},view:e}}function U(i){const e=[];for(const{name:n}of i)e.push({view:{key:"style",value:{"text-align":n}},model:{key:"alignment",value:n}});return e}function j(i){const e=[];for(const{name:n}of i)e.push({view:{key:"align",value:n},model:{key:"alignment",value:n}});return e}function G(i){const e={};for(const t of i)e[t.name]={key:"class",value:t.className};return{model:{key:"alignment",values:i.map(t=>t.name)},view:e}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/const u=new Map([["left",O],["right",L],["center",C],["justify",P]]);class x extends f{get localizedOptionTitles(){const e=this.editor.t;return{left:e("Align left"),right:e("Align right"),center:e("Align center"),justify:e("Justify")}}static get pluginName(){return"AlignmentUI"}static get isOfficialPlugin(){return!0}init(){const e=this.editor,n=A(e.config.get("alignment.options"));n.map(t=>t.name).filter(h).forEach(t=>this._addButton(t)),this._addToolbarDropdown(n),this._addMenuBarMenu(n)}_addButton(e){this.editor.ui.componentFactory.add(`alignment:${e}`,t=>this._createButton(t,e))}_createButton(e,n,t={}){const o=this.editor,a=o.commands.get("alignment"),s=new D(e);return s.set({label:this.localizedOptionTitles[n],icon:u.get(n),tooltip:!0,isToggleable:!0,...t}),s.bind("isEnabled").to(a),s.bind("isOn").to(a,"value",c=>c===n),this.listenTo(s,"execute",()=>{o.execute("alignment",{value:n}),o.editing.view.focus()}),s}_addToolbarDropdown(e){const n=this.editor;n.ui.componentFactory.add("alignment",o=>{const a=k(o),s=o.uiLanguageDirection==="rtl"?"w":"e",c=o.t;N(a,()=>e.map(m=>this._createButton(o,m.name,{tooltipPosition:s})),{enableActiveItemFocusOnDropdownOpen:!0,isVertical:!0,ariaLabel:c("Text alignment toolbar")}),a.buttonView.set({label:c("Text alignment"),tooltip:!0}),a.extendTemplate({attributes:{class:"ck-alignment-dropdown"}});const r=o.contentLanguageDirection==="rtl"?u.get("right"):u.get("left"),l=n.commands.get("alignment");return a.buttonView.bind("icon").to(l,"value",m=>u.get(m)||r),a.bind("isEnabled").to(l,"isEnabled"),this.listenTo(a,"execute",()=>{n.editing.view.focus()}),a})}_addMenuBarMenu(e){const n=this.editor;n.ui.componentFactory.add("menuBar:alignment",t=>{const o=n.commands.get("alignment"),a=t.t,s=new _(t),c=new I(t);s.bind("isEnabled").to(o),c.set({ariaLabel:a("Text alignment"),role:"menu"}),s.buttonView.set({label:a("Text alignment")});for(const r of e){const l=new V(t,s),m=new M(t);m.delegate("execute").to(s),m.set({label:this.localizedOptionTitles[r.name],icon:u.get(r.name),role:"menuitemcheckbox",isToggleable:!0}),m.on("execute",()=>{n.execute("alignment",{value:r.name}),n.editing.view.focus()}),m.bind("isOn").to(o,"value",g=>g===r.name),m.bind("isEnabled").to(o,"isEnabled"),l.children.add(m),c.items.add(l)}return s.panelView.children.add(c),s})}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class J extends f{static get requires(){return[y,x]}static get pluginName(){return"Alignment"}static get isOfficialPlugin(){return!0}}export{J as Alignment,v as AlignmentCommand,y as AlignmentEditing,x as AlignmentUI,d as _ALIGNMENT_SUPPORTED_OPTIONS,h as _isAlignmentSupported,p as _isDefaultAlignment,A as _normalizeAlignmentOptions};
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import{Plugin as y}from"@ckeditor/ckeditor5-core";import{Delete as P}from"@ckeditor/ckeditor5-typing";import{ModelLiveRange as B,ModelSchemaContext as S}from"@ckeditor/ckeditor5-engine";import{first as q}from"@ckeditor/ckeditor5-utils";/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/function g(o,t,e,s){let a,n=null;typeof s=="function"?a=s:(n=o.commands.get(s),a=()=>{o.execute(s)}),o.model.document.on("change:data",(h,l)=>{if(n&&!n.isEnabled||!t.isEnabled)return;const r=q(o.model.document.selection.getRanges());if(!r.isCollapsed||l.isUndo||!l.isLocal)return;const f=Array.from(o.model.document.differ.getChanges()),c=f[0];if(f.length!=1||c.type!=="insert"||c.name!="$text"||c.length!=1)return;const i=c.position.parent;if(i.is("element","codeBlock")||i.is("element","listItem")&&typeof s!="function"&&!["numberedList","bulletedList","todoList"].includes(s)||n&&n.value===!0)return;const u=i.getChild(0),A=o.model.createRangeOn(u);if(!A.containsRange(r)&&!r.end.isEqual(A.end))return;const p=e.exec(u.data.substr(0,r.end.offset));p&&o.model.enqueueChange(d=>{const m=o.model.document.selection,L=d.createPositionAt(i,0),$=d.createPositionAt(i,p[0].length),k=new B(L,$);if(a({match:p})!==!1){const x=Array.from(m.getAttributes());d.remove(k);const C=m.getFirstRange(),v=d.createRangeIn(i);i.isEmpty&&!v.isEqual(C)&&!v.containsRange(C,!0)&&d.remove(i),F(d,m,x)}k.detach(),o.model.enqueueChange(()=>{o.plugins.get("Delete").requestUndoOnBackspace()})})})}function F(o,t,e){const s=o.model.schema,a=t.getFirstPosition();let n=new S(a);s.checkChild(n,"$text")&&(n=n.push("$text"));for(const[h,l]of e)s.checkAttribute(n,h)&&o.setSelectionAttribute(h,l)}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/function b(o,t,e,s){let a,n;e instanceof RegExp?a=e:n=e,n=n||(h=>{let l;const r=[],f=[];for(;(l=a.exec(h))!==null&&!(l&&l.length<4);){let{index:c,"1":i,"2":u,"3":A}=l;const p=i+u+A;c+=l[0].length-p.length;const d=[c,c+i.length],m=[c+i.length+u.length,c+i.length+u.length+A.length];r.push(d),r.push(m),f.push([c+i.length,c+i.length+u.length])}return{remove:r,format:f}}),o.model.document.on("change:data",(h,l)=>{if(l.isUndo||!l.isLocal||!t.isEnabled)return;const r=o.model,f=r.document.selection;if(!f.isCollapsed)return;const c=Array.from(r.document.differ.getChanges()),i=c[0];if(c.length!=1||i.type!=="insert"||i.name!="$text"||i.length!=1)return;const u=f.focus,A=u.parent,{text:p,range:d}=I(r.createRange(r.createPositionAt(A,0),u),r),m=n(p),L=R(d.start,m.format,r),$=R(d.start,m.remove,r);L.length&&$.length&&r.enqueueChange(k=>{if(s(k,L)!==!1){for(const x of $.reverse())k.remove(x);r.enqueueChange(()=>{o.plugins.get("Delete").requestUndoOnBackspace()})}})})}function R(o,t,e){return t.filter(s=>s[0]!==void 0&&s[1]!==void 0).map(s=>e.createRange(o.getShiftedBy(s[0]),o.getShiftedBy(s[1])))}function I(o,t){let e=o.start;return{text:Array.from(o.getItems()).reduce((a,n)=>!(n.is("$text")||n.is("$textProxy"))||n.getAttribute("code")?(e=t.createPositionAfter(n),""):a+n.data,""),range:t.createRange(e,o.end)}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class T extends y{static get requires(){return[P]}static get pluginName(){return"Autoformat"}static get isOfficialPlugin(){return!0}afterInit(){const t=this.editor,e=this.editor.t;this._addListAutoformats(),this._addBasicStylesAutoformats(),this._addHeadingAutoformats(),this._addBlockQuoteAutoformats(),this._addCodeBlockAutoformats(),this._addHorizontalLineAutoformats(),t.accessibility.addKeystrokeInfos({keystrokes:[{label:e("Revert autoformatting action"),keystroke:"Backspace"}]})}_addListAutoformats(){const t=this.editor.commands;t.get("bulletedList")&&g(this.editor,this,/^[*-]\s$/,"bulletedList"),t.get("numberedList")&&g(this.editor,this,/^1[.|)]\s$/,"numberedList"),t.get("todoList")&&g(this.editor,this,/^\[\s?\]\s$/,"todoList"),t.get("checkTodoList")&&g(this.editor,this,/^\[\s?x\s?\]\s$/,()=>{this.editor.execute("todoList"),this.editor.execute("checkTodoList")})}_addBasicStylesAutoformats(){const t=this.editor.commands;if(t.get("bold")){const e=_(this.editor,"bold");b(this.editor,this,/(?:^|\s)(\*\*)([^*]+)(\*\*)$/g,e),b(this.editor,this,/(?:^|\s)(__)([^_]+)(__)$/g,e)}if(t.get("italic")){const e=_(this.editor,"italic");b(this.editor,this,/(?:^|\s)(\*)([^*_]+)(\*)$/g,e),b(this.editor,this,/(?:^|\s)(_)([^_]+)(_)$/g,e)}if(t.get("code")){const e=_(this.editor,"code");b(this.editor,this,/(`)([^`]+)(`)$/g,e)}if(t.get("strikethrough")){const e=_(this.editor,"strikethrough");b(this.editor,this,/(~~)([^~]+)(~~)$/g,e)}}_addHeadingAutoformats(){const t=this.editor.commands.get("heading");t&&t.modelElements.filter(e=>e.match(/^heading[1-6]$/)).forEach(e=>{const s=e[7],a=new RegExp(`^(#{${s}})\\s$`);g(this.editor,this,a,()=>{if(!t.isEnabled||t.value===e)return!1;this.editor.execute("heading",{value:e})})})}_addBlockQuoteAutoformats(){this.editor.commands.get("blockQuote")&&g(this.editor,this,/^>\s$/,"blockQuote")}_addCodeBlockAutoformats(){const t=this.editor,e=t.model.document.selection;t.commands.get("codeBlock")&&g(t,this,/^```$/,()=>{if(e.getFirstPosition().parent.is("element","listItem"))return!1;this.editor.execute("codeBlock",{usePreviousLanguageChoice:!0})})}_addHorizontalLineAutoformats(){this.editor.commands.get("horizontalLine")&&g(this.editor,this,/^---$/,"horizontalLine")}}function _(o,t){return(e,s)=>{if(!o.commands.get(t).isEnabled)return!1;const n=o.model.schema.getValidRanges(s,t);for(const h of n)e.setAttribute(t,!0,h);e.removeSelectionAttribute(t)}}export{T as Autoformat,g as blockAutoformatEditing,b as inlineAutoformatEditing};
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import{Command as G,Plugin as n}from"@ckeditor/ckeditor5-core";import{ModelDocumentSelection as X}from"@ckeditor/ckeditor5-engine";import{IconBold as j,IconCode as W,IconItalic as z,IconStrikethrough as J,IconSubscript as Q,IconSuperscript as Y,IconUnderline as Z}from"@ckeditor/ckeditor5-icons";import{MenuBarMenuListItemButtonView as a,ButtonView as d}from"@ckeditor/ckeditor5-ui";import{TwoStepCaretMovement as O,inlineHighlight as tt}from"@ckeditor/ckeditor5-typing";/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class l extends G{attributeKey;constructor(t,e){super(t),this.attributeKey=e}refresh(){const t=this.editor.model,e=t.document;this.value=this._getValueFromFirstAllowedNode(),this.isEnabled=t.schema.checkAttributeInSelection(e.selection,this.attributeKey)}execute(t={}){const e=this.editor.model,o=e.document.selection,c=t.forceValue===void 0?!this.value:t.forceValue;e.change(g=>{if(o.isCollapsed)c?g.setSelectionAttribute(this.attributeKey,!0):g.removeSelectionAttribute(this.attributeKey);else{const I=e.schema.getValidRanges(o.getRanges(),this.attributeKey,{includeEmptyRanges:!0});for(const r of I){let x=r,S=this.attributeKey;r.isCollapsed&&(x=r.start.parent,S=X._getStoreAttributeKey(this.attributeKey)),c?g.setAttribute(S,c,x):g.removeAttribute(S,x)}}})}_getValueFromFirstAllowedNode(){const t=this.editor.model,e=t.schema,i=t.document.selection;if(i.isCollapsed)return i.hasAttribute(this.attributeKey);for(const o of i.getRanges())for(const c of o.getItems())if(e.checkAttribute(c,this.attributeKey))return c.hasAttribute(this.attributeKey);return!1}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/const p="bold";class P extends n{static get pluginName(){return"BoldEditing"}static get isOfficialPlugin(){return!0}init(){const t=this.editor,e=this.editor.t;t.model.schema.extend("$text",{allowAttributes:p}),t.model.schema.setAttributeProperties(p,{isFormatting:!0,copyOnEnter:!0}),t.conversion.attributeToElement({model:p,view:"strong",upcastAlso:["b",i=>{const o=i.getStyle("font-weight");return o&&(o=="bold"||Number(o)>=600)?{name:!0,styles:["font-weight"]}:null}]}),t.commands.add(p,new l(t,p)),t.keystrokes.set("CTRL+B",p),t.accessibility.addKeystrokeInfos({keystrokes:[{label:e("Bold text"),keystroke:"CTRL+B"}]})}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/function u({editor:s,commandName:t,plugin:e,icon:i,label:o,keystroke:c}){return g=>{const I=s.commands.get(t),r=new g(s.locale);return r.set({label:o,icon:i,keystroke:c,isToggleable:!0}),r.bind("isEnabled").to(I,"isEnabled"),r.bind("isOn").to(I,"value"),r instanceof a?r.set({role:"menuitemcheckbox"}):r.set({tooltip:!0}),e.listenTo(r,"execute",()=>{s.execute(t),s.editing.view.focus()}),r}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/const E="bold";class U extends n{static get pluginName(){return"BoldUI"}static get isOfficialPlugin(){return!0}init(){const t=this.editor,e=t.locale.t,i=u({editor:t,commandName:E,plugin:this,icon:j,label:e("Bold"),keystroke:"CTRL+B"});t.ui.componentFactory.add(E,()=>i(d)),t.ui.componentFactory.add("menuBar:"+E,()=>i(a))}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class et extends n{static get requires(){return[P,U]}static get pluginName(){return"Bold"}static get isOfficialPlugin(){return!0}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/const m="code",it="ck-code_selected";class R extends n{static get pluginName(){return"CodeEditing"}static get isOfficialPlugin(){return!0}static get requires(){return[O]}init(){const t=this.editor,e=this.editor.t;t.model.schema.extend("$text",{allowAttributes:m}),t.model.schema.setAttributeProperties(m,{isFormatting:!0,copyOnEnter:!1}),t.conversion.attributeToElement({model:m,view:"code"}),t.commands.add(m,new l(t,m)),t.plugins.get(O).registerAttribute(m),tt(t,m,"code",it),t.accessibility.addKeystrokeInfos({keystrokes:[{label:e("Move out of an inline code style"),keystroke:[["arrowleft","arrowleft"],["arrowright","arrowright"]]}]})}}function st(s,{insertAt:t}={}){if(typeof document>"u")return;const e=document.head||document.getElementsByTagName("head")[0],i=document.createElement("style");i.type="text/css",window.litNonce&&i.setAttribute("nonce",window.litNonce),t==="top"&&e.firstChild?e.insertBefore(i,e.firstChild):e.appendChild(i),i.styleSheet?i.styleSheet.cssText=s:i.appendChild(document.createTextNode(s))}st(".ck-content code{background-color:hsla(0,0%,78%,.3);border-radius:2px;padding:.15em}.ck.ck-editor__editable .ck-code_selected{background-color:hsla(0,0%,78%,.5)}");/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/const C="code";class F extends n{static get pluginName(){return"CodeUI"}static get isOfficialPlugin(){return!0}init(){const t=this.editor,e=t.locale.t,i=u({editor:t,commandName:C,plugin:this,icon:W,label:e("Code")});t.ui.componentFactory.add(C,()=>i(d)),t.ui.componentFactory.add("menuBar:"+C,()=>i(a))}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class nt extends n{static get requires(){return[R,F]}static get pluginName(){return"Code"}static get isOfficialPlugin(){return!0}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/const h="italic";class v extends n{static get pluginName(){return"ItalicEditing"}static get isOfficialPlugin(){return!0}init(){const t=this.editor,e=this.editor.t;t.model.schema.extend("$text",{allowAttributes:h}),t.model.schema.setAttributeProperties(h,{isFormatting:!0,copyOnEnter:!0}),t.conversion.attributeToElement({model:h,view:"i",upcastAlso:["em",{styles:{"font-style":"italic"}}]}),t.commands.add(h,new l(t,h)),t.keystrokes.set("CTRL+I",h),t.accessibility.addKeystrokeInfos({keystrokes:[{label:e("Italic text"),keystroke:"CTRL+I"}]})}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/const T="italic";class L extends n{static get pluginName(){return"ItalicUI"}static get isOfficialPlugin(){return!0}init(){const t=this.editor,e=t.locale.t,i=u({editor:t,commandName:T,plugin:this,icon:z,keystroke:"CTRL+I",label:e("Italic")});t.ui.componentFactory.add(T,()=>i(d)),t.ui.componentFactory.add("menuBar:"+T,()=>i(a))}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class rt extends n{static get requires(){return[v,L]}static get pluginName(){return"Italic"}static get isOfficialPlugin(){return!0}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/const f="strikethrough";class K extends n{static get pluginName(){return"StrikethroughEditing"}static get isOfficialPlugin(){return!0}init(){const t=this.editor,e=this.editor.t;t.model.schema.extend("$text",{allowAttributes:f}),t.model.schema.setAttributeProperties(f,{isFormatting:!0,copyOnEnter:!0}),t.conversion.attributeToElement({model:f,view:"s",upcastAlso:["del","strike",{styles:{"text-decoration":"line-through"}}]}),t.commands.add(f,new l(t,f)),t.keystrokes.set("CTRL+SHIFT+X","strikethrough"),t.accessibility.addKeystrokeInfos({keystrokes:[{label:e("Strikethrough text"),keystroke:"CTRL+SHIFT+X"}]})}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/const B="strikethrough";class $ extends n{static get pluginName(){return"StrikethroughUI"}static get isOfficialPlugin(){return!0}init(){const t=this.editor,e=t.locale.t,i=u({editor:t,commandName:B,plugin:this,icon:J,keystroke:"CTRL+SHIFT+X",label:e("Strikethrough")});t.ui.componentFactory.add(B,()=>i(d)),t.ui.componentFactory.add("menuBar:"+B,()=>i(a))}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class ot extends n{static get requires(){return[K,$]}static get pluginName(){return"Strikethrough"}static get isOfficialPlugin(){return!0}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/const b="subscript";class H extends n{static get pluginName(){return"SubscriptEditing"}static get isOfficialPlugin(){return!0}init(){const t=this.editor;t.model.schema.extend("$text",{allowAttributes:b}),t.model.schema.setAttributeProperties(b,{isFormatting:!0,copyOnEnter:!0}),t.conversion.attributeToElement({model:b,view:"sub",upcastAlso:[{styles:{"vertical-align":"sub"}}]}),t.commands.add(b,new l(t,b))}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/const N="subscript";class _ extends n{static get pluginName(){return"SubscriptUI"}static get isOfficialPlugin(){return!0}init(){const t=this.editor,e=t.locale.t,i=u({editor:t,commandName:N,plugin:this,icon:Q,label:e("Subscript")});t.ui.componentFactory.add(N,()=>i(d)),t.ui.componentFactory.add("menuBar:"+N,()=>i(a))}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class ct extends n{static get requires(){return[H,_]}static get pluginName(){return"Subscript"}static get isOfficialPlugin(){return!0}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/const y="superscript";class q extends n{static get pluginName(){return"SuperscriptEditing"}static get isOfficialPlugin(){return!0}init(){const t=this.editor;t.model.schema.extend("$text",{allowAttributes:y}),t.model.schema.setAttributeProperties(y,{isFormatting:!0,copyOnEnter:!0}),t.conversion.attributeToElement({model:y,view:"sup",upcastAlso:[{styles:{"vertical-align":"super"}}]}),t.commands.add(y,new l(t,y))}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/const A="superscript";class D extends n{static get pluginName(){return"SuperscriptUI"}static get isOfficialPlugin(){return!0}init(){const t=this.editor,e=t.locale.t,i=u({editor:t,commandName:A,plugin:this,icon:Y,label:e("Superscript")});t.ui.componentFactory.add(A,()=>i(d)),t.ui.componentFactory.add("menuBar:"+A,()=>i(a))}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class at extends n{static get requires(){return[q,D]}static get pluginName(){return"Superscript"}static get isOfficialPlugin(){return!0}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/const k="underline";class V extends n{static get pluginName(){return"UnderlineEditing"}static get isOfficialPlugin(){return!0}init(){const t=this.editor,e=this.editor.t;t.model.schema.extend("$text",{allowAttributes:k}),t.model.schema.setAttributeProperties(k,{isFormatting:!0,copyOnEnter:!0}),t.conversion.attributeToElement({model:k,view:"u",upcastAlso:{styles:{"text-decoration":"underline"}}}),t.commands.add(k,new l(t,k)),t.keystrokes.set("CTRL+U","underline"),t.accessibility.addKeystrokeInfos({keystrokes:[{label:e("Underline text"),keystroke:"CTRL+U"}]})}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/const w="underline";class M extends n{static get pluginName(){return"UnderlineUI"}static get isOfficialPlugin(){return!0}init(){const t=this.editor,e=t.locale.t,i=u({editor:t,commandName:w,plugin:this,icon:Z,label:e("Underline"),keystroke:"CTRL+U"});t.ui.componentFactory.add(w,()=>i(d)),t.ui.componentFactory.add("menuBar:"+w,()=>i(a))}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class lt extends n{static get requires(){return[V,M]}static get pluginName(){return"Underline"}static get isOfficialPlugin(){return!0}}export{l as AttributeCommand,et as Bold,P as BoldEditing,U as BoldUI,nt as Code,R as CodeEditing,F as CodeUI,rt as Italic,v as ItalicEditing,L as ItalicUI,ot as Strikethrough,K as StrikethroughEditing,$ as StrikethroughUI,ct as Subscript,H as SubscriptEditing,_ as SubscriptUI,at as Superscript,q as SuperscriptEditing,D as SuperscriptUI,lt as Underline,V as UnderlineEditing,M as UnderlineUI,u as _getBasicStylesButtonCreator};
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import{Command as B,Plugin as m}from"@ckeditor/ckeditor5-core";import{Enter as v}from"@ckeditor/ckeditor5-enter";import{Delete as x}from"@ckeditor/ckeditor5-typing";import{first as f}from"@ckeditor/ckeditor5-utils";import{IconQuote as y}from"@ckeditor/ckeditor5-icons";import{ButtonView as E,MenuBarMenuListItemButtonView as P}from"@ckeditor/ckeditor5-ui";/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class h extends B{refresh(){this.value=this._getValue(),this.isEnabled=this._checkEnabled()}execute(e={}){const t=this.editor.model,o=t.schema,i=t.document.selection,n=Array.from(i.getSelectedBlocks()),s=e.forceValue===void 0?!this.value:e.forceValue;t.change(u=>{if(!s)this._removeQuote(u,n.filter(d));else{const r=n.filter(l=>d(l)||k(o,l));this._applyQuote(u,r)}})}_getValue(){const e=this.editor.model.document.selection,t=f(e.getSelectedBlocks());return!!(t&&d(t))}_checkEnabled(){if(this.value)return!0;const e=this.editor.model.document.selection,t=this.editor.model.schema,o=f(e.getSelectedBlocks());return o?k(t,o):!1}_removeQuote(e,t){p(e,t).reverse().forEach(o=>{if(o.start.isAtStart&&o.end.isAtEnd){e.unwrap(o.start.parent);return}if(o.start.isAtStart){const n=e.createPositionBefore(o.start.parent);e.move(o,n);return}o.end.isAtEnd||e.split(o.end);const i=e.createPositionAfter(o.end.parent);e.move(o,i)})}_applyQuote(e,t){const o=[];p(e,t).reverse().forEach(i=>{let n=d(i.start);n||(n=e.createElement("blockQuote"),e.wrap(i,n)),o.push(n)}),o.reverse().reduce((i,n)=>i.nextSibling==n?(e.merge(e.createPositionAfter(i)),i):n)}}function d(c){return c.parent.name=="blockQuote"?c.parent:null}function p(c,e){let t,o=0;const i=[];for(;o<e.length;){const n=e[o],s=e[o+1];t||(t=c.createPositionBefore(n)),(!s||n.nextSibling!=s)&&(i.push(c.createRange(t,c.createPositionAfter(n))),t=null),o++}return i}function k(c,e){const t=c.checkChild(e.parent,"blockQuote"),o=c.checkChild(["$root","blockQuote"],e);return t&&o}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class b extends m{static get pluginName(){return"BlockQuoteEditing"}static get isOfficialPlugin(){return!0}static get requires(){return[v,x]}init(){const e=this.editor,t=e.model.schema;e.commands.add("blockQuote",new h(e)),t.register("blockQuote",{inheritAllFrom:"$container"}),e.conversion.elementToElement({model:"blockQuote",view:"blockquote"}),e.model.document.registerPostFixer(s=>{const u=e.model.document.differ.getChanges();for(const r of u)if(r.type=="insert"){const l=r.position.nodeAfter;if(!l)continue;if(l.is("element","blockQuote")&&l.isEmpty)return s.remove(l),!0;if(l.is("element","blockQuote")&&!t.checkChild(r.position,l))return s.unwrap(l),!0;if(l.is("element")){const Q=s.createRangeIn(l);for(const a of Q.getItems())if(a.is("element","blockQuote")&&!t.checkChild(s.createPositionBefore(a),a))return s.unwrap(a),!0}}else if(r.type=="remove"){const l=r.position.parent;if(l.is("element","blockQuote")&&l.isEmpty)return s.remove(l),!0}return!1});const o=this.editor.editing.view.document,i=e.model.document.selection,n=e.commands.get("blockQuote");this.listenTo(o,"enter",(s,u)=>{if(!i.isCollapsed||!n.value)return;i.getLastPosition().parent.isEmpty&&(e.execute("blockQuote"),e.editing.view.scrollToTheSelection(),u.preventDefault(),s.stop())},{context:"blockquote"}),this.listenTo(o,"delete",(s,u)=>{if(u.direction!="backward"||!i.isCollapsed||!n.value)return;const r=i.getLastPosition().parent;r.isEmpty&&!r.previousSibling&&(e.execute("blockQuote"),e.editing.view.scrollToTheSelection(),u.preventDefault(),s.stop())},{context:"blockquote"})}}function C(c,{insertAt:e}={}){if(typeof document>"u")return;const t=document.head||document.getElementsByTagName("head")[0],o=document.createElement("style");o.type="text/css",window.litNonce&&o.setAttribute("nonce",window.litNonce),e==="top"&&t.firstChild?t.insertBefore(o,t.firstChild):t.appendChild(o),o.styleSheet?o.styleSheet.cssText=c:o.appendChild(document.createTextNode(c))}C(".ck-content blockquote{border-left:5px solid #ccc;font-style:italic;margin-left:0;margin-right:0;overflow:hidden;padding-left:1.5em;padding-right:1.5em}.ck-content[dir=rtl] blockquote{border-left:0;border-right:5px solid #ccc}");/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class g extends m{static get pluginName(){return"BlockQuoteUI"}static get isOfficialPlugin(){return!0}init(){const e=this.editor;e.ui.componentFactory.add("blockQuote",()=>{const t=this._createButton(E);return t.set({tooltip:!0}),t}),e.ui.componentFactory.add("menuBar:blockQuote",()=>{const t=this._createButton(P);return t.set({role:"menuitemcheckbox"}),t})}_createButton(e){const t=this.editor,o=t.locale,i=t.commands.get("blockQuote"),n=new e(t.locale),s=o.t;return n.set({label:s("Block quote"),icon:y,isToggleable:!0}),n.bind("isEnabled").to(i,"isEnabled"),n.bind("isOn").to(i,"value"),this.listenTo(n,"execute",()=>{t.execute("blockQuote"),t.editing.view.focus()}),n}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class A extends m{static get requires(){return[b,g]}static get pluginName(){return"BlockQuote"}static get isOfficialPlugin(){return!0}}export{A as BlockQuote,h as BlockQuoteCommand,b as BlockQuoteEditing,g as BlockQuoteUI};
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import{EditorUI as g,normalizeToolbarConfig as b,DialogView as u,BoxedEditorUIView as m,StickyPanelView as w,ToolbarView as y,MenuBarView as _,InlineEditableUIView as v}from"@ckeditor/ckeditor5-ui";import{enableViewPlaceholder as P}from"@ckeditor/ckeditor5-engine";import{ElementReplacer as T,Rect as c,CKEditorError as S,getDataFromElement as C}from"@ckeditor/ckeditor5-utils";import{ElementApiMixin as V,Editor as x,attachToForm as B}from"@ckeditor/ckeditor5-core";/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class f extends g{view;_toolbarConfig;_elementReplacer;constructor(t,e){super(t),this.view=e,this._toolbarConfig=b(t.config.get("toolbar")),this._elementReplacer=new T,this.listenTo(t.editing.view,"scrollToTheSelection",this._handleScrollToTheSelectionWithStickyPanel.bind(this))}get element(){return this.view.element}init(t){const e=this.editor,i=this.view,r=e.editing.view,o=i.editable,s=r.document.getRoot();o.name=s.rootName,i.render();const l=o.element;this.setEditableElement(o.name,l),i.editable.bind("isFocused").to(this.focusTracker),r.attachDomRoot(l),t&&this._elementReplacer.replace(t,this.element),this._initPlaceholder(),this._initToolbar(),i.menuBarView&&this.initMenuBar(i.menuBarView),this._initDialogPluginIntegration(),this._initContextualBalloonIntegration(),this.fire("ready")}destroy(){super.destroy();const t=this.view,e=this.editor.editing.view;this._elementReplacer.restore(),e.getDomRoot(t.editable.name)&&e.detachDomRoot(t.editable.name),t.destroy()}_initToolbar(){const t=this.view;t.stickyPanel.bind("isActive").to(this.focusTracker,"isFocused"),t.stickyPanel.limiterElement=t.element,t.stickyPanel.bind("viewportTopOffset").to(this,"viewportOffset",({visualTop:e})=>e||0),t.toolbar.fillFromConfig(this._toolbarConfig,this.componentFactory),this.addToolbar(t.toolbar)}_initPlaceholder(){const t=this.editor,e=t.editing.view,i=e.document.getRoot(),r=t.sourceElement;let o;const s=t.config.get("placeholder");s&&(o=typeof s=="string"?s:s[this.view.editable.name]),!o&&r&&r.tagName.toLowerCase()==="textarea"&&(o=r.getAttribute("placeholder")),o&&(i.placeholder=o),P({view:e,element:i,isDirectHost:!1,keepOnFocus:!0})}_initContextualBalloonIntegration(){if(!this.editor.plugins.has("ContextualBalloon"))return;const{stickyPanel:t}=this.view,e=this.editor.plugins.get("ContextualBalloon");e.on("getPositionOptions",r=>{const o=r.return;if(!o||!t.isSticky||!t.element)return;const s=new c(t.element).height,l=typeof o.target=="function"?o.target():o.target,d=typeof o.limiter=="function"?o.limiter():o.limiter;if(l&&d&&new c(l).height>=new c(d).height-s)return;const h={...o.viewportOffsetConfig},p=(h.top||0)+s;r.return={...o,viewportOffsetConfig:{...h,top:p}}},{priority:"low"});const i=()=>{e.visibleView&&e.updatePosition()};this.listenTo(t,"change:isSticky",i),this.listenTo(this.editor.ui,"change:viewportOffset",i)}_handleScrollToTheSelectionWithStickyPanel(t,e,i){const r=this.view.stickyPanel;if(r.isSticky){const o=new c(r.element).height;e.viewportOffset.top+=o}else{const o=()=>{this.editor.editing.view.scrollToTheSelection(i)};this.listenTo(r,"change:isSticky",o),setTimeout(()=>{this.stopListening(r,"change:isSticky",o)},20)}}_initDialogPluginIntegration(){if(!this.editor.plugins.has("Dialog"))return;const t=this.view.stickyPanel,e=this.editor.plugins.get("Dialog");e.on("show",()=>{const i=e.view;i.on("moveTo",(r,o)=>{if(!t.isSticky||i.wasMoved||i.isModal)return;const s=new c(t.contentPanelElement);o[1]<s.bottom+u.defaultOffset&&(o[1]=s.bottom+u.defaultOffset)},{priority:"high"})},{priority:"low"})}}function O(n,{insertAt:t}={}){if(typeof document>"u")return;const e=document.head||document.getElementsByTagName("head")[0],i=document.createElement("style");i.type="text/css",window.litNonce&&i.setAttribute("nonce",window.litNonce),t==="top"&&e.firstChild?e.insertBefore(i,e.firstChild):e.appendChild(i),i.styleSheet?i.styleSheet.cssText=n:i.appendChild(document.createTextNode(n))}O(".ck.ck-editor{position:relative}.ck.ck-editor .ck-editor__top .ck-sticky-panel .ck-toolbar{z-index:var(--ck-z-panel)}.ck.ck-editor__top .ck-sticky-panel .ck-sticky-panel__content{border:solid var(--ck-color-base-border);border-radius:0;border-width:1px 1px 0}.ck-rounded-corners .ck.ck-editor__top .ck-sticky-panel .ck-sticky-panel__content,.ck.ck-editor__top .ck-sticky-panel .ck-sticky-panel__content.ck-rounded-corners{border-radius:var(--ck-border-radius);border-bottom-left-radius:0;border-bottom-right-radius:0}.ck.ck-editor__top .ck-sticky-panel .ck-sticky-panel__content.ck-sticky-panel__content_sticky{border-bottom-width:1px}.ck.ck-editor__top .ck-sticky-panel .ck-sticky-panel__content .ck-menu-bar{border:0;border-bottom:1px solid var(--ck-color-base-border)}.ck.ck-editor__top .ck-sticky-panel .ck-sticky-panel__content .ck-toolbar{border:0}.ck.ck-editor__main>.ck-editor__editable{background:var(--ck-color-base-background);border-radius:0}.ck-rounded-corners .ck.ck-editor__main>.ck-editor__editable,.ck.ck-editor__main>.ck-editor__editable.ck-rounded-corners{border-radius:var(--ck-border-radius);border-top-left-radius:0;border-top-right-radius:0}.ck.ck-editor__main>.ck-editor__editable:not(.ck-focused){border-color:var(--ck-color-base-border)}");/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class k extends m{stickyPanel;toolbar;editable;constructor(t,e,i={}){super(t),this.stickyPanel=new w(t),this.toolbar=new y(t,{shouldGroupWhenFull:i.shouldToolbarGroupWhenFull}),i.useMenuBar&&(this.menuBarView=new _(t)),this.editable=new v(t,e,void 0,{label:i.label})}render(){super.render(),this.menuBarView?this.stickyPanel.content.addMany([this.menuBarView,this.toolbar]):this.stickyPanel.content.add(this.toolbar),this.top.add(this.stickyPanel),this.main.add(this.editable)}}function E(n){return typeof n=="object"&&n!==null}function R(n){if(typeof n!="object"||n==null)return!1;if(Object.getPrototypeOf(n)===null)return!0;if(Object.prototype.toString.call(n)!=="[object Object]"){const e=n[Symbol.toStringTag];return e==null||!Object.getOwnPropertyDescriptor(n,Symbol.toStringTag)?.writable?!1:n.toString()===`[object ${e}]`}let t=n;for(;Object.getPrototypeOf(t)!==null;)t=Object.getPrototypeOf(t);return Object.getPrototypeOf(n)===t}function I(n){return E(n)&&n.nodeType===1&&!R(n)}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class F extends V(x){static get editorName(){return"ClassicEditor"}ui;constructor(t,e={}){if(!a(t)&&e.initialData!==void 0)throw new S("editor-create-initial-data",null);super(e),this.config.define("menuBar.isVisible",!1),this.config.get("initialData")===void 0&&this.config.set("initialData",D(t)),a(t)&&(this.sourceElement=t),this.model.document.createRoot();const i=!this.config.get("toolbar.shouldNotGroupWhenFull"),r=this.config.get("menuBar"),o=new k(this.locale,this.editing.view,{shouldToolbarGroupWhenFull:i,useMenuBar:r.isVisible,label:this.config.get("label")});this.ui=new f(this,o),B(this)}destroy(){return this.sourceElement&&this.updateSourceElement(),this.ui.destroy(),super.destroy()}static create(t,e={}){return new Promise(i=>{const r=new this(t,e);i(r.initPlugins().then(()=>r.ui.init(a(t)?t:null)).then(()=>r.data.init(r.config.get("initialData"))).then(()=>r.fire("ready")).then(()=>r))})}}function D(n){return a(n)?C(n):n}function a(n){return I(n)}export{F as ClassicEditor,f as ClassicEditorUI,k as ClassicEditorUIView};
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import{ElementApiMixin as u,Editor as h,secureSourceElement as g}from"@ckeditor/ckeditor5-core";import{CKEditorError as a,getDataFromElement as f}from"@ckeditor/ckeditor5-utils";import{EditorUI as b,EditorUIView as m,ToolbarView as w,MenuBarView as p,InlineEditableUIView as y}from"@ckeditor/ckeditor5-ui";import{enableViewPlaceholder as E}from"@ckeditor/ckeditor5-engine";/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class d extends b{view;constructor(e,i){super(e),this.view=i}init(){const e=this.editor,i=this.view,n=e.editing.view,o=i.editable,r=n.document.getRoot();o.name=r.rootName,i.render();const l=o.element;this.setEditableElement(o.name,l),i.editable.bind("isFocused").to(this.focusTracker),n.attachDomRoot(l),this._initPlaceholder(),this._initToolbar(),this.initMenuBar(this.view.menuBarView),this.fire("ready")}destroy(){super.destroy();const e=this.view,i=this.editor.editing.view;i.getDomRoot(e.editable.name)&&i.detachDomRoot(e.editable.name),e.destroy()}_initToolbar(){const e=this.editor,i=this.view;i.toolbar.fillFromConfig(e.config.get("toolbar"),this.componentFactory),this.addToolbar(i.toolbar)}_initPlaceholder(){const e=this.editor,i=e.editing.view,n=i.document.getRoot(),o=e.config.get("placeholder");if(o){const r=typeof o=="string"?o:o[n.rootName];r&&(n.placeholder=r)}E({view:i,element:n,isDirectHost:!1,keepOnFocus:!0})}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class c extends m{toolbar;editable;menuBarView;constructor(e,i,n={}){super(e),this.toolbar=new w(e,{shouldGroupWhenFull:n.shouldToolbarGroupWhenFull}),this.menuBarView=new p(e),this.editable=new y(e,i,n.editableElement,{label:n.label}),this.toolbar.extendTemplate({attributes:{class:["ck-reset_all","ck-rounded-corners"],dir:e.uiLanguageDirection}}),this.menuBarView.extendTemplate({attributes:{class:["ck-reset_all","ck-rounded-corners"],dir:e.uiLanguageDirection}})}render(){super.render(),this.registerChild([this.menuBarView,this.toolbar,this.editable])}}function T(t){return typeof t=="object"&&t!==null}function V(t){if(typeof t!="object"||t==null)return!1;if(Object.getPrototypeOf(t)===null)return!0;if(Object.prototype.toString.call(t)!=="[object Object]"){const i=t[Symbol.toStringTag];return i==null||!Object.getOwnPropertyDescriptor(t,Symbol.toStringTag)?.writable?!1:t.toString()===`[object ${i}]`}let e=t;for(;Object.getPrototypeOf(e)!==null;)e=Object.getPrototypeOf(e);return Object.getPrototypeOf(t)===e}function v(t){return T(t)&&t.nodeType===1&&!V(t)}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class P extends u(h){static get editorName(){return"DecoupledEditor"}ui;constructor(e,i={}){if(!s(e)&&i.initialData!==void 0)throw new a("editor-create-initial-data",null);super(i),this.config.get("initialData")===void 0&&this.config.set("initialData",R(e)),s(e)&&(this.sourceElement=e,g(this,e)),this.model.document.createRoot();const n=!this.config.get("toolbar.shouldNotGroupWhenFull"),o=new c(this.locale,this.editing.view,{editableElement:this.sourceElement,shouldToolbarGroupWhenFull:n,label:this.config.get("label")});this.ui=new d(this,o)}destroy(){const e=this.getData();return this.ui.destroy(),super.destroy().then(()=>{this.sourceElement&&this.updateSourceElement(e)})}static create(e,i={}){return new Promise(n=>{if(s(e)&&e.tagName==="TEXTAREA")throw new a("editor-wrong-element",null);const o=new this(e,i);n(o.initPlugins().then(()=>o.ui.init()).then(()=>o.data.init(o.config.get("initialData"))).then(()=>o.fire("ready")).then(()=>o))})}}function R(t){return s(t)?f(t):t}function s(t){return v(t)}export{P as DecoupledEditor,d as DecoupledEditorUI,c as DecoupledEditorUIView};
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import{Command as m,Plugin as E}from"@ckeditor/ckeditor5-core";import{Observer as k,BubblingEventInfo as S,ViewDocumentDomEventData as y}from"@ckeditor/ckeditor5-engine";import{env as C}from"@ckeditor/ckeditor5-utils";/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/function*d(s,e){for(const t of e)t&&s.getAttributeProperties(t[0]).copyOnEnter&&(yield t)}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class p extends m{execute(){this.editor.model.change(e=>{this.enterBlock(e),this.fire("afterExecute",{writer:e})})}enterBlock(e){const t=this.editor.model,n=t.document.selection,i=t.schema,o=n.isCollapsed,r=n.getFirstRange(),a=r.start.parent,c=r.end.parent;if(i.isLimit(a)||i.isLimit(c))return!o&&a==c&&t.deleteContent(n),!1;if(o){const l=d(e.model.schema,n.getAttributes());return h(e,r.start),e.setSelectionAttribute(l),!0}else{const l=!(r.start.isAtStart&&r.end.isAtEnd),v=a==c;if(t.deleteContent(n,{leaveUnmerged:l}),l){if(v)return h(e,n.focus),!0;e.setSelection(c,0)}}return!1}}function h(s,e){s.split(e),s.setSelection(e.parent.nextSibling,0)}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/const T={insertParagraph:{isSoft:!1},insertLineBreak:{isSoft:!0}};class f extends k{constructor(e){super(e);const t=this.document;let n=!1;t.on("keydown",(i,o)=>{n=o.shiftKey}),t.on("beforeinput",(i,o)=>{if(!this.isEnabled)return;let r=o.inputType;C.isSafari&&n&&r=="insertParagraph"&&(r="insertLineBreak");const a=o.domEvent,c=T[r];if(!c)return;const l=new S(t,"enter",o.targetRanges[0]);t.fire(l,new y(e,a,{isSoft:c.isSoft})),l.stop.called&&i.stop()})}observe(){}stopObserving(){}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class x extends E{static get pluginName(){return"Enter"}static get isOfficialPlugin(){return!0}init(){const e=this.editor,t=e.editing.view,n=t.document,i=this.editor.t;t.addObserver(f),e.commands.add("enter",new p(e)),this.listenTo(n,"enter",(o,r)=>{n.isComposing||r.preventDefault(),!r.isSoft&&(e.execute("enter"),t.scrollToTheSelection())},{priority:"low"}),e.accessibility.addKeystrokeInfos({keystrokes:[{label:i("Insert a hard break (a new paragraph)"),keystroke:"Enter"}]})}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class g extends m{execute(){const e=this.editor.model,t=e.document;e.change(n=>{B(e,n,t.selection),this.fire("afterExecute",{writer:n})})}refresh(){const e=this.editor.model,t=e.document;this.isEnabled=A(e.schema,t.selection)}}function A(s,e){if(e.rangeCount>1)return!1;const t=e.anchor;if(!t||!s.checkChild(t,"softBreak"))return!1;const n=e.getFirstRange(),i=n.start.parent,o=n.end.parent;return!((u(i,s)||u(o,s))&&i!==o)}function B(s,e,t){const n=t.isCollapsed,i=t.getFirstRange(),o=i.start.parent,r=i.end.parent,a=o==r;if(n){const c=d(s.schema,t.getAttributes());b(s,e,i.end),e.removeSelectionAttribute(t.getAttributeKeys()),e.setSelectionAttribute(c)}else{const c=!(i.start.isAtStart&&i.end.isAtEnd);s.deleteContent(t,{leaveUnmerged:c}),a?b(s,e,t.focus):c&&e.setSelection(r,0)}}function b(s,e,t){const n=e.createElement("softBreak");s.insertContent(n,t),e.setSelection(n,"after")}function u(s,e){return s.is("rootElement")?!1:e.isLimit(s)||u(s.parent,e)}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class O extends E{static get pluginName(){return"ShiftEnter"}static get isOfficialPlugin(){return!0}init(){const e=this.editor,t=e.model.schema,n=e.conversion,i=e.editing.view,o=i.document,r=this.editor.t;t.register("softBreak",{allowWhere:"$text",isInline:!0}),n.for("upcast").elementToElement({model:"softBreak",view:"br"}),n.for("downcast").elementToElement({model:"softBreak",view:(a,{writer:c})=>c.createEmptyElement("br")}),i.addObserver(f),e.commands.add("shiftEnter",new g(e)),this.listenTo(o,"enter",(a,c)=>{o.isComposing||c.preventDefault(),c.isSoft&&(e.execute("shiftEnter"),i.scrollToTheSelection())},{priority:"low"}),e.accessibility.addKeystrokeInfos({keystrokes:[{label:r("Insert a soft break (a <code><br></code> element)"),keystroke:"Shift+Enter"}]})}}export{x as Enter,p as EnterCommand,f as EnterObserver,O as ShiftEnter,g as ShiftEnterCommand,d as _getCopyOnEnterAttributes};
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import{Plugin as t}from"@ckeditor/ckeditor5-core";import{Clipboard as r}from"@ckeditor/ckeditor5-clipboard";import{Enter as i,ShiftEnter as e}from"@ckeditor/ckeditor5-enter";import{SelectAll as o}from"@ckeditor/ckeditor5-select-all";import{Typing as s}from"@ckeditor/ckeditor5-typing";import{Undo as m}from"@ckeditor/ckeditor5-undo";import{AccessibilityHelp as l}from"@ckeditor/ckeditor5-ui";/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class n extends t{static get requires(){return[l,r,i,o,e,s,m]}static get pluginName(){return"Essentials"}static get isOfficialPlugin(){return!0}}export{n as Essentials};
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import{Command as ae,Plugin as h}from"@ckeditor/ckeditor5-core";import{ModelDocumentSelection as ce,isLengthStyleValue as ue,isPercentageStyleValue as me,addBackgroundStylesRules as de}from"@ckeditor/ckeditor5-engine";import{ColorSelectorView as T,createDropdown as S,addListToDropdown as L,MenuBarMenuView as z,MenuBarMenuListView as E,MenuBarMenuListItemView as _,MenuBarMenuListItemButtonView as D,UIModel as I,normalizeColorOptions as fe,getLocalizedColorOptions as pe,focusChildOnDropdownOpen as ge}from"@ckeditor/ckeditor5-ui";import{Collection as $,CKEditorError as R}from"@ckeditor/ckeditor5-utils";import{IconFontFamily as U,IconFontSize as M,IconFontColor as be,IconFontBackground as he}from"@ckeditor/ckeditor5-icons";/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class C extends ae{attributeKey;constructor(e,o){super(e),this.attributeKey=o}refresh(){const e=this.editor.model,o=e.document;this.value=o.selection.getAttribute(this.attributeKey),this.isEnabled=e.schema.checkAttributeInSelection(o.selection,this.attributeKey)}execute(e={}){const o=this.editor.model,i=o.document.selection,r=e.value,u=e.batch,a=s=>{if(i.isCollapsed)r?s.setSelectionAttribute(this.attributeKey,r):s.removeSelectionAttribute(this.attributeKey);else{const m=o.schema.getValidRanges(i.getRanges(),this.attributeKey,{includeEmptyRanges:!0});for(const d of m){let l=d,c=this.attributeKey;d.isCollapsed&&(l=d.start.parent,c=ce._getStoreAttributeKey(this.attributeKey)),r?s.setAttribute(c,r,l):s.removeAttribute(c,l)}}};u?o.enqueueChange(u,s=>{a(s)}):o.change(s=>{a(s)})}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/const p="fontSize",g="fontFamily",w="fontColor",y="fontBackgroundColor";function O(t,e){const o={model:{key:t,values:[]},view:{},upcastAlso:{}};for(const n of e)o.model.values.push(n.model),o.view[n.model]=n.view,n.upcastAlso&&(o.upcastAlso[n.model]=n.upcastAlso);return o}function k(t){return e=>we(e.getStyle(t))}function N(t){return(e,{writer:o})=>o.createAttributeElement("span",{style:`${t}:${e}`},{priority:7})}function G({dropdownView:t,colors:e,columns:o,removeButtonLabel:n,colorPickerLabel:i,documentColorsLabel:r,documentColorsCount:u,colorPickerViewConfig:a}){const s=t.locale,m=new T(s,{colors:e,columns:o,removeButtonLabel:n,colorPickerLabel:i,documentColorsLabel:r,documentColorsCount:u,colorPickerViewConfig:a});return t.colorSelectorView=m,t.panelView.children.add(m),m}function we(t){return t.replace(/\s/g,"")}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class K extends C{constructor(e){super(e,g)}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/function A(t){return t.map(ye).filter(e=>e!==void 0)}function V(t){return t.replace(/["']/g,"").split(",").map(e=>e.trim())}function ye(t){if(typeof t=="object")return t;if(t==="default")return{title:"Default",model:void 0};if(typeof t=="string")return ve(t)}function ve(t){const e=V(t),o=e[0],n=e.map(Ce).join(", ");return{title:o,model:n,view:{name:"span",styles:{"font-family":n},priority:7}}}function Ce(t){return t=t.trim(),t.indexOf(" ")>0&&(t=`'${t}'`),t}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class q extends h{static get pluginName(){return"FontFamilyEditing"}static get isOfficialPlugin(){return!0}constructor(e){super(e),e.config.define(g,{options:["default","Arial, Helvetica, sans-serif","Courier New, Courier, monospace","Georgia, serif","Lucida Sans Unicode, Lucida Grande, sans-serif","Tahoma, Geneva, sans-serif","Times New Roman, Times, serif","Trebuchet MS, Helvetica, sans-serif","Verdana, Geneva, sans-serif"],supportAllValues:!1})}init(){const e=this.editor;e.model.schema.extend("$text",{allowAttributes:g}),e.model.schema.setAttributeProperties(g,{isFormatting:!0,copyOnEnter:!0});const o=A(e.config.get("fontFamily.options")).filter(i=>i.model),n=O(g,o);e.config.get("fontFamily.supportAllValues")?(this._prepareAnyValueConverters(),this._prepareCompatibilityConverter()):e.conversion.attributeToElement(n),e.commands.add(g,new K(e))}_prepareAnyValueConverters(){const e=this.editor;e.conversion.for("downcast").attributeToElement({model:g,view:(o,{writer:n})=>n.createAttributeElement("span",{style:"font-family:"+o},{priority:7})}),e.conversion.for("upcast").elementToAttribute({model:{key:g,value:o=>o.getStyle("font-family")},view:{name:"span",styles:{"font-family":/.*/}}})}_prepareCompatibilityConverter(){this.editor.conversion.for("upcast").elementToAttribute({view:{name:"font",attributes:{face:/.*/}},model:{key:g,value:o=>o.getAttribute("face")}})}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class j extends h{static get pluginName(){return"FontFamilyUI"}static get isOfficialPlugin(){return!0}init(){const e=this.editor,o=e.t,n=this._getLocalizedOptions(),i=e.commands.get(g),r=o("Font Family"),u=Fe(n,i);e.ui.componentFactory.add(g,a=>{const s=S(a);return L(s,u,{role:"menu",ariaLabel:r}),s.buttonView.set({label:r,icon:U,tooltip:!0}),s.extendTemplate({attributes:{class:"ck-font-family-dropdown"}}),s.bind("isEnabled").to(i),this.listenTo(s,"execute",m=>{e.execute(m.source.commandName,{value:m.source.commandParam}),e.editing.view.focus()}),s}),e.ui.componentFactory.add(`menuBar:${g}`,a=>{const s=new z(a);s.buttonView.set({label:r,icon:U}),s.bind("isEnabled").to(i);const m=new E(a);for(const d of u){const l=new _(a,s),c=new D(a);c.set({role:"menuitemradio",isToggleable:!0}),c.bind(...Object.keys(d.model)).to(d.model),c.delegate("execute").to(s),c.on("execute",()=>{e.execute(d.model.commandName,{value:d.model.commandParam}),e.editing.view.focus()}),l.children.add(c),m.items.add(l)}return s.panelView.children.add(m),s})}_getLocalizedOptions(){const e=this.editor,o=e.t;return A(e.config.get(g).options).map(i=>(i.title==="Default"&&(i.title=o("Default")),i))}}function Fe(t,e){const o=new $;for(const n of t){const i={type:"button",model:new I({commandName:g,commandParam:n.model,label:n.title,role:"menuitemradio",withText:!0})};i.model.bind("isOn").to(e,"value",r=>{if(r===n.model)return!0;if(!r||!n.model)return!1;const u=V(r)[0].toLowerCase(),a=V(n.model)[0].toLowerCase();return u===a}),n.view&&typeof n.view!="string"&&n.view.styles&&i.model.set("labelStyle",`font-family: ${n.view.styles["font-family"]}`),o.add(i)}return o}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class H extends h{static get requires(){return[q,j]}static get pluginName(){return"FontFamily"}static get isOfficialPlugin(){return!0}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class Y extends C{constructor(e){super(e,p)}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/function F(t){return t.map(e=>xe(e)).filter(e=>e!==void 0)}const W={get tiny(){return{title:"Tiny",model:"tiny",view:{name:"span",classes:"text-tiny",priority:7}}},get small(){return{title:"Small",model:"small",view:{name:"span",classes:"text-small",priority:7}}},get big(){return{title:"Big",model:"big",view:{name:"span",classes:"text-big",priority:7}}},get huge(){return{title:"Huge",model:"huge",view:{name:"span",classes:"text-huge",priority:7}}}};function xe(t){if(typeof t=="number"&&(t=String(t)),typeof t=="object"&&Oe(t))return B(t);const e=ze(t);if(e)return B(e);if(t==="default")return{model:void 0,title:"Default"};if(!ke(t))return Se(t)}function Se(t){return typeof t=="string"&&(t={title:t,model:`${parseFloat(t)}px`}),t.view={name:"span",styles:{"font-size":t.model}},B(t)}function B(t){return t.view&&typeof t.view!="string"&&!t.view.priority&&(t.view.priority=7),t}function ze(t){return typeof t=="string"?W[t]:W[t.model]}function Oe(t){return t.title&&t.model&&t.view}function ke(t){let e;if(typeof t=="object")if(t.model)e=parseFloat(t.model);else throw new R("font-size-invalid-definition",null,t);else e=parseFloat(t);return isNaN(e)}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/const Z=["x-small","x-small","small","medium","large","x-large","xx-large","xxx-large"];class J extends h{static get pluginName(){return"FontSizeEditing"}static get isOfficialPlugin(){return!0}constructor(e){super(e),e.config.define(p,{options:["tiny","small","default","big","huge"],supportAllValues:!1})}init(){const e=this.editor;e.model.schema.extend("$text",{allowAttributes:p}),e.model.schema.setAttributeProperties(p,{isFormatting:!0,copyOnEnter:!0});const o=e.config.get("fontSize.supportAllValues"),n=F(this.editor.config.get("fontSize.options")).filter(r=>r.model),i=O(p,n);o?(this._prepareAnyValueConverters(i),this._prepareCompatibilityConverter()):e.conversion.attributeToElement(i),e.commands.add(p,new Y(e))}_prepareAnyValueConverters(e){const o=this.editor,n=e.model.values.filter(i=>!ue(String(i))&&!me(String(i)));if(n.length)throw new R("font-size-invalid-use-of-named-presets",null,{presets:n});o.conversion.for("downcast").attributeToElement({model:p,view:(i,{writer:r})=>{if(i)return r.createAttributeElement("span",{style:"font-size:"+i},{priority:7})}}),o.conversion.for("upcast").elementToAttribute({model:{key:p,value:i=>i.getStyle("font-size")},view:{name:"span",styles:{"font-size":/.*/}}})}_prepareCompatibilityConverter(){this.editor.conversion.for("upcast").elementToAttribute({view:{name:"font",attributes:{size:/^[+-]?\d{1,3}$/}},model:{key:p,value:o=>{const n=o.getAttribute("size"),i=n[0]==="-"||n[0]==="+";let r=parseInt(n,10);i&&(r=3+r);const u=Z.length-1,a=Math.min(Math.max(r,0),u);return Z[a]}}})}}function Ne(t,{insertAt:e}={}){if(typeof document>"u")return;const o=document.head||document.getElementsByTagName("head")[0],n=document.createElement("style");n.type="text/css",window.litNonce&&n.setAttribute("nonce",window.litNonce),e==="top"&&o.firstChild?o.insertBefore(n,o.firstChild):o.appendChild(n),n.styleSheet?n.styleSheet.cssText=t:n.appendChild(document.createTextNode(t))}Ne(":root{--ck-content-font-size-tiny:0.7em;--ck-content-font-size-small:0.85em;--ck-content-font-size-big:1.4em;--ck-content-font-size-huge:1.8em}.ck-content .text-tiny{font-size:var(--ck-content-font-size-tiny)}.ck-content .text-small{font-size:var(--ck-content-font-size-small)}.ck-content .text-big{font-size:var(--ck-content-font-size-big)}.ck-content .text-huge{font-size:var(--ck-content-font-size-huge)}");/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class Q extends h{static get pluginName(){return"FontSizeUI"}static get isOfficialPlugin(){return!0}init(){const e=this.editor,o=e.t,n=this._getLocalizedOptions(),i=e.commands.get(p),r=o("Font Size"),u=Ae(n,i);e.ui.componentFactory.add(p,a=>{const s=S(a);return L(s,u,{role:"menu",ariaLabel:r}),s.buttonView.set({label:r,icon:M,tooltip:!0}),s.extendTemplate({attributes:{class:["ck-font-size-dropdown"]}}),s.bind("isEnabled").to(i),this.listenTo(s,"execute",m=>{e.execute(m.source.commandName,{value:m.source.commandParam}),e.editing.view.focus()}),s}),e.ui.componentFactory.add(`menuBar:${p}`,a=>{const s=new z(a);s.buttonView.set({label:r,icon:M}),s.bind("isEnabled").to(i);const m=new E(a);for(const d of u){const l=new _(a,s),c=new D(a);c.set({role:"menuitemradio",isToggleable:!0}),c.bind(...Object.keys(d.model)).to(d.model),c.delegate("execute").to(s),c.on("execute",()=>{e.execute(d.model.commandName,{value:d.model.commandParam}),e.editing.view.focus()}),l.children.add(c),m.items.add(l)}return s.panelView.children.add(m),s})}_getLocalizedOptions(){const e=this.editor,o=e.t,n={Default:o("Default"),Tiny:o("Tiny"),Small:o("Small"),Big:o("Big"),Huge:o("Huge")};return F(e.config.get(p).options).map(r=>{const u=n[r.title];return u&&u!=r.title&&(r=Object.assign({},r,{title:u})),r})}}function Ae(t,e){const o=new $;for(const n of t){const i={type:"button",model:new I({commandName:p,commandParam:n.model,label:n.title,class:"ck-fontsize-option",role:"menuitemradio",withText:!0})};n.view&&typeof n.view!="string"&&(n.view.styles&&i.model.set("labelStyle",`font-size:${n.view.styles["font-size"]}`),n.view.classes&&i.model.set("class",`${i.model.class} ${n.view.classes}`)),i.model.bind("isOn").to(e,"value",r=>r===n.model),o.add(i)}return o}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class X extends h{static get requires(){return[J,Q]}static get pluginName(){return"FontSize"}static get isOfficialPlugin(){return!0}normalizeSizeOptions(e){return F(e)}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class ee extends C{constructor(e){super(e,w)}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class te extends h{static get pluginName(){return"FontColorEditing"}static get isOfficialPlugin(){return!0}constructor(e){super(e),e.config.define(w,{colors:[{color:"hsl(0, 0%, 0%)",label:"Black"},{color:"hsl(0, 0%, 30%)",label:"Dim grey"},{color:"hsl(0, 0%, 60%)",label:"Grey"},{color:"hsl(0, 0%, 90%)",label:"Light grey"},{color:"hsl(0, 0%, 100%)",label:"White",hasBorder:!0},{color:"hsl(0, 75%, 60%)",label:"Red"},{color:"hsl(30, 75%, 60%)",label:"Orange"},{color:"hsl(60, 75%, 60%)",label:"Yellow"},{color:"hsl(90, 75%, 60%)",label:"Light green"},{color:"hsl(120, 75%, 60%)",label:"Green"},{color:"hsl(150, 75%, 60%)",label:"Aquamarine"},{color:"hsl(180, 75%, 60%)",label:"Turquoise"},{color:"hsl(210, 75%, 60%)",label:"Light blue"},{color:"hsl(240, 75%, 60%)",label:"Blue"},{color:"hsl(270, 75%, 60%)",label:"Purple"}],columns:5}),e.conversion.for("upcast").elementToAttribute({view:{name:"span",styles:{color:/[\s\S]+/}},model:{key:w,value:k("color")}}),e.conversion.for("upcast").elementToAttribute({view:{name:"font",attributes:{color:/^#?\w+$/}},model:{key:w,value:o=>o.getAttribute("color")}}),e.conversion.for("downcast").attributeToElement({model:w,view:N("color")}),e.commands.add(w,new ee(e)),e.model.schema.extend("$text",{allowAttributes:w}),e.model.schema.setAttributeProperties(w,{isFormatting:!0,copyOnEnter:!0})}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class P extends h{commandName;componentName;icon;dropdownLabel;columns;constructor(e,{commandName:o,componentName:n,icon:i,dropdownLabel:r}){super(e),this.commandName=o,this.componentName=n,this.icon=i,this.dropdownLabel=r,this.columns=e.config.get(`${this.componentName}.columns`)}init(){const e=this.editor,o=e.locale,n=o.t,i=e.commands.get(this.commandName),r=e.config.get(this.componentName),u=fe(r.colors),a=pe(o,u),s=r.documentColors,m=r.colorPicker!==!1;e.ui.componentFactory.add(this.componentName,d=>{const l=S(d);let c=!1;const f=G({dropdownView:l,colors:a.map(b=>({label:b.label,color:b.model,options:{hasBorder:b.hasBorder}})),columns:this.columns,removeButtonLabel:n("Remove color"),colorPickerLabel:n("Color picker"),documentColorsLabel:s!==0?n("Document colors"):"",documentColorsCount:s===void 0?this.columns:s,colorPickerViewConfig:m?r.colorPicker||{}:!1});return f.bind("selectedColor").to(i,"value"),l.buttonView.set({label:this.dropdownLabel,icon:this.icon,tooltip:!0}),l.extendTemplate({attributes:{class:"ck-color-ui-dropdown"}}),l.bind("isEnabled").to(i),f.on("execute",(b,v)=>{l.isOpen&&e.execute(this.commandName,{value:v.value,batch:this._undoStepBatch}),v.source!=="colorPicker"&&e.editing.view.focus(),v.source==="colorPickerSaveButton"&&(l.isOpen=!1)}),f.on("colorPicker:show",()=>{this._undoStepBatch=e.model.createBatch()}),f.on("colorPicker:cancel",()=>{this._undoStepBatch.operations.length&&(l.isOpen=!1,e.execute("undo",this._undoStepBatch)),e.editing.view.focus()}),l.on("change:isOpen",(b,v,x)=>{c||(c=!0,l.colorSelectorView.appendUI()),x&&(s!==0&&f.updateDocumentColors(e.model,this.componentName),f.updateSelectedColors(),f.showColorGridsFragment())}),ge(l,()=>l.colorSelectorView.colorGridsFragmentView.staticColorsGrid.items.find(b=>b.isOn)),l}),e.ui.componentFactory.add(`menuBar:${this.componentName}`,d=>{const l=new z(d);l.buttonView.set({label:this.dropdownLabel,icon:this.icon}),l.bind("isEnabled").to(i);let c=!1;const f=new T(d,{colors:a.map(b=>({label:b.label,color:b.model,options:{hasBorder:b.hasBorder}})),columns:this.columns,removeButtonLabel:n("Remove color"),colorPickerLabel:n("Color picker"),documentColorsLabel:s!==0?n("Document colors"):"",documentColorsCount:s===void 0?this.columns:s,colorPickerViewConfig:!1});return f.bind("selectedColor").to(i,"value"),f.delegate("execute").to(l),f.on("execute",(b,v)=>{e.execute(this.commandName,{value:v.value,batch:this._undoStepBatch}),e.editing.view.focus()}),l.on("change:isOpen",(b,v,x)=>{c||(c=!0,f.appendUI()),x&&(s!==0&&f.updateDocumentColors(e.model,this.componentName),f.updateSelectedColors(),f.showColorGridsFragment())}),l.panelView.children.add(f),l})}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class oe extends P{constructor(e){const o=e.locale.t;super(e,{commandName:w,componentName:w,icon:be,dropdownLabel:o("Font Color")})}static get pluginName(){return"FontColorUI"}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class ne extends h{static get requires(){return[te,oe]}static get pluginName(){return"FontColor"}static get isOfficialPlugin(){return!0}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class ie extends C{constructor(e){super(e,y)}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class se extends h{static get pluginName(){return"FontBackgroundColorEditing"}static get isOfficialPlugin(){return!0}constructor(e){super(e),e.config.define(y,{colors:[{color:"hsl(0, 0%, 0%)",label:"Black"},{color:"hsl(0, 0%, 30%)",label:"Dim grey"},{color:"hsl(0, 0%, 60%)",label:"Grey"},{color:"hsl(0, 0%, 90%)",label:"Light grey"},{color:"hsl(0, 0%, 100%)",label:"White",hasBorder:!0},{color:"hsl(0, 75%, 60%)",label:"Red"},{color:"hsl(30, 75%, 60%)",label:"Orange"},{color:"hsl(60, 75%, 60%)",label:"Yellow"},{color:"hsl(90, 75%, 60%)",label:"Light green"},{color:"hsl(120, 75%, 60%)",label:"Green"},{color:"hsl(150, 75%, 60%)",label:"Aquamarine"},{color:"hsl(180, 75%, 60%)",label:"Turquoise"},{color:"hsl(210, 75%, 60%)",label:"Light blue"},{color:"hsl(240, 75%, 60%)",label:"Blue"},{color:"hsl(270, 75%, 60%)",label:"Purple"}],columns:5}),e.data.addStyleProcessorRules(de),e.conversion.for("upcast").elementToAttribute({view:{name:"span",styles:{"background-color":/[\s\S]+/}},model:{key:y,value:k("background-color")}}),e.conversion.for("downcast").attributeToElement({model:y,view:N("background-color")}),e.commands.add(y,new ie(e)),e.model.schema.extend("$text",{allowAttributes:y}),e.model.schema.setAttributeProperties(y,{isFormatting:!0,copyOnEnter:!0})}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class re extends P{constructor(e){const o=e.locale.t;super(e,{commandName:y,componentName:y,icon:he,dropdownLabel:o("Font Background Color")})}static get pluginName(){return"FontBackgroundColorUI"}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class le extends h{static get requires(){return[se,re]}static get pluginName(){return"FontBackgroundColor"}static get isOfficialPlugin(){return!0}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class Ve extends h{static get requires(){return[H,X,ne,le]}static get pluginName(){return"Font"}static get isOfficialPlugin(){return!0}}export{Ve as Font,le as FontBackgroundColor,ie as FontBackgroundColorCommand,se as FontBackgroundColorEditing,re as FontBackgroundColorUI,ne as FontColor,ee as FontColorCommand,te as FontColorEditing,oe as FontColorUI,P as FontColorUIBase,C as FontCommand,H as FontFamily,K as FontFamilyCommand,q as FontFamilyEditing,j as FontFamilyUI,X as FontSize,Y as FontSizeCommand,J as FontSizeEditing,Q as FontSizeUI,G as _addFontColorSelectorToDropdown,O as _buildFontDefinition,A as _normalizeFontFamilyOptions,F as _normalizeFontSizeOptions,N as _renderDowncastFontElement,k as _renderUpcastFontColorAttribute};
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import{Command as B,Plugin as b}from"@ckeditor/ckeditor5-core";import{ModelDocumentSelection as E}from"@ckeditor/ckeditor5-engine";import{IconEraser as f,IconMarker as H,IconPen as M}from"@ckeditor/ckeditor5-icons";import{ButtonView as T,createDropdown as A,SplitButtonView as C,addToolbarToDropdown as I,MenuBarMenuView as S,MenuBarMenuListView as O,MenuBarMenuListItemView as w,MenuBarMenuListItemButtonView as v,ListSeparatorView as P,ToolbarSeparatorView as R}from"@ckeditor/ckeditor5-ui";/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class V extends B{refresh(){const e=this.editor.model,t=e.document;this.value=t.selection.getAttribute("highlight"),this.isEnabled=e.schema.checkAttributeInSelection(t.selection,"highlight")}execute(e={}){const t=this.editor.model,h=t.document.selection,o=e.value;t.change(i=>{if(h.isCollapsed){const a=h.getFirstPosition();if(h.hasAttribute("highlight")){const r=d=>d.item.hasAttribute("highlight")&&d.item.getAttribute("highlight")===this.value,l=a.getLastMatchingPosition(r,{direction:"backward"}),n=a.getLastMatchingPosition(r),u=i.createRange(l,n);!o||this.value===o?(a.isEqual(n)||i.removeAttribute("highlight",u),i.removeSelectionAttribute("highlight")):(a.isEqual(n)||i.setAttribute("highlight",o,u),i.setSelectionAttribute("highlight",o))}else o&&i.setSelectionAttribute("highlight",o)}else{const a=t.schema.getValidRanges(h.getRanges(),"highlight",{includeEmptyRanges:!0});for(const r of a){let l=r,n="highlight";r.isCollapsed&&(l=r.start.parent,n=E._getStoreAttributeKey("highlight")),o?i.setAttribute(n,o,l):i.removeAttribute(n,l)}}})}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class y extends b{static get pluginName(){return"HighlightEditing"}static get isOfficialPlugin(){return!0}constructor(e){super(e),e.config.define("highlight",{options:[{model:"yellowMarker",class:"marker-yellow",title:"Yellow marker",color:"var(--ck-content-highlight-marker-yellow)",type:"marker"},{model:"greenMarker",class:"marker-green",title:"Green marker",color:"var(--ck-content-highlight-marker-green)",type:"marker"},{model:"pinkMarker",class:"marker-pink",title:"Pink marker",color:"var(--ck-content-highlight-marker-pink)",type:"marker"},{model:"blueMarker",class:"marker-blue",title:"Blue marker",color:"var(--ck-content-highlight-marker-blue)",type:"marker"},{model:"redPen",class:"pen-red",title:"Red pen",color:"var(--ck-content-highlight-pen-red)",type:"pen"},{model:"greenPen",class:"pen-green",title:"Green pen",color:"var(--ck-content-highlight-pen-green)",type:"pen"}]})}init(){const e=this.editor;e.model.schema.extend("$text",{allowAttributes:"highlight"});const t=e.config.get("highlight.options");e.conversion.attributeToElement(_(t)),e.commands.add("highlight",new V(e))}}function _(g){const e={model:{key:"highlight",values:[]},view:{}};for(const t of g)e.model.values.push(t.model),e.view[t.model]={name:"mark",classes:t.class};return e}function D(g,{insertAt:e}={}){if(typeof document>"u")return;const t=document.head||document.getElementsByTagName("head")[0],c=document.createElement("style");c.type="text/css",window.litNonce&&c.setAttribute("nonce",window.litNonce),e==="top"&&t.firstChild?t.insertBefore(c,t.firstChild):t.appendChild(c),c.styleSheet?c.styleSheet.cssText=g:c.appendChild(document.createTextNode(g))}D(":root{--ck-content-highlight-marker-yellow:#fdfd77;--ck-content-highlight-marker-green:#62f962;--ck-content-highlight-marker-pink:#fc7899;--ck-content-highlight-marker-blue:#72ccfd;--ck-content-highlight-pen-red:#e71313;--ck-content-highlight-pen-green:#128a00}.ck-content .marker-yellow{background-color:var(--ck-content-highlight-marker-yellow)}.ck-content .marker-green{background-color:var(--ck-content-highlight-marker-green)}.ck-content .marker-pink{background-color:var(--ck-content-highlight-marker-pink)}.ck-content .marker-blue{background-color:var(--ck-content-highlight-marker-blue)}.ck-content .pen-red{background-color:transparent;color:var(--ck-content-highlight-pen-red)}.ck-content .pen-green{background-color:transparent;color:var(--ck-content-highlight-pen-green)}");/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class x extends b{get localizedOptionTitles(){const e=this.editor.t;return{"Yellow marker":e("Yellow marker"),"Green marker":e("Green marker"),"Pink marker":e("Pink marker"),"Blue marker":e("Blue marker"),"Red pen":e("Red pen"),"Green pen":e("Green pen")}}static get pluginName(){return"HighlightUI"}static get isOfficialPlugin(){return!0}init(){const e=this.editor.config.get("highlight.options");for(const t of e)this._addHighlighterButton(t);this._addRemoveHighlightButton(),this._addDropdown(e),this._addMenuBarButton(e)}_addRemoveHighlightButton(){const e=this.editor.t,t=this.editor.commands.get("highlight");this._addButton("removeHighlight",e("Remove highlight"),f,null,c=>{c.bind("isEnabled").to(t,"isEnabled")})}_addHighlighterButton(e){const t=this.editor.commands.get("highlight");this._addButton("highlight:"+e.model,e.title,p(e.type),e.model,c);function c(h){h.bind("isEnabled").to(t,"isEnabled"),h.bind("isOn").to(t,"value",o=>o===e.model),h.iconView.fillColor=e.color,h.isToggleable=!0}}_addButton(e,t,c,h,o){const i=this.editor;i.ui.componentFactory.add(e,a=>{const r=new T(a),l=this.localizedOptionTitles[t]?this.localizedOptionTitles[t]:t;return r.set({label:l,icon:c,tooltip:!0}),r.on("execute",()=>{i.execute("highlight",{value:h}),i.editing.view.focus()}),o(r),r})}_addDropdown(e){const t=this.editor,c=t.t,h=t.ui.componentFactory,o=e[0],i=e.reduce((a,r)=>(a[r.model]=r,a),{});h.add("highlight",a=>{const r=t.commands.get("highlight"),l=A(a,C),n=l.buttonView;n.set({label:c("Highlight"),tooltip:!0,lastExecuted:o.model,commandValue:o.model,isToggleable:!0}),n.bind("icon").to(r,"value",s=>p(d(s,"type"))),n.bind("color").to(r,"value",s=>d(s,"color")),n.bind("commandValue").to(r,"value",s=>d(s,"model")),n.bind("isOn").to(r,"value",s=>!!s),n.delegate("execute").to(l);const u=()=>{const s=e.map(m=>{const k=h.create("highlight:"+m.model);return this.listenTo(k,"execute",()=>{l.buttonView.set({lastExecuted:m.model})}),k});return s.push(new R),s.push(h.create("removeHighlight")),s};l.bind("isEnabled").to(r,"isEnabled"),I(l,u,{enableActiveItemFocusOnDropdownOpen:!0,ariaLabel:c("Text highlight toolbar")}),F(l),n.on("execute",()=>{t.execute("highlight",{value:n.commandValue})}),this.listenTo(l,"execute",()=>{t.editing.view.focus()});function d(s,m){const k=!s||s===n.lastExecuted?n.lastExecuted:s;return i[k][m]}return l})}_addMenuBarButton(e){const t=this.editor,c=t.t,h=t.commands.get("highlight");t.ui.componentFactory.add("menuBar:highlight",o=>{const i=new S(o);i.buttonView.set({label:c("Highlight"),icon:p("marker")}),i.bind("isEnabled").to(h),i.buttonView.iconView.fillColor="transparent";const a=new O(o);for(const n of e){const u=new w(o,i),d=new v(o);d.set({label:n.title,icon:p(n.type),role:"menuitemradio",isToggleable:!0}),d.iconView.fillColor=n.color,d.delegate("execute").to(i),d.bind("isOn").to(h,"value",s=>s===n.model),d.on("execute",()=>{t.execute("highlight",{value:n.model}),t.editing.view.focus()}),u.children.add(d),a.items.add(u)}a.items.add(new P(o));const r=new w(o,i),l=new v(o);return l.set({label:c("Remove highlight"),icon:f}),l.delegate("execute").to(i),l.on("execute",()=>{t.execute("highlight",{value:null}),t.editing.view.focus()}),r.children.add(l),a.items.add(r),i.panelView.children.add(a),i})}}function F(g){g.buttonView.actionView.iconView.bind("fillColor").to(g.buttonView,"color")}function p(g){return g==="marker"?H:M}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class L extends b{static get requires(){return[y,x]}static get pluginName(){return"Highlight"}static get isOfficialPlugin(){return!0}}export{L as Highlight,V as HighlightCommand,y as HighlightEditing,x as HighlightUI};
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import{Command as h,Plugin as s}from"@ckeditor/ckeditor5-core";import{findOptimalInsertionRange as p,toWidget as g,Widget as f}from"@ckeditor/ckeditor5-widget";import{IconHorizontalLine as z}from"@ckeditor/ckeditor5-icons";import{ButtonView as L,MenuBarMenuListItemButtonView as E}from"@ckeditor/ckeditor5-ui";/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class c extends h{refresh(){const e=this.editor.model,t=e.schema,n=e.document.selection;this.isEnabled=y(n,t,e)}execute(){const e=this.editor.model;e.change(t=>{const n=t.createElement("horizontalLine");e.insertObject(n,null,null,{setSelection:"after"})})}}function y(o,e,t){const n=b(o,t);return e.checkChild(n,"horizontalLine")}function b(o,e){const n=p(o,e).start.parent;return n.isEmpty&&!n.is("element","$root")?n.parent:n}function w(o,{insertAt:e}={}){if(typeof document>"u")return;const t=document.head||document.getElementsByTagName("head")[0],n=document.createElement("style");n.type="text/css",window.litNonce&&n.setAttribute("nonce",window.litNonce),e==="top"&&t.firstChild?t.insertBefore(n,t.firstChild):t.appendChild(n),n.styleSheet?n.styleSheet.cssText=o:n.appendChild(document.createTextNode(o))}w(".ck-editor__editable .ck-horizontal-line{display:flow-root}.ck-content hr{background:#dedede;border:0;height:4px;margin:15px 0}");/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class d extends s{static get pluginName(){return"HorizontalLineEditing"}static get isOfficialPlugin(){return!0}init(){const e=this.editor,t=e.model.schema,n=e.t,l=e.conversion;t.register("horizontalLine",{inheritAllFrom:"$blockObject"}),l.for("dataDowncast").elementToElement({model:"horizontalLine",view:(r,{writer:i})=>i.createEmptyElement("hr")}),l.for("editingDowncast").elementToStructure({model:"horizontalLine",view:(r,{writer:i})=>{const u=n("Horizontal line"),a=i.createContainerElement("div",null,i.createEmptyElement("hr"));return i.addClass("ck-horizontal-line",a),i.setCustomProperty("hr",!0,a),x(a,i,u)}}),l.for("upcast").elementToElement({view:"hr",model:"horizontalLine"}),e.commands.add("horizontalLine",new c(e))}}function x(o,e,t){return e.setCustomProperty("horizontalLine",!0,o),g(o,e,{label:t})}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class m extends s{static get pluginName(){return"HorizontalLineUI"}static get isOfficialPlugin(){return!0}init(){const e=this.editor;e.ui.componentFactory.add("horizontalLine",()=>{const t=this._createButton(L);return t.set({tooltip:!0}),t}),e.ui.componentFactory.add("menuBar:horizontalLine",()=>this._createButton(E))}_createButton(e){const t=this.editor,n=t.locale,l=t.commands.get("horizontalLine"),r=new e(t.locale),i=n.t;return r.set({label:i("Horizontal line"),icon:z}),r.bind("isEnabled").to(l,"isEnabled"),this.listenTo(r,"execute",()=>{t.execute("horizontalLine"),t.editing.view.focus()}),r}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class H extends s{static get requires(){return[d,m,f]}static get pluginName(){return"HorizontalLine"}static get isOfficialPlugin(){return!0}}export{H as HorizontalLine,c as HorizontalLineCommand,d as HorizontalLineEditing,m as HorizontalLineUI};
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import{Command as h,Plugin as f}from"@ckeditor/ckeditor5-core";import{ModelDocumentSelection as C}from"@ckeditor/ckeditor5-engine";import{getLanguageDirection as A,Collection as P}from"@ckeditor/ckeditor5-utils";import{createDropdown as T,addListToDropdown as V,MenuBarMenuView as y,MenuBarMenuListView as v,ListSeparatorView as D,MenuBarMenuListItemView as M,MenuBarMenuListItemButtonView as I,UIModel as b}from"@ckeditor/ckeditor5-ui";/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/function m(c,e){return e=e||A(c),`${c}:${e}`}function x(c){const[e,t]=c.split(":");return{languageCode:e,textDirection:t}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class p extends h{refresh(){const e=this.editor.model,t=e.document;this.value=this._getValueFromFirstAllowedNode(),this.isEnabled=e.schema.checkAttributeInSelection(t.selection,"language")}execute({languageCode:e,textDirection:t}={}){const r=this.editor.model,o=r.document.selection,u=e?m(e,t):!1;r.change(g=>{if(o.isCollapsed)u?g.setSelectionAttribute("language",u):g.removeSelectionAttribute("language");else{const a=r.schema.getValidRanges(o.getRanges(),"language",{includeEmptyRanges:!0});for(const i of a){let n=i,l="language";i.isCollapsed&&(n=i.start.parent,l=C._getStoreAttributeKey("language")),u?g.setAttribute(l,u,n):g.removeAttribute(l,n)}}})}_getValueFromFirstAllowedNode(){const e=this.editor.model,t=e.schema,r=e.document.selection;if(r.isCollapsed)return r.getAttribute("language")||!1;for(const s of r.getRanges())for(const o of s.getItems())if(t.checkAttribute(o,"language"))return o.getAttribute("language")||!1;return!1}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class w extends f{static get pluginName(){return"TextPartLanguageEditing"}static get isOfficialPlugin(){return!0}constructor(e){super(e),e.config.define("language",{textPartLanguage:[{title:"Arabic",languageCode:"ar"},{title:"French",languageCode:"fr"},{title:"Spanish",languageCode:"es"}]})}init(){const e=this.editor;e.model.schema.extend("$text",{allowAttributes:"language"}),e.model.schema.setAttributeProperties("language",{copyOnEnter:!0}),this._defineConverters(),e.commands.add("textPartLanguage",new p(e))}_defineConverters(){const e=this.editor.conversion;e.for("upcast").elementToAttribute({model:{key:"language",value:t=>{const r=t.getAttribute("lang"),s=t.getAttribute("dir");return m(r,s)}},view:{name:"span",attributes:{lang:/[\s\S]+/}}}),e.for("downcast").attributeToElement({model:"language",view:(t,{writer:r},s)=>{if(!t||!s.item.is("$textProxy")&&!s.item.is("documentSelection"))return;const{languageCode:o,textDirection:u}=x(t);return r.createAttributeElement("span",{lang:o,dir:u})}})}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class L extends f{static get pluginName(){return"TextPartLanguageUI"}static get isOfficialPlugin(){return!0}init(){const e=this.editor,t=e.t,r=t("Choose language"),s=t("Language");e.ui.componentFactory.add("textPartLanguage",o=>{const{definitions:u,titles:g}=this._getItemMetadata(),a=e.commands.get("textPartLanguage"),i=T(o);return V(i,u,{ariaLabel:s,role:"menu"}),i.buttonView.set({ariaLabel:s,ariaLabelledBy:void 0,isOn:!1,withText:!0,tooltip:s}),i.extendTemplate({attributes:{class:["ck-text-fragment-language-dropdown"]}}),i.bind("isEnabled").to(a,"isEnabled"),i.buttonView.bind("label").to(a,"value",n=>n&&g[n]||r),i.buttonView.bind("ariaLabel").to(a,"value",n=>{const l=n&&g[n];return l?`${l}, ${s}`:s}),this.listenTo(i,"execute",n=>{a.execute({languageCode:n.source.languageCode,textDirection:n.source.textDirection}),e.editing.view.focus()}),i}),e.ui.componentFactory.add("menuBar:textPartLanguage",o=>{const{definitions:u}=this._getItemMetadata(),g=e.commands.get("textPartLanguage"),a=new y(o);a.buttonView.set({label:s});const i=new v(o);i.set({ariaLabel:t("Language"),role:"menu"});for(const n of u){if(n.type!="button"){i.items.add(new D(o));continue}const l=new M(o,a),d=new I(o);d.set({role:"menuitemradio",isToggleable:!0}),d.bind(...Object.keys(n.model)).to(n.model),d.delegate("execute").to(a),l.children.add(d),i.items.add(l)}return a.bind("isEnabled").to(g,"isEnabled"),a.panelView.children.add(i),a.on("execute",n=>{g.execute({languageCode:n.source.languageCode,textDirection:n.source.textDirection}),e.editing.view.focus()}),a})}_getItemMetadata(){const e=this.editor,t=new P,r={},s=e.commands.get("textPartLanguage"),o=e.config.get("language.textPartLanguage"),u=e.locale.t,g=u("Remove language");t.add({type:"button",model:new b({label:g,languageCode:!1,withText:!0})}),t.add({type:"separator"});for(const a of o){const i={type:"button",model:new b({label:a.title,languageCode:a.languageCode,role:"menuitemradio",textDirection:a.textDirection,withText:!0})},n=m(a.languageCode,a.textDirection);i.model.bind("isOn").to(s,"value",l=>l===n),t.add(i),r[n]=a.title}return{definitions:t,titles:r}}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class E extends f{static get requires(){return[w,L]}static get pluginName(){return"TextPartLanguage"}static get isOfficialPlugin(){return!0}}export{E as TextPartLanguage,p as TextPartLanguageCommand,w as TextPartLanguageEditing,L as TextPartLanguageUI,x as _parseLanguageAttribute,m as _stringifyLanguageAttribute};
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import{Command as l,Plugin as d}from"@ckeditor/ckeditor5-core";import{first as u}from"@ckeditor/ckeditor5-utils";import{ButtonView as g}from"@ckeditor/ckeditor5-ui";import{IconParagraph as f}from"@ckeditor/ckeditor5-icons";/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class h extends l{constructor(e){super(e),this._isEnabledBasedOnSelection=!1}refresh(){const e=this.editor.model,a=e.document,r=u(a.selection.getSelectedBlocks());this.value=!!r&&r.is("element","paragraph"),this.isEnabled=!!r&&m(r,e.schema)}execute(e={}){const a=this.editor.model,r=a.document,t=e.selection||r.selection;a.canEditAt(t)&&a.change(n=>{const o=t.getSelectedBlocks();for(const c of o)!c.is("element","paragraph")&&m(c,a.schema)&&n.rename(c,"paragraph")})}}function m(s,e){return e.checkChild(s.parent,"paragraph")&&!e.isObject(s)}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class p extends l{constructor(e){super(e),this._isEnabledBasedOnSelection=!1}execute(e){const a=this.editor.model,r=e.attributes;let t=e.position;return a.canEditAt(t)?a.change(n=>{if(t=this._findPositionToInsertParagraph(t,n),!t)return null;const o=n.createElement("paragraph");return r&&a.schema.setAllowedAttributes(o,r,n),a.insertContent(o,t),n.setSelection(o,"in"),n.createPositionAt(o,0)}):null}_findPositionToInsertParagraph(e,a){const r=this.editor.model;if(r.schema.checkChild(e,"paragraph"))return e;const t=r.schema.findAllowedParent(e,"paragraph");if(!t)return null;const n=e.parent,o=r.schema.checkChild(n,"$text");return n.isEmpty||o&&e.isAtEnd?r.createPositionAfter(n):!n.isEmpty&&o&&e.isAtStart?r.createPositionBefore(n):a.split(e,t).position}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class i extends d{static get pluginName(){return"Paragraph"}static get isOfficialPlugin(){return!0}init(){const e=this.editor,a=e.model;e.commands.add("paragraph",new h(e)),e.commands.add("insertParagraph",new p(e)),a.schema.register("paragraph",{inheritAllFrom:"$block"}),e.conversion.elementToElement({model:"paragraph",view:"p"}),e.conversion.for("upcast").elementToElement({model:(r,{writer:t})=>!i.paragraphLikeElements.has(r.name)||r.isEmpty?null:t.createElement("paragraph"),view:/.+/,converterPriority:"low"})}static paragraphLikeElements=new Set(["blockquote","dd","div","dt","h1","h2","h3","h4","h5","h6","li","p","td","th"])}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class P extends d{static get requires(){return[i]}init(){const e=this.editor,a=e.t;e.ui.componentFactory.add("paragraph",r=>{const t=new g(r),n=e.commands.get("paragraph");return t.label=a("Paragraph"),t.icon=f,t.tooltip=!0,t.isToggleable=!0,t.bind("isEnabled").to(n),t.bind("isOn").to(n,"value"),t.on("execute",()=>{e.execute("paragraph")}),t})}}export{p as InsertParagraphCommand,i as Paragraph,P as ParagraphButtonUI,h as ParagraphCommand};
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import{Plugin as a,Command as f}from"@ckeditor/ckeditor5-core";import{IconRemoveFormat as F}from"@ckeditor/ckeditor5-icons";import{ButtonView as h,MenuBarMenuListItemButtonView as b}from"@ckeditor/ckeditor5-ui";import{first as c}from"@ckeditor/ckeditor5-utils";/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/const n="removeFormat";class u extends a{static get pluginName(){return"RemoveFormatUI"}static get isOfficialPlugin(){return!0}init(){const t=this.editor;t.ui.componentFactory.add(n,()=>{const e=this._createButton(h);return e.set({tooltip:!0}),e}),t.ui.componentFactory.add(`menuBar:${n}`,()=>this._createButton(b))}_createButton(t){const e=this.editor,i=e.locale,s=e.commands.get(n),o=new t(e.locale),r=i.t;return o.set({label:r("Remove Format"),icon:F}),o.bind("isEnabled").to(s,"isEnabled"),this.listenTo(o,"execute",()=>{e.execute(n),e.editing.view.focus()}),o}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class d extends f{_customAttributesHandlers=[];refresh(){const t=this.editor.model;this.isEnabled=!!c(this._getFormattingItems(t.document.selection))}execute(){const t=this.editor.model;t.change(e=>{for(const i of this._getFormattingItems(t.document.selection))if(i.is("selection"))for(const s of this._getFormattingAttributes(i))e.removeSelectionAttribute(s);else{const s=e.createRangeOn(i);for(const o of this._getFormattingAttributes(i))this._removeFormatting(o,i,s,e)}})}registerCustomAttribute(t,e){this._customAttributesHandlers.push({isFormatting:t,removeFormatting:e})}_removeFormatting(t,e,i,s){let o=!1;for(const{isFormatting:r,removeFormatting:g}of this._customAttributesHandlers)r(t,e)&&(g(t,i,s),o=!0);o||s.removeAttribute(t,i)}*_getFormattingItems(t){const e=this.editor.model,i=e.schema,s=o=>!!c(this._getFormattingAttributes(o));for(const o of t.getRanges())for(const r of o.getItems())i.isBlock(r)&&o.end.isTouching(e.createPositionAt(r,0))||s(r)&&(yield r);for(const o of t.getSelectedBlocks())s(o)&&(yield o);s(t)&&(yield t)}*_getFormattingAttributes(t){const e=this.editor.model.schema;for(const[i]of t.getAttributes()){for(const{isFormatting:o}of this._customAttributesHandlers)o(i,t)&&(yield i);const s=e.getAttributeProperties(i);s&&s.isFormatting&&(yield i)}}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class l extends a{static get pluginName(){return"RemoveFormatEditing"}static get licenseFeatureCode(){return"RF"}static get isOfficialPlugin(){return!0}static get isPremiumPlugin(){return!0}init(){const t=this.editor;t.commands.add("removeFormat",new d(t))}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class v extends a{static get requires(){return[l,u]}static get pluginName(){return"RemoveFormat"}static get isOfficialPlugin(){return!0}}export{v as RemoveFormat,d as RemoveFormatCommand,l as RemoveFormatEditing,u as RemoveFormatUI};
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import{Command as m,Plugin as o}from"@ckeditor/ckeditor5-core";import{getCode as g,parseKeystroke as f}from"@ckeditor/ckeditor5-utils";import{IconSelectAll as p}from"@ckeditor/ckeditor5-icons";import{ButtonView as A,MenuBarMenuListItemButtonView as h}from"@ckeditor/ckeditor5-ui";/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class r extends m{constructor(e){super(e),this.affectsData=!1}execute(){const e=this.editor.model,t=e.document.selection;let i=e.schema.getLimitElement(t);if(t.containsEntireContent(i)||!a(e.schema,i))do if(i=i.parent,!i)return;while(!a(e.schema,i));e.change(n=>{n.setSelection(i,"in")})}}function a(l,e){return l.isLimit(e)&&(l.checkChild(e,"$text")||l.checkChild(e,"paragraph"))}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/const S=f("Ctrl+A");class u extends o{static get pluginName(){return"SelectAllEditing"}static get isOfficialPlugin(){return!0}init(){const e=this.editor,t=e.t,n=e.editing.view.document;e.commands.add("selectAll",new r(e)),this.listenTo(n,"keydown",(c,s)=>{g(s)===S&&(e.execute("selectAll"),s.preventDefault())}),e.accessibility.addKeystrokeInfos({keystrokes:[{label:t("Select all"),keystroke:"CTRL+A"}]})}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class d extends o{static get pluginName(){return"SelectAllUI"}static get isOfficialPlugin(){return!0}init(){const e=this.editor;e.ui.componentFactory.add("selectAll",()=>{const t=this._createButton(A);return t.set({tooltip:!0}),t}),e.ui.componentFactory.add("menuBar:selectAll",()=>this._createButton(h))}_createButton(e){const t=this.editor,i=t.locale,n=t.commands.get("selectAll"),c=new e(t.locale),s=i.t;return c.set({label:s("Select all"),icon:p,keystroke:"Ctrl+A"}),c.bind("isEnabled").to(n,"isEnabled"),this.listenTo(c,"execute",()=>{t.execute("selectAll"),t.editing.view.focus()}),c}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class w extends o{static get requires(){return[u,d]}static get pluginName(){return"SelectAll"}static get isOfficialPlugin(){return!0}}export{w as SelectAll,r as SelectAllCommand,u as SelectAllEditing,d as SelectAllUI};
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import{Plugin as h,Command as E}from"@ckeditor/ckeditor5-core";import{ButtonView as U,View as f,addKeyboardHandlingForGrid as C,LabelView as G,ViewCollection as D,FocusCycler as N,createDropdown as P}from"@ckeditor/ckeditor5-ui";import{FocusTracker as k,KeystrokeHandler as b,first as H,logWarning as I}from"@ckeditor/ckeditor5-utils";import{findAttributeRange as L,findAttributeRangeBound as R}from"@ckeditor/ckeditor5-typing";/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class m extends U{styleDefinition;previewView;constructor(t,e){super(t),this.styleDefinition=e,this.previewView=this._createPreview(),this.set({label:e.name,class:"ck-style-grid__button",withText:!0}),this.extendTemplate({attributes:{role:"option"}}),this.children.add(this.previewView,0)}_createPreview(){const t=new f(this.locale);return t.setTemplate({tag:"div",attributes:{class:["ck","ck-reset_all-excluded","ck-style-grid__button__preview","ck-content"],"aria-hidden":"true"},children:[this.styleDefinition.previewTemplate]}),t}}function y(r,{insertAt:t}={}){if(!r||typeof document>"u")return;const e=document.head||document.getElementsByTagName("head")[0],s=document.createElement("style");s.type="text/css",window.litNonce&&s.setAttribute("nonce",window.litNonce),t==="top"&&e.firstChild?e.insertBefore(s,e.firstChild):e.appendChild(s),s.styleSheet?s.styleSheet.cssText=r:s.appendChild(document.createTextNode(r))}y(":root{--ck-style-panel-columns:3}.ck.ck-style-panel .ck-style-grid{display:grid;grid-template-columns:repeat(var(--ck-style-panel-columns),auto);justify-content:start}.ck.ck-style-panel .ck-style-grid .ck-style-grid__button{display:flex;flex-direction:column;justify-content:space-between}.ck.ck-style-panel .ck-style-grid .ck-style-grid__button .ck-style-grid__button__preview{align-content:center;align-items:center;display:flex;flex-basis:100%;flex-grow:1;justify-content:flex-start}:root{--ck-style-panel-button-width:120px;--ck-style-panel-button-height:80px;--ck-style-panel-button-label-background:#f0f0f0;--ck-style-panel-button-hover-label-background:#ebebeb;--ck-style-panel-button-hover-border-color:#b3b3b3}.ck.ck-style-panel .ck-style-grid{column-gap:var(--ck-spacing-large);row-gap:var(--ck-spacing-large)}.ck.ck-style-panel .ck-style-grid .ck-style-grid__button{--ck-color-button-default-hover-background:var(--ck-color-base-background);--ck-color-button-default-active-background:var(--ck-color-base-background);height:var(--ck-style-panel-button-height);padding:0;width:var(--ck-style-panel-button-width)}.ck.ck-style-panel .ck-style-grid .ck-style-grid__button:not(:focus){border:1px solid var(--ck-color-base-border)}.ck.ck-style-panel .ck-style-grid .ck-style-grid__button .ck-button__label{flex-shrink:0;height:22px;line-height:22px;overflow:hidden;padding:0 var(--ck-spacing-medium);text-overflow:ellipsis;width:100%}.ck.ck-style-panel .ck-style-grid .ck-style-grid__button .ck-style-grid__button__preview{background:var(--ck-color-base-background);border:2px solid var(--ck-color-base-background);opacity:.9;overflow:hidden;padding:var(--ck-spacing-medium);width:100%}.ck.ck-style-panel .ck-style-grid .ck-style-grid__button.ck-disabled{--ck-color-button-default-disabled-background:var(--ck-color-base-foreground)}.ck.ck-style-panel .ck-style-grid .ck-style-grid__button.ck-disabled:not(:focus){border-color:var(--ck-style-panel-button-label-background)}.ck.ck-style-panel .ck-style-grid .ck-style-grid__button.ck-disabled .ck-style-grid__button__preview{border-color:var(--ck-color-base-foreground);filter:saturate(.3);opacity:.4}.ck.ck-style-panel .ck-style-grid .ck-style-grid__button.ck-on{border-color:var(--ck-color-base-active)}.ck.ck-style-panel .ck-style-grid .ck-style-grid__button.ck-on .ck-button__label{box-shadow:0 -1px 0 var(--ck-color-base-active);z-index:1}.ck.ck-style-panel .ck-style-grid .ck-style-grid__button.ck-on:hover{border-color:var(--ck-color-base-active-focus)}.ck.ck-style-panel .ck-style-grid .ck-style-grid__button:not(.ck-on) .ck-button__label{background:var(--ck-style-panel-button-label-background)}.ck.ck-style-panel .ck-style-grid .ck-style-grid__button:not(.ck-on):hover .ck-button__label{background:var(--ck-style-panel-button-hover-label-background)}.ck.ck-style-panel .ck-style-grid .ck-style-grid__button:hover:not(.ck-disabled):not(.ck-on){border-color:var(--ck-style-panel-button-hover-border-color)}.ck.ck-style-panel .ck-style-grid .ck-style-grid__button:hover:not(.ck-disabled):not(.ck-on) .ck-style-grid__button__preview{opacity:1}");/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class _ extends f{focusTracker;keystrokes;children;constructor(t,e){super(t),this.focusTracker=new k,this.keystrokes=new b,this.set("activeStyles",[]),this.set("enabledStyles",[]),this.children=this.createCollection(),this.children.delegate("execute").to(this);for(const s of e){const i=new m(t,s);this.children.add(i)}this.on("change:activeStyles",()=>{for(const s of this.children)s.isOn=this.activeStyles.includes(s.styleDefinition.name)}),this.on("change:enabledStyles",()=>{for(const s of this.children)s.isEnabled=this.enabledStyles.includes(s.styleDefinition.name)}),this.setTemplate({tag:"div",attributes:{class:["ck","ck-style-grid"],role:"listbox"},children:this.children})}render(){super.render();for(const t of this.children)this.focusTracker.add(t.element);C({keystrokeHandler:this.keystrokes,focusTracker:this.focusTracker,gridItems:this.children,numberOfColumns:3,uiLanguageDirection:this.locale&&this.locale.uiLanguageDirection}),this.keystrokes.listenTo(this.element)}focus(){this.children.first.focus()}destroy(){super.destroy(),this.focusTracker.destroy(),this.keystrokes.destroy()}}y(".ck.ck-style-panel .ck-style-panel__style-group>.ck-label{margin:var(--ck-spacing-large) 0}.ck.ck-style-panel .ck-style-panel__style-group:first-child>.ck-label{margin-top:0}");/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class p extends f{gridView;labelView;constructor(t,e,s){super(t),this.labelView=new G(t),this.labelView.text=e,this.gridView=new _(t,s),this.setTemplate({tag:"div",attributes:{class:["ck","ck-style-panel__style-group"],role:"group","aria-labelledby":this.labelView.id},children:[this.labelView,this.gridView]})}}y(":root{--ck-style-panel-max-height:470px}.ck.ck-style-panel{max-height:var(--ck-style-panel-max-height);overflow-y:auto;padding:var(--ck-spacing-large)}");/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class S extends f{focusTracker;keystrokes;children;blockStylesGroupView;inlineStylesGroupView;_focusables;_focusCycler;constructor(t,e){super(t);const s=t.t;this.focusTracker=new k,this.keystrokes=new b,this.children=this.createCollection(),this.blockStylesGroupView=new p(t,s("Block styles"),e.block),this.inlineStylesGroupView=new p(t,s("Text styles"),e.inline),this.set("activeStyles",[]),this.set("enabledStyles",[]),this._focusables=new D,this._focusCycler=new N({focusables:this._focusables,focusTracker:this.focusTracker,keystrokeHandler:this.keystrokes,actions:{focusPrevious:["shift + tab"],focusNext:["tab"]}}),e.block.length&&this.children.add(this.blockStylesGroupView),e.inline.length&&this.children.add(this.inlineStylesGroupView),this.blockStylesGroupView.gridView.delegate("execute").to(this),this.inlineStylesGroupView.gridView.delegate("execute").to(this),this.blockStylesGroupView.gridView.bind("activeStyles","enabledStyles").to(this,"activeStyles","enabledStyles"),this.inlineStylesGroupView.gridView.bind("activeStyles","enabledStyles").to(this,"activeStyles","enabledStyles"),this.setTemplate({tag:"div",attributes:{class:["ck","ck-style-panel"]},children:this.children})}render(){super.render(),this._focusables.add(this.blockStylesGroupView.gridView),this._focusables.add(this.inlineStylesGroupView.gridView),this.focusTracker.add(this.blockStylesGroupView.gridView.element),this.focusTracker.add(this.inlineStylesGroupView.gridView.element),this.keystrokes.listenTo(this.element)}focus(){this._focusCycler.focusFirst()}focusLast(){this._focusCycler.focusLast()}}function O(r){return r!==null&&(typeof r=="object"||typeof r=="function")}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/const j=["caption","colgroup","dd","dt","figcaption","legend","li","optgroup","option","rp","rt","summary","tbody","td","tfoot","th","thead","tr"];class d extends h{_htmlSupport;static get pluginName(){return"StyleUtils"}static get isOfficialPlugin(){return!0}constructor(t){super(t),this.decorate("isStyleEnabledForBlock"),this.decorate("isStyleActiveForBlock"),this.decorate("getAffectedBlocks"),this.decorate("isStyleEnabledForInlineSelection"),this.decorate("isStyleActiveForInlineSelection"),this.decorate("getAffectedInlineSelectable"),this.decorate("getStylePreview"),this.decorate("configureGHSDataFilter")}init(){this._htmlSupport=this.editor.plugins.get("GeneralHtmlSupport")}normalizeConfig(t,e=[]){const s={block:[],inline:[]};for(const i of e){const l=[],o=[];for(const n of t.getDefinitionsForView(i.element)){const u="appliesToBlock"in n?n.appliesToBlock:!1;if(n.isBlock||u){if(typeof u=="string")l.push(u);else if(n.isBlock){const a=n;l.push(n.model),a.paragraphLikeModel&&l.push(a.paragraphLikeModel)}}else o.push(n.model)}const c=this.getStylePreview(i,[{text:"AaBbCcDdEeFfGgHhIiJj"}]);l.length?s.block.push({...i,previewTemplate:c,modelElements:l,isBlock:!0}):s.inline.push({...i,previewTemplate:c,ghsAttributes:o})}return s}isStyleEnabledForBlock(t,e){const s=this.editor.model,i=this._htmlSupport.getGhsAttributeNameForElement(t.element);return s.schema.checkAttribute(e,i)?t.modelElements.includes(e.name):!1}isStyleActiveForBlock(t,e){const s=this._htmlSupport.getGhsAttributeNameForElement(t.element),i=e.getAttribute(s);return this.hasAllClasses(i,t.classes)}getAffectedBlocks(t,e){return t.modelElements.includes(e.name)?[e]:null}isStyleEnabledForInlineSelection(t,e){const s=this.editor.model;for(const i of t.ghsAttributes)if(s.schema.checkAttributeInSelection(e,i))return!0;return!1}isStyleActiveForInlineSelection(t,e){for(const s of t.ghsAttributes){const i=this._getValueFromFirstAllowedNode(e,s);if(this.hasAllClasses(i,t.classes))return!0}return!1}getAffectedInlineSelectable(t,e){return e}getStylePreview(t,e){const{element:s,classes:i}=t;return{tag:M(s)?s:"div",attributes:{class:i},children:e}}hasAllClasses(t,e){return O(t)&&z(t)&&e.every(s=>t.classes.includes(s))}configureGHSDataFilter({block:t,inline:e}){const s=this.editor.plugins.get("DataFilter");s.loadAllowedConfig(t.map(w)),s.loadAllowedConfig(e.map(w))}_getValueFromFirstAllowedNode(t,e){const i=this.editor.model.schema;if(t.isCollapsed)return t.getAttribute(e);for(const l of t.getRanges())for(const o of l.getItems())if(i.checkAttribute(o,e))return o.getAttribute(e);return null}}function z(r){return!!r.classes&&Array.isArray(r.classes)}function M(r){return!j.includes(r)}function w({element:r,classes:t}){return{name:r,classes:t}}y(".ck.ck-dropdown.ck-style-dropdown.ck-style-dropdown_multiple-active>.ck-button>.ck-button__label{font-style:italic}");/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class A extends h{static get pluginName(){return"StyleUI"}static get isOfficialPlugin(){return!0}static get requires(){return[d]}init(){const t=this.editor,e=t.plugins.get("DataSchema"),s=t.plugins.get("StyleUtils"),i=t.config.get("style.definitions"),l=s.normalizeConfig(e,i);t.ui.componentFactory.add("style",o=>{const c=o.t,n=P(o),u=t.commands.get("style");return n.once("change:isOpen",()=>{const a=new S(o,l);n.panelView.children.add(a),a.delegate("execute").to(n),a.bind("activeStyles").to(u,"value"),a.bind("enabledStyles").to(u,"enabledStyles")}),n.bind("isEnabled").to(u),n.buttonView.withText=!0,n.buttonView.bind("label").to(u,"value",a=>a.length>1?c("Multiple styles"):a.length===1?a[0]:c("Styles")),n.bind("class").to(u,"value",a=>{const g=["ck-style-dropdown"];return a.length>1&&g.push("ck-style-dropdown_multiple-active"),g.join(" ")}),n.on("execute",a=>{t.execute("style",{styleName:a.source.styleDefinition.name}),t.editing.view.focus()}),n})}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class v extends E{_styleDefinitions;_styleUtils;constructor(t,e){super(t),this.set("value",[]),this.set("enabledStyles",[]),this._styleDefinitions=e,this._styleUtils=this.editor.plugins.get(d)}refresh(){const t=this.editor.model,e=t.document.selection,s=new Set,i=new Set;for(const o of this._styleDefinitions.inline)this._styleUtils.isStyleEnabledForInlineSelection(o,e)&&i.add(o.name),this._styleUtils.isStyleActiveForInlineSelection(o,e)&&s.add(o.name);const l=H(e.getSelectedBlocks())||e.getFirstPosition().parent;if(l){const o=l.getAncestors({includeSelf:!0,parentFirst:!0});for(const c of o){if(c.is("rootElement"))break;for(const n of this._styleDefinitions.block)this._styleUtils.isStyleEnabledForBlock(n,c)&&(i.add(n.name),this._styleUtils.isStyleActiveForBlock(n,c)&&s.add(n.name));if(t.schema.isObject(c))break}}this.enabledStyles=Array.from(i).sort(),this.isEnabled=this.enabledStyles.length>0,this.value=this.isEnabled?Array.from(s).sort():[]}execute({styleName:t,forceValue:e}){if(!this.enabledStyles.includes(t)){I("style-command-executed-with-incorrect-style-name");return}const s=this.editor.model,i=s.document.selection,l=this.editor.plugins.get("GeneralHtmlSupport"),o=[...this._styleDefinitions.inline,...this._styleDefinitions.block],c=o.filter(({name:a})=>this.value.includes(a)),n=o.find(({name:a})=>a==t),u=e===void 0?!this.value.includes(n.name):e;s.change(()=>{let a;$(n)?a=this._findAffectedBlocks(J(i),n):a=[this._styleUtils.getAffectedInlineSelectable(n,i)];for(const g of a)u?l.addModelHtmlClass(n.element,n.classes,g):l.removeModelHtmlClass(n.element,q(c,n),g)})}_findAffectedBlocks(t,e){const s=new Set;for(const i of t){const l=i.getAncestors({includeSelf:!0,parentFirst:!0});for(const o of l){if(o.is("rootElement"))break;const c=this._styleUtils.getAffectedBlocks(e,o);if(c){for(const n of c)s.add(n);break}}}return s}}function q(r,t){return r.reduce((e,s)=>s.name===t.name?e:e.filter(i=>!s.classes.includes(i)),t.classes)}function $(r){return"isBlock"in r}function J(r){const t=Array.from(r.getSelectedBlocks());return t.length?t:[r.getFirstPosition().parent]}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class x extends h{_listUtils;_styleUtils;_htmlSupport;static get pluginName(){return"ListStyleSupport"}static get isOfficialPlugin(){return!0}static get requires(){return[d,"GeneralHtmlSupport"]}init(){const t=this.editor;t.plugins.has("ListEditing")&&(this._styleUtils=t.plugins.get(d),this._listUtils=this.editor.plugins.get("ListUtils"),this._htmlSupport=this.editor.plugins.get("GeneralHtmlSupport"),this.listenTo(this._styleUtils,"isStyleEnabledForBlock",(e,[s,i])=>{this._isStyleEnabledForBlock(s,i)&&(e.return=!0,e.stop())},{priority:"high"}),this.listenTo(this._styleUtils,"isStyleActiveForBlock",(e,[s,i])=>{this._isStyleActiveForBlock(s,i)&&(e.return=!0,e.stop())},{priority:"high"}),this.listenTo(this._styleUtils,"getAffectedBlocks",(e,[s,i])=>{const l=this._getAffectedBlocks(s,i);l&&(e.return=l,e.stop())},{priority:"high"}),this.listenTo(this._styleUtils,"getStylePreview",(e,[s,i])=>{const l=this._getStylePreview(s,i);l&&(e.return=l,e.stop())},{priority:"high"}))}_isStyleEnabledForBlock(t,e){const s=this.editor.model;if(!["ol","ul","li"].includes(t.element)||!this._listUtils.isListItemBlock(e))return!1;const i=this._htmlSupport.getGhsAttributeNameForElement(t.element);if(t.element=="ol"||t.element=="ul"){if(!s.schema.checkAttribute(e,i))return!1;const o=this._listUtils.isNumberedListType(e.getAttribute("listType"))?"ol":"ul";return t.element==o}else return s.schema.checkAttribute(e,i)}_isStyleActiveForBlock(t,e){const s=this._htmlSupport.getGhsAttributeNameForElement(t.element),i=e.getAttribute(s);return this._styleUtils.hasAllClasses(i,t.classes)}_getAffectedBlocks(t,e){return this._isStyleEnabledForBlock(t,e)?t.element=="li"?this._listUtils.expandListBlocksToCompleteItems(e,{withNested:!1}):this._listUtils.expandListBlocksToCompleteList(e):null}_getStylePreview(t,e){const{element:s,classes:i}=t;return s=="ol"||s=="ul"?{tag:s,attributes:{class:i},children:[{tag:"li",children:e}]}:s=="li"?{tag:"ol",children:[{tag:s,attributes:{class:i},children:e}]}:null}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class B extends h{_tableUtils;_styleUtils;static get pluginName(){return"TableStyleSupport"}static get isOfficialPlugin(){return!0}static get requires(){return[d]}init(){const t=this.editor;t.plugins.has("TableEditing")&&(this._styleUtils=t.plugins.get(d),this._tableUtils=this.editor.plugins.get("TableUtils"),this.listenTo(this._styleUtils,"isStyleEnabledForBlock",(e,[s,i])=>{this._isApplicable(s,i)&&(e.return=this._isStyleEnabledForBlock(s,i),e.stop())},{priority:"high"}),this.listenTo(this._styleUtils,"getAffectedBlocks",(e,[s,i])=>{this._isApplicable(s,i)&&(e.return=this._getAffectedBlocks(s,i),e.stop())},{priority:"high"}),this.listenTo(this._styleUtils,"configureGHSDataFilter",(e,[{block:s}])=>{this.editor.plugins.get("DataFilter").loadAllowedConfig(s.filter(l=>l.element=="figcaption").map(l=>({name:"caption",classes:l.classes})))}))}_isApplicable(t,e){return["td","th"].includes(t.element)?e.name=="tableCell":["thead","tbody"].includes(t.element)?e.name=="table":!1}_isStyleEnabledForBlock(t,e){if(["td","th"].includes(t.element)){const s=this._tableUtils.getCellLocation(e),l=e.parent.parent,o=l.getAttribute("headingRows")||0,c=l.getAttribute("headingColumns")||0,n=s.row<o||s.column<c;return t.element=="th"?n:!n}if(["thead","tbody"].includes(t.element)){const s=e.getAttribute("headingRows")||0;return t.element=="thead"?s>0:s<this._tableUtils.getRows(e)}/* istanbul ignore next -- @preserve */return!1}_getAffectedBlocks(t,e){return this._isStyleEnabledForBlock(t,e)?[e]:null}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class V extends h{_styleUtils;_htmlSupport;static get pluginName(){return"LinkStyleSupport"}static get isOfficialPlugin(){return!0}static get requires(){return[d,"GeneralHtmlSupport"]}init(){const t=this.editor;t.plugins.has("LinkEditing")&&(this._styleUtils=t.plugins.get(d),this._htmlSupport=this.editor.plugins.get("GeneralHtmlSupport"),this.listenTo(this._styleUtils,"isStyleEnabledForInlineSelection",(e,[s,i])=>{s.element=="a"&&(e.return=this._isStyleEnabled(s,i),e.stop())},{priority:"high"}),this.listenTo(this._styleUtils,"isStyleActiveForInlineSelection",(e,[s,i])=>{s.element=="a"&&(e.return=this._isStyleActive(s,i),e.stop())},{priority:"high"}),this.listenTo(this._styleUtils,"getAffectedInlineSelectable",(e,[s,i])=>{if(s.element!="a")return;const l=this._getAffectedSelectable(s,i);l&&(e.return=l,e.stop())},{priority:"high"}))}_isStyleEnabled(t,e){const s=this.editor.model;if(e.isCollapsed)return e.hasAttribute("linkHref");for(const i of e.getRanges())for(const l of i.getItems())if((l.is("$textProxy")||s.schema.isInline(l))&&l.hasAttribute("linkHref"))return!0;return!1}_isStyleActive(t,e){const s=this.editor.model,i=this._htmlSupport.getGhsAttributeNameForElement(t.element);if(e.isCollapsed){if(e.hasAttribute("linkHref")){const l=e.getAttribute(i);if(this._styleUtils.hasAllClasses(l,t.classes))return!0}return!1}for(const l of e.getRanges())for(const o of l.getItems())if((o.is("$textProxy")||s.schema.isInline(o))&&o.hasAttribute("linkHref")){const c=o.getAttribute(i);return this._styleUtils.hasAllClasses(c,t.classes)}return!1}_getAffectedSelectable(t,e){const s=this.editor.model;if(e.isCollapsed){const l=e.getAttribute("linkHref");return L(e.getFirstPosition(),"linkHref",l,s)}const i=[];for(const l of e.getRanges()){const o=s.createRange(F(l.start,"linkHref",!0,s),F(l.end,"linkHref",!1,s));for(const c of o.getItems())(c.is("$textProxy")||s.schema.isInline(c))&&c.hasAttribute("linkHref")&&i.push(this.editor.model.createRangeOn(c))}return K(i)}}function F(r,t,e,s){const i=r.textNode||(e?r.nodeAfter:r.nodeBefore);if(!i||!i.hasAttribute(t))return r;const l=i.getAttribute(t);return R(r,t,l,e,s)}function K(r){for(let t=1;t<r.length;t++){const e=r[t-1].getJoined(r[t]);e&&r.splice(--t,2,e)}return r}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class T extends h{static get pluginName(){return"StyleEditing"}static get isOfficialPlugin(){return!0}static get requires(){return["GeneralHtmlSupport",d,x,B,V]}init(){const t=this.editor,e=t.plugins.get("DataSchema"),s=t.plugins.get("StyleUtils"),i=t.config.get("style.definitions"),l=s.normalizeConfig(e,i);t.commands.add("style",new v(t,l)),s.configureGHSDataFilter(l)}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class W extends h{static get pluginName(){return"Style"}static get isOfficialPlugin(){return!0}static get requires(){return[T,A]}}export{V as LinkStyleSupport,x as ListStyleSupport,W as Style,v as StyleCommand,T as StyleEditing,A as StyleUI,d as StyleUtils,B as TableStyleSupport,m as _StyleGridButtonView,_ as _StyleGridView,p as _StyleGroupView,S as _StylePanelView};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
function t(n,{insertAt:e}={}){}t();
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import{Command as q,Plugin as x}from"@ckeditor/ckeditor5-core";import{env as v,EventInfo as ie,count as se,isInsideSurrogatePair as oe,isInsideCombinedSymbol as re,isInsideEmojiSequence as ce,keyCodes as E,ObservableMixin as le}from"@ckeditor/ckeditor5-utils";import{Observer as N,FocusObserver as ae,ViewDocumentDomEventData as A,_tryFixingModelRange as M,ModelLiveRange as ue,BubblingEventInfo as B,MouseObserver as de,TouchObserver as fe}from"@ckeditor/ckeditor5-engine";/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class I{model;limit;_isLocked;_size;_batch=null;_changeCallback;_selectionChangeCallback;constructor(e,t=20){this.model=e,this._size=0,this.limit=t,this._isLocked=!1,this._changeCallback=(n,s)=>{s.isLocal&&s.isUndoable&&s!==this._batch&&this._reset(!0)},this._selectionChangeCallback=()=>{this._reset()},this.model.document.on("change",this._changeCallback),this.model.document.selection.on("change:range",this._selectionChangeCallback),this.model.document.selection.on("change:attribute",this._selectionChangeCallback)}get batch(){return this._batch||(this._batch=this.model.createBatch({isTyping:!0})),this._batch}get size(){return this._size}input(e){this._size+=e,this._size>=this.limit&&this._reset(!0)}get isLocked(){return this._isLocked}lock(){this._isLocked=!0}unlock(){this._isLocked=!1}destroy(){this.model.document.off("change",this._changeCallback),this.model.document.selection.off("change:range",this._selectionChangeCallback),this.model.document.selection.off("change:attribute",this._selectionChangeCallback)}_reset(e=!1){(!this.isLocked||e)&&(this._batch=null,this._size=0)}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class G extends q{_buffer;constructor(e,t){super(e),this._buffer=new I(e.model,t),this._isEnabledBasedOnSelection=!1}get buffer(){return this._buffer}destroy(){super.destroy(),this._buffer.destroy()}execute(e={}){const t=this.editor.model,n=t.document,s=e.text||"",i=s.length;let c=n.selection;if(e.selection?c=e.selection:e.range&&(c=t.createSelection(e.range)),!t.canEditAt(c))return;const r=e.resultRange;t.enqueueChange(this._buffer.batch,l=>{this._buffer.lock();const a=Array.from(n.selection.getAttributes());t.deleteContent(c),s&&t.insertContent(l.createText(s,a),c),r?l.setSelection(r):c.is("documentSelection")||l.setSelection(c),this._buffer.unlock(),this._buffer.input(i)})}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/const $=["insertText","insertReplacementText"],he=[...$,"insertCompositionText"];class me extends N{focusObserver;constructor(e){super(e),this.focusObserver=e.getObserver(ae);const t=v.isAndroid?he:$,n=e.document;n.on("beforeinput",(s,i)=>{if(!this.isEnabled)return;const{data:c,targetRanges:r,inputType:l,domEvent:a,isComposing:u}=i;if(!t.includes(l))return;this.focusObserver.flush();const d=new ie(n,"insertText");n.fire(d,new A(e,a,{text:c,selection:e.createSelection(r),isComposing:u})),d.stop.called&&s.stop()}),v.isAndroid||n.on("compositionend",(s,{data:i,domEvent:c})=>{this.isEnabled&&i&&n.fire("insertText",new A(e,c,{text:i,isComposing:!0}))},{priority:"low"})}observe(){}stopObserving(){}}function ge(o,e,{signal:t,edges:n}={}){let s,i=null;const c=n!=null&&n.includes("leading"),r=n==null||n.includes("trailing"),l=()=>{i!==null&&(o.apply(s,i),s=void 0,i=null)},a=()=>{r&&l(),m()};let u=null;const d=()=>{u!=null&&clearTimeout(u),u=setTimeout(()=>{u=null,a()},e)},h=()=>{u!==null&&(clearTimeout(u),u=null)},m=()=>{h(),s=void 0,i=null},f=()=>{h(),l()},g=function(...b){if(t?.aborted)return;s=this,i=b;const R=u==null;d(),c&&R&&l()};return g.schedule=d,g.cancel=m,g.flush=f,t?.addEventListener("abort",m,{once:!0}),g}function pe(o,e=0,t={}){typeof t!="object"&&(t={});const{leading:n=!1,trailing:s=!0,maxWait:i}=t,c=Array(2);n&&(c[0]="leading"),s&&(c[1]="trailing");let r,l=null;const a=ge(function(...h){r=o.apply(this,h),l=null},e,{edges:c}),u=function(...h){return i!=null&&(l===null&&(l=Date.now()),Date.now()-l>=i)?(r=o.apply(this,h),l=Date.now(),a.cancel(),a.schedule(),r):(a.apply(this,h),r)},d=()=>(a.flush(),r);return u.cancel=a.cancel,u.flush=d,u}function z(o){if(o==null)return"";if(typeof o=="string")return o;if(Array.isArray(o))return o.map(z).join(",");const e=String(o);return e==="0"&&Object.is(Number(o),-0)?"-0":e}function be(o){return o.replace(/[\\^$.*+?()[\]{}|]/g,"\\$&")}function _e(o){return be(z(o))}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class W extends x{_typingQueue;static get pluginName(){return"Input"}static get isOfficialPlugin(){return!0}init(){const e=this.editor,t=e.model,n=e.editing.view,s=e.editing.mapper,i=t.document.selection;this._typingQueue=new ye(e),n.addObserver(me);const c=new G(e,e.config.get("typing.undoStep")||20);e.commands.add("insertText",c),e.commands.add("input",c),this.listenTo(n.document,"beforeinput",()=>{this._typingQueue.flush("next beforeinput")},{priority:"high"}),this.listenTo(n.document,"insertText",(r,l)=>{const{text:a,selection:u}=l;if(n.document.selection.isFake&&u&&n.document.selection.isSimilar(u)&&l.preventDefault(),u&&Array.from(u.getRanges()).some(f=>!f.isCollapsed)&&l.preventDefault(),!c.isEnabled){l.preventDefault();return}let d;u&&(d=Array.from(u.getRanges()).filter(f=>f.root.is("rootElement")).map(f=>s.toModelRange(f)).map(f=>M(f,t.schema)||f)),(!d||!d.length)&&(d=Array.from(i.getRanges()));let h=a;if(v.isAndroid){const f=Array.from(d[0].getItems()).reduce((g,b)=>g+(b.is("$textProxy")?b.data:""),"");if(f&&(f.length<=h.length?h.startsWith(f)&&(h=h.substring(f.length),d[0].start=d[0].start.getShiftedBy(f.length)):f.startsWith(h)&&(d[0].start=d[0].start.getShiftedBy(h.length),h="")),h.length==0&&d[0].isCollapsed)return}const m={text:h,selection:t.createSelection(d)};this._typingQueue.push(m,!!l.isComposing),l.domEvent.defaultPrevented&&this._typingQueue.flush("beforeinput default prevented")}),v.isAndroid?this.listenTo(n.document,"keydown",(r,l)=>{i.isCollapsed||l.keyCode!=229||!n.document.isComposing||U(t,c)}):this.listenTo(n.document,"compositionstart",()=>{i.isCollapsed||U(t,c)},{priority:"high"}),this.listenTo(n.document,"mutations",(r,{mutations:l})=>{if(this._typingQueue.hasAffectedElements())for(const{node:a}of l){const u=Ee(a,s),d=s.toModelElement(u);if(this._typingQueue.isElementAffected(d)){this._typingQueue.flush("mutations");return}}}),this.listenTo(n.document,"compositionend",()=>{this._typingQueue.flush("before composition end")},{priority:"high"}),this.listenTo(n.document,"compositionend",()=>{this._typingQueue.flush("after composition end");const r=[];if(this._typingQueue.hasAffectedElements())for(const l of this._typingQueue.flushAffectedElements()){const a=s.toViewElement(l);a&&r.push({type:"children",node:a})}(r.length||!v.isAndroid)&&n.document.fire("mutations",{mutations:r})},{priority:"lowest"})}destroy(){super.destroy(),this._typingQueue.destroy()}}class ye{editor;flushDebounced=pe(()=>this.flush("timeout"),50);_queue=[];_isComposing=!1;_affectedElements=new Set;constructor(e){this.editor=e}destroy(){for(this.flushDebounced.cancel(),this._affectedElements.clear();this._queue.length;)this.shift()}get length(){return this._queue.length}push(e,t){const n={text:e.text};if(e.selection){n.selectionRanges=[];for(const s of e.selection.getRanges())n.selectionRanges.push(ue.fromRange(s)),this._affectedElements.add(s.start.parent)}this._queue.push(n),this._isComposing||=t,this.flushDebounced()}shift(){const e=this._queue.shift(),t={text:e.text};if(e.selectionRanges){const n=e.selectionRanges.map(s=>ve(s)).filter(s=>!!s);n.length&&(t.selection=this.editor.model.createSelection(n))}return t}flush(e){const t=this.editor,n=t.model,s=t.editing.view;if(this.flushDebounced.cancel(),!this._queue.length)return;const c=t.commands.get("insertText").buffer;n.enqueueChange(c.batch,()=>{for(c.lock();this._queue.length;){const r=this.shift();t.execute("insertText",r)}c.unlock(),this._isComposing||this._affectedElements.clear(),this._isComposing=!1}),s.scrollToTheSelection()}isElementAffected(e){return this._affectedElements.has(e)}hasAffectedElements(){return this._affectedElements.size>0}flushAffectedElements(){const e=Array.from(this._affectedElements);return this._affectedElements.clear(),e}}function U(o,e){if(!e.isEnabled)return;const t=e.buffer;t.lock(),o.enqueueChange(t.batch,()=>{o.deleteContent(o.document.selection)}),t.unlock()}function ve(o){const e=o.toRange();return o.detach(),e.root.rootName=="$graveyard"?null:e}function Ee(o,e){let t=o.is("$text")?o.parent:o;for(;!e.toModelElement(t);)t=t.parent;return t}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class F extends q{direction;_buffer;constructor(e,t){super(e),this.direction=t,this._buffer=new I(e.model,e.config.get("typing.undoStep")),this._isEnabledBasedOnSelection=!1}get buffer(){return this._buffer}execute(e={}){const t=this.editor.model,n=t.document;t.enqueueChange(this._buffer.batch,s=>{this._buffer.lock();const i=s.createSelection(e.selection||n.selection);if(!t.canEditAt(i))return;const c=e.sequence||1,r=i.isCollapsed;if(i.isCollapsed&&t.modifySelection(i,{direction:this.direction,unit:e.unit,treatEmojiAsSingleUnit:!0}),this._shouldEntireContentBeReplacedWithParagraph(c)){this._replaceEntireContentWithParagraph(s);return}if(this._shouldReplaceFirstBlockWithParagraph(i,c)){this.editor.execute("paragraph",{selection:i});return}if(i.isCollapsed)return;let l=0;i.getFirstRange().getMinimalFlatRanges().forEach(a=>{l+=se(a.getWalker({singleCharacters:!0,ignoreElementEnd:!0,shallow:!0}))}),t.deleteContent(i,{doNotResetEntireContent:r,direction:this.direction}),this._buffer.input(l),s.setSelection(i),this._buffer.unlock()})}_shouldEntireContentBeReplacedWithParagraph(e){if(e>1)return!1;const t=this.editor.model,s=t.document.selection,i=t.schema.getLimitElement(s);if(!(s.isCollapsed&&s.containsEntireContent(i))||!t.schema.checkChild(i,"paragraph"))return!1;const r=i.getChild(0);return!(r&&r.is("element","paragraph"))}_replaceEntireContentWithParagraph(e){const t=this.editor.model,s=t.document.selection,i=t.schema.getLimitElement(s),c=e.createElement("paragraph");e.remove(e.createRangeIn(i)),e.insert(c,i),e.setSelection(c,0)}_shouldReplaceFirstBlockWithParagraph(e,t){const n=this.editor.model;if(t>1||this.direction!="backward"||!e.isCollapsed)return!1;const s=e.getFirstPosition(),i=n.schema.getLimitElement(s),c=i.getChild(0);return!(s.parent!=c||!e.containsEntireContent(c)||!n.schema.checkChild(i,"paragraph")||c.name=="paragraph")}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/const Te="character",Q="word",Ce="codePoint",y="selection",T="backward",w="forward",j={deleteContent:{unit:y,direction:T},deleteContentBackward:{unit:Ce,direction:T},deleteWordBackward:{unit:Q,direction:T},deleteHardLineBackward:{unit:y,direction:T},deleteSoftLineBackward:{unit:y,direction:T},deleteContentForward:{unit:Te,direction:w},deleteWordForward:{unit:Q,direction:w},deleteHardLineForward:{unit:y,direction:w},deleteSoftLineForward:{unit:y,direction:w}};class H extends N{constructor(e){super(e);const t=e.document;let n=0;t.on("keydown",()=>{n++}),t.on("keyup",()=>{n=0}),t.on("beforeinput",(s,i)=>{if(!this.isEnabled)return;const{targetRanges:c,domEvent:r,inputType:l}=i,a=j[l];if(!a)return;const u={direction:a.direction,unit:a.unit,sequence:n};u.unit==y&&(u.selectionToRemove=e.createSelection(c[0])),l==="deleteContentBackward"&&(v.isAndroid&&(u.sequence=1),Ae(c)&&(u.unit=y,u.selectionToRemove=e.createSelection(c)));const d=new B(t,"delete",c[0]);t.fire(d,new A(e,r,u)),d.stop.called&&s.stop()}),v.isBlink&&xe(this)}observe(){}stopObserving(){}}function xe(o){const e=o.view,t=e.document;let n=null,s=!1;t.on("keydown",(r,{keyCode:l})=>{n=l,s=!1}),t.on("keyup",(r,{keyCode:l,domEvent:a})=>{const u=t.selection,d=o.isEnabled&&l==n&&i(l)&&!u.isCollapsed&&!s;if(n=null,d){const h=u.getFirstRange(),m=new B(t,"delete",h),f={unit:y,direction:c(l),selectionToRemove:u};t.fire(m,new A(e,a,f))}}),t.on("beforeinput",(r,{inputType:l})=>{const a=j[l];i(n)&&a&&a.direction==c(n)&&(s=!0)},{priority:"high"}),t.on("beforeinput",(r,{inputType:l,data:a})=>{n==E.delete&&l=="insertText"&&a=="\x7F"&&r.stop()},{priority:"high"});function i(r){return r==E.backspace||r==E.delete}function c(r){return r==E.backspace?T:w}}function Ae(o){if(o.length!=1||o[0].isCollapsed)return!1;const e=o[0].getWalker({direction:"backward",singleCharacters:!0,ignoreElementEnd:!0});let t=0;for(const{nextPosition:n,item:s}of e){if(n.parent.is("$text")){const i=n.parent.data,c=n.offset;if(oe(i,c)||re(i,c)||ce(i,c))continue;t++}else(s.is("containerElement")||s.is("emptyElement"))&&t++;if(t>1)return!0}return!1}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class K extends x{_undoOnBackspace;static get pluginName(){return"Delete"}static get isOfficialPlugin(){return!0}init(){const e=this.editor,t=e.editing.view,n=t.document,s=e.model.document;t.addObserver(H),this._undoOnBackspace=!1;const i=new F(e,"forward");e.commands.add("deleteForward",i),e.commands.add("forwardDelete",i),e.commands.add("delete",new F(e,"backward")),this.listenTo(n,"delete",(c,r)=>{n.isComposing||r.preventDefault();const{direction:l,sequence:a,selectionToRemove:u,unit:d}=r,h=l==="forward"?"deleteForward":"delete",m={sequence:a};if(d=="selection"){const f=Array.from(u.getRanges()).map(g=>e.editing.mapper.toModelRange(g)).map(g=>M(g,e.model.schema)||g);m.selection=e.model.createSelection(f)}else m.unit=d;e.execute(h,m),t.scrollToTheSelection()},{priority:"low"}),this.listenTo(n,"keydown",(c,r)=>{if(n.isComposing||r.keyCode!=E.backspace||!s.selection.isCollapsed)return;const l=e.model.schema.getLimitElement(s.selection),a=e.model.createPositionAt(l,0);if(a.isTouching(s.selection.getFirstPosition())){r.preventDefault();const u=e.model.schema.getNearestSelectionRange(a,"forward");if(!u)return;const d=t.createSelection(e.editing.mapper.toViewRange(u)),h=d.getFirstRange(),m=new B(document,"delete",h),f={unit:"selection",direction:"backward",selectionToRemove:d};n.fire(m,new A(t,r.domEvent,f))}}),this.editor.plugins.has("UndoEditing")&&(this.listenTo(n,"delete",(c,r)=>{this._undoOnBackspace&&r.direction=="backward"&&r.sequence==1&&r.unit=="codePoint"&&(this._undoOnBackspace=!1,e.execute("undo"),r.preventDefault(),c.stop())},{context:"$capture"}),this.listenTo(s,"change",()=>{this._undoOnBackspace=!1}))}requestUndoOnBackspace(){this.editor.plugins.has("UndoEditing")&&(this._undoOnBackspace=!0)}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class we extends x{static get requires(){return[W,K]}static get pluginName(){return"Typing"}static get isOfficialPlugin(){return!0}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/function V(o,e){let t=o.start;return{text:Array.from(o.getWalker({ignoreElementEnd:!1})).reduce((s,{item:i})=>i.is("$text")||i.is("$textProxy")?s+i.data:(t=e.createPositionAfter(i),""),""),range:e.createRange(t,o.end)}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class Y extends le(){model;testCallback;_hasMatch;constructor(e,t){super(),this.model=e,this.testCallback=t,this._hasMatch=!1,this.set("isEnabled",!0),this.on("change:isEnabled",()=>{this.isEnabled?this._startListening():(this.stopListening(e.document.selection),this.stopListening(e.document))}),this._startListening()}get hasMatch(){return this._hasMatch}_startListening(){const t=this.model.document;this.listenTo(t.selection,"change:range",(n,{directChange:s})=>{if(s){if(!t.selection.isCollapsed){this.hasMatch&&(this.fire("unmatched"),this._hasMatch=!1);return}this._evaluateTextBeforeSelection("selection")}}),this.listenTo(t,"change:data",(n,s)=>{s.isUndo||!s.isLocal||this._evaluateTextBeforeSelection("data",{batch:s})})}_evaluateTextBeforeSelection(e,t={}){const n=this.model,i=n.document.selection,c=n.createRange(n.createPositionAt(i.focus.parent,0),i.focus),{text:r,range:l}=V(c,n),a=this.testCallback(r);if(!a&&this.hasMatch&&this.fire("unmatched"),this._hasMatch=!!a,a){const u=Object.assign(t,{text:r,range:l});typeof a=="object"&&Object.assign(u,a),this.fire(`matched:${e}`,u)}}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class Se extends x{attributes;_overrideUid;_isNextGravityRestorationSkipped=!1;static get pluginName(){return"TwoStepCaretMovement"}static get isOfficialPlugin(){return!0}constructor(e){super(e),this.attributes=new Set,this._overrideUid=null}init(){const e=this.editor,t=e.model,n=e.editing.view,s=e.locale,i=t.document.selection;this.listenTo(n.document,"arrowKey",(c,r)=>{if(!i.isCollapsed||r.shiftKey||r.altKey||r.ctrlKey)return;const l=r.keyCode==E.arrowright,a=r.keyCode==E.arrowleft;if(!l&&!a)return;const u=s.contentLanguageDirection;let d=!1;u==="ltr"&&l||u==="rtl"&&a?d=this._handleForwardMovement(r):d=this._handleBackwardMovement(r),d===!0&&c.stop()},{context:"$text",priority:"highest"}),this.listenTo(i,"change:range",(c,r)=>{if(this._isNextGravityRestorationSkipped){this._isNextGravityRestorationSkipped=!1;return}this._isGravityOverridden&&(!r.directChange&&p(i.getFirstPosition(),this.attributes)||this._restoreGravity())}),this._enableClickingAfterNode(),this._enableInsertContentSelectionAttributesFixer(),this._handleDeleteContentAfterNode()}registerAttribute(e){this.attributes.add(e)}_handleForwardMovement(e){const t=this.attributes,n=this.editor.model,s=n.document.selection,i=s.getFirstPosition();return this._isGravityOverridden||i.isAtStart&&_(s,t)?!1:p(i,t)?(e&&k(e),_(s,t)&&p(i,t,!0)?S(n,t):this._overrideGravity(),!0):!1}_handleBackwardMovement(e){const t=this.attributes,n=this.editor.model,s=n.document.selection,i=s.getFirstPosition();return this._isGravityOverridden?(e&&k(e),this._restoreGravity(),p(i,t,!0)?S(n,t):O(n,t,i),!0):i.isAtStart?_(s,t)?(e&&k(e),O(n,t,i),!0):!1:!_(s,t)&&p(i,t,!0)?(e&&k(e),O(n,t,i),!0):J(i,t)?i.isAtEnd&&!_(s,t)&&p(i,t)?(e&&k(e),O(n,t,i),!0):(this._isNextGravityRestorationSkipped=!0,this._overrideGravity(),!1):!1}_enableClickingAfterNode(){const e=this.editor,t=e.model,n=t.document.selection,s=e.editing.view.document;e.editing.view.addObserver(de),e.editing.view.addObserver(fe);let i=!1,c=!1;this.listenTo(s,"touchstart",()=>{c=!1,i=!0}),this.listenTo(s,"mousedown",()=>{c=!0}),this.listenTo(s,"selectionChange",()=>{const r=this.attributes;if(!c&&!i||(c=!1,i=!1,!n.isCollapsed)||!_(n,r))return;const l=n.getFirstPosition();p(l,r)&&(l.isAtStart||p(l,r,!0)?S(t,r):this._isGravityOverridden||this._overrideGravity())})}_enableInsertContentSelectionAttributesFixer(){const t=this.editor.model,n=t.document.selection,s=this.attributes;this.listenTo(t,"insertContent",()=>{const i=n.getFirstPosition();_(n,s)&&p(i,s)&&S(t,s)},{priority:"low"})}_handleDeleteContentAfterNode(){const e=this.editor,t=e.model,n=t.document.selection,s=e.editing.view;let i=!1,c=!1;this.listenTo(s.document,"delete",(r,l)=>{i=l.direction==="backward"},{priority:"high"}),this.listenTo(t,"deleteContent",()=>{if(!i)return;const r=n.getFirstPosition();c=_(n,this.attributes)&&!J(r,this.attributes)},{priority:"high"}),this.listenTo(t,"deleteContent",()=>{i&&(i=!1,!c&&e.model.enqueueChange(()=>{const r=n.getFirstPosition();_(n,this.attributes)&&p(r,this.attributes)&&(r.isAtStart||p(r,this.attributes,!0)?S(t,this.attributes):this._isGravityOverridden||this._overrideGravity())}))},{priority:"low"})}get _isGravityOverridden(){return!!this._overrideUid}_overrideGravity(){this._overrideUid=this.editor.model.change(e=>e.overrideSelectionGravity())}_restoreGravity(){this.editor.model.change(e=>{e.restoreSelectionGravity(this._overrideUid),this._overrideUid=null})}}function _(o,e){for(const t of e)if(o.hasAttribute(t))return!0;return!1}function O(o,e,t){const n=t.nodeBefore;o.change(s=>{if(n){const i=[],c=o.schema.isObject(n)&&o.schema.isInline(n);for(const[r,l]of n.getAttributes())o.schema.checkAttribute("$text",r)&&(!c||o.schema.getAttributeProperties(r).copyFromObject!==!1)&&i.push([r,l]);s.setSelectionAttribute(i)}else s.removeSelectionAttribute(e)})}function S(o,e){o.change(t=>{t.removeSelectionAttribute(e)})}function k(o){o.preventDefault()}function J(o,e){const t=o.getShiftedBy(-1);return p(t,e)}function p(o,e,t=!1){const{nodeBefore:n,nodeAfter:s}=o;for(const i of e){const c=n?n.getAttribute(i):void 0,r=s?s.getAttribute(i):void 0;if(!(t&&(c===void 0||r===void 0))&&r!==c)return!0}return!1}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/const X={copyright:{from:"(c)",to:"\xA9"},registeredTrademark:{from:"(r)",to:"\xAE"},trademark:{from:"(tm)",to:"\u2122"},oneHalf:{from:/(^|[^/a-z0-9])(1\/2)([^/a-z0-9])$/i,to:[null,"\xBD",null]},oneThird:{from:/(^|[^/a-z0-9])(1\/3)([^/a-z0-9])$/i,to:[null,"\u2153",null]},twoThirds:{from:/(^|[^/a-z0-9])(2\/3)([^/a-z0-9])$/i,to:[null,"\u2154",null]},oneForth:{from:/(^|[^/a-z0-9])(1\/4)([^/a-z0-9])$/i,to:[null,"\xBC",null]},threeQuarters:{from:/(^|[^/a-z0-9])(3\/4)([^/a-z0-9])$/i,to:[null,"\xBE",null]},lessThanOrEqual:{from:"<=",to:"\u2264"},greaterThanOrEqual:{from:">=",to:"\u2265"},notEqual:{from:"!=",to:"\u2260"},arrowLeft:{from:"<-",to:"\u2190"},arrowRight:{from:"->",to:"\u2192"},horizontalEllipsis:{from:"...",to:"\u2026"},enDash:{from:/(^| )(--)( )$/,to:[null,"\u2013",null]},emDash:{from:/(^| )(---)( )$/,to:[null,"\u2014",null]},quotesPrimary:{from:C('"'),to:[null,"\u201C",null,"\u201D"]},quotesSecondary:{from:C("'"),to:[null,"\u2018",null,"\u2019"]},quotesPrimaryEnGb:{from:C("'"),to:[null,"\u2018",null,"\u2019"]},quotesSecondaryEnGb:{from:C('"'),to:[null,"\u201C",null,"\u201D"]},quotesPrimaryPl:{from:C('"'),to:[null,"\u201E",null,"\u201D"]},quotesSecondaryPl:{from:C("'"),to:[null,"\u201A",null,"\u2019"]}},Z={symbols:["copyright","registeredTrademark","trademark"],mathematical:["oneHalf","oneThird","twoThirds","oneForth","threeQuarters","lessThanOrEqual","greaterThanOrEqual","notEqual","arrowLeft","arrowRight"],typography:["horizontalEllipsis","enDash","emDash"],quotes:["quotesPrimary","quotesSecondary"]},ke=["symbols","mathematical","typography","quotes"];class Re extends x{static get requires(){return["Delete","Input"]}static get pluginName(){return"TextTransformation"}static get isOfficialPlugin(){return!0}constructor(e){super(e),e.config.define("typing",{transformations:{include:ke}})}init(){const t=this.editor.model.document.selection;t.on("change:range",()=>{const n=t.anchor,s=!!n&&n.parent.is("element","codeBlock"),i=t.hasAttribute("code");this.isEnabled=!(s||i)}),this._enableTransformationWatchers()}_enableTransformationWatchers(){const e=this.editor,t=e.model,n=e.plugins.get("Delete"),s=Be(e.config.get("typing.transformations")),i=r=>{for(const l of s)if(l.from.test(r))return{normalizedTransformation:l}},c=new Y(e.model,i);c.on("matched:data",(r,l)=>{if(!l.batch.isTyping)return;const{from:a,to:u}=l.normalizedTransformation,d=a.exec(l.text),h=u(d.slice(1)),m=l.range;let f=d.index;t.enqueueChange(g=>{for(let b=1;b<d.length;b++){const R=d[b],D=h[b-1];if(D==null){f+=R.length;continue}const P=m.start.getShiftedBy(f),te=t.createRange(P,P.getShiftedBy(R.length)),ne=Pe(P);t.insertContent(g.createText(D,ne),te),f+=D.length}t.enqueueChange(()=>{n.requestUndoOnBackspace()})})}),c.bind("isEnabled").to(this)}}function Oe(o){return typeof o=="string"?new RegExp(`(${_e(o)})$`):o}function De(o){return typeof o=="string"?()=>[o]:o instanceof Array?()=>o:o}function Pe(o){return(o.textNode?o.textNode:o.nodeAfter).getAttributes()}function C(o){return new RegExp(`(^|\\s)(${o})([^${o}]*)(${o})$`)}function Be(o){const e=o.extra||[],t=o.remove||[],n=i=>!t.includes(i),s=o.include.concat(e).filter(n);return Ie(s).filter(n).map(i=>typeof i=="string"&&X[i]?X[i]:i).filter(i=>typeof i=="object").map(i=>({from:Oe(i.from),to:De(i.to)}))}function Ie(o){const e=new Set;for(const t of o)if(typeof t=="string"&&Z[t])for(const n of Z[t])e.add(n);else e.add(t);return Array.from(e)}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/function ee(o,e,t,n){return n.createRange(L(o,e,t,!0,n),L(o,e,t,!1,n))}function L(o,e,t,n,s){let i=o.textNode||(n?o.nodeBefore:o.nodeAfter),c=null;for(;i&&i.getAttribute(e)==t;)c=i,i=n?i.previousSibling:i.nextSibling;return c?s.createPositionAt(c,n?"before":"after"):o}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/function Fe(o,e,t,n){const s=o.editing.view,i=new Set;s.document.registerPostFixer(c=>{const r=o.model.document.selection;let l=!1;if(r.hasAttribute(e)){const a=ee(r.getFirstPosition(),e,r.getAttribute(e),o.model),u=o.editing.mapper.toViewRange(a);for(const d of u.getItems())d.is("element",t)&&!d.hasClass(n)&&(c.addClass(n,d),i.add(d),l=!0)}return l}),o.conversion.for("editingDowncast").add(c=>{c.on("insert",r,{priority:"highest"}),c.on("remove",r,{priority:"highest"}),c.on("attribute",r,{priority:"highest"}),c.on("selection",r,{priority:"highest"});function r(){s.change(l=>{for(const a of i.values())l.removeClass(n,a),i.delete(a)})}})}export{K as Delete,F as DeleteCommand,W as Input,G as InsertTextCommand,Re as TextTransformation,Y as TextWatcher,Se as TwoStepCaretMovement,we as Typing,I as TypingChangeBuffer,H as _DeleteObserver,ee as findAttributeRange,L as findAttributeRangeBound,V as getLastTextLine,Fe as inlineHighlight};
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import{Command as R,Plugin as u}from"@ckeditor/ckeditor5-core";import{transformOperationSets as k,NoOperation as C}from"@ckeditor/ckeditor5-engine";import{ButtonView as T,MenuBarMenuListItemButtonView as O}from"@ckeditor/ckeditor5-ui";import{IconUndo as f,IconRedo as g}from"@ckeditor/ckeditor5-icons";/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class m extends R{_stack=[];_createdBatches=new WeakSet;constructor(t){super(t),this.refresh(),this._isEnabledBasedOnSelection=!1,this.listenTo(t.data,"set",(e,o)=>{o[1]={...o[1]};const s=o[1];s.batchType||(s.batchType={isUndoable:!1})},{priority:"high"}),this.listenTo(t.data,"set",(e,o)=>{o[1].batchType.isUndoable||this.clearStack()})}refresh(){this.isEnabled=this._stack.length>0}get createdBatches(){return this._createdBatches}addBatch(t){const e=this.editor.model.document.selection,o={ranges:e.hasOwnRange?Array.from(e.getRanges()):[],isBackward:e.isBackward};this._stack.push({batch:t,selection:o}),this.refresh()}clearStack(){this._stack=[],this.refresh()}_restoreSelection(t,e,o){const s=this.editor.model,i=s.document,n=[],d=t.map(r=>r.getTransformedByOperations(o)),h=d.flat();for(const r of d){const l=r.filter(c=>c.root!=i.graveyard).filter(c=>!w(c,h));l.length&&(U(l),n.push(l[0]))}n.length&&s.change(r=>{r.setSelection(n,{backward:e})})}_undo(t,e){const o=this.editor.model,s=o.document;this._createdBatches.add(e);const i=t.operations.slice().filter(n=>n.isDocumentOperation);i.reverse();for(const n of i){const d=n.baseVersion+1,h=Array.from(s.history.getOperations(d)),l=k([n.getReversed()],h,{useRelations:!0,document:this.editor.model.document,padWithNoOps:!1,forceWeakRemove:!0}).operationsA;for(let c of l){const p=c.affectedSelectable;p&&!o.canEditAt(p)&&(c=new C(c.baseVersion)),e.addOperation(c),o.applyOperation(c),s.history.setOperationAsUndone(n,c)}}}}function U(a){a.sort((t,e)=>t.start.isBefore(e.start)?-1:1);for(let t=1;t<a.length;t++){const o=a[t-1].getJoined(a[t],!0);o&&(t--,a.splice(t,2,o))}}function w(a,t){return t.some(e=>e!==a&&e.containsRange(a,!0))}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class _ extends m{execute(t=null){const e=t?this._stack.findIndex(i=>i.batch==t):this._stack.length-1,o=this._stack.splice(e,1)[0],s=this.editor.model.createBatch({isUndo:!0});this.editor.model.enqueueChange(s,()=>{this._undo(o.batch,s);const i=this.editor.model.document.history.getOperations(o.batch.baseVersion);this._restoreSelection(o.selection.ranges,o.selection.isBackward,i)}),this.fire("revert",o.batch,s),this.refresh()}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class B extends m{execute(){const t=this._stack.pop(),e=this.editor.model.createBatch({isUndo:!0});this.editor.model.enqueueChange(e,()=>{const s=t.batch.operations[t.batch.operations.length-1].baseVersion+1,i=this.editor.model.document.history.getOperations(s);this._restoreSelection(t.selection.ranges,t.selection.isBackward,i),this._undo(t.batch,e)}),this.fire("revert",t.batch,e),this.refresh()}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class b extends u{_undoCommand;_redoCommand;_batchRegistry=new WeakSet;static get pluginName(){return"UndoEditing"}static get isOfficialPlugin(){return!0}init(){const t=this.editor,e=t.t;this._undoCommand=new _(t),this._redoCommand=new B(t),t.commands.add("undo",this._undoCommand),t.commands.add("redo",this._redoCommand),this.listenTo(t.model,"applyOperation",(o,s)=>{const i=s[0];if(!i.isDocumentOperation)return;const n=i.batch,d=this._redoCommand.createdBatches.has(n),h=this._undoCommand.createdBatches.has(n);this._batchRegistry.has(n)||(this._batchRegistry.add(n),n.isUndoable&&(d?this._undoCommand.addBatch(n):h||(this._undoCommand.addBatch(n),this._redoCommand.clearStack())))},{priority:"highest"}),this.listenTo(this._undoCommand,"revert",(o,s,i)=>{this._redoCommand.addBatch(i)}),t.keystrokes.set("CTRL+Z","undo"),t.keystrokes.set("CTRL+Y","redo"),t.keystrokes.set("CTRL+SHIFT+Z","redo"),t.accessibility.addKeystrokeInfos({keystrokes:[{label:e("Undo"),keystroke:"CTRL+Z"},{label:e("Redo"),keystroke:[["CTRL+Y"],["CTRL+SHIFT+Z"]]}]})}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class y extends u{static get pluginName(){return"UndoUI"}static get isOfficialPlugin(){return!0}init(){const t=this.editor,e=t.locale,o=t.t,s=e.uiLanguageDirection=="ltr"?f:g,i=e.uiLanguageDirection=="ltr"?g:f;this._addButtonsToFactory("undo",o("Undo"),"CTRL+Z",s),this._addButtonsToFactory("redo",o("Redo"),"CTRL+Y",i)}_addButtonsToFactory(t,e,o,s){const i=this.editor;i.ui.componentFactory.add(t,()=>{const n=this._createButton(T,t,e,o,s);return n.set({tooltip:!0}),n}),i.ui.componentFactory.add("menuBar:"+t,()=>this._createButton(O,t,e,o,s))}_createButton(t,e,o,s,i){const n=this.editor,d=n.locale,h=n.commands.get(e),r=new t(d);return r.set({label:o,icon:i,keystroke:s}),r.bind("isEnabled").to(h,"isEnabled"),this.listenTo(r,"execute",()=>{n.execute(e),n.editing.view.focus()}),r}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class S extends u{static get requires(){return[b,y]}static get pluginName(){return"Undo"}static get isOfficialPlugin(){return!0}}export{B as RedoCommand,S as Undo,_ as UndoCommand,b as UndoEditing,m as UndoRedoBaseCommand,y as UndoUI};
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import{Plugin as n,PendingActions as u}from"@ckeditor/ckeditor5-core";import{ObservableMixin as h,Collection as m,logWarning as p,uid as w,CKEditorError as c}from"@ckeditor/ckeditor5-utils";/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class g extends h(){total;_reader;_data;constructor(){super();const e=new window.FileReader;this._reader=e,this._data=void 0,this.set("loaded",0),e.onprogress=t=>{this.loaded=t.loaded}}get error(){return this._reader.error}get data(){return this._data}read(e){const t=this._reader;return this.total=e.size,new Promise((s,r)=>{t.onload=()=>{const a=t.result;this._data=a,s(a)},t.onerror=()=>{r("error")},t.onabort=()=>{r("aborted")},this._reader.readAsDataURL(e)})}abort(){this._reader.abort()}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class d extends n{loaders=new m;_loadersMap=new Map;_pendingAction=null;static get pluginName(){return"FileRepository"}static get isOfficialPlugin(){return!0}static get requires(){return[u]}init(){this.loaders.on("change",()=>this._updatePendingAction()),this.set("uploaded",0),this.set("uploadTotal",null),this.bind("uploadedPercent").to(this,"uploaded",this,"uploadTotal",(e,t)=>t?e/t*100:0)}getLoader(e){return this._loadersMap.get(e)||null}createLoader(e){if(!this.createUploadAdapter)return p("filerepository-no-upload-adapter"),null;const t=new f(Promise.resolve(e),this.createUploadAdapter);return this.loaders.add(t),this._loadersMap.set(e,t),e instanceof Promise&&t.file.then(s=>{this._loadersMap.set(s,t)}).catch(()=>{}),t.on("change:uploaded",()=>{let s=0;for(const r of this.loaders)s+=r.uploaded;this.uploaded=s}),t.on("change:uploadTotal",()=>{let s=0;for(const r of this.loaders)r.uploadTotal&&(s+=r.uploadTotal);this.uploadTotal=s}),t}destroyLoader(e){const t=e instanceof f?e:this.getLoader(e);t._destroy(),this.loaders.remove(t),this._loadersMap.forEach((s,r)=>{s===t&&this._loadersMap.delete(r)})}_updatePendingAction(){const e=this.editor.plugins.get(u);if(this.loaders.length){if(!this._pendingAction){const t=this.editor.t,s=r=>`${t("Upload in progress")} ${parseInt(r)}%.`;this._pendingAction=e.add(s(this.uploadedPercent)),this._pendingAction.bind("message").to(this,"uploadedPercent",s)}}else e.remove(this._pendingAction),this._pendingAction=null}}class f extends h(){id;_filePromiseWrapper;_adapter;_reader;constructor(e,t){super(),this.id=w(),this._filePromiseWrapper=this._createFilePromiseWrapper(e),this._adapter=t(this),this._reader=new g,this.set("status","idle"),this.set("uploaded",0),this.set("uploadTotal",null),this.bind("uploadedPercent").to(this,"uploaded",this,"uploadTotal",(s,r)=>r?s/r*100:0),this.set("uploadResponse",null)}get file(){return this._filePromiseWrapper?this._filePromiseWrapper.promise.then(e=>this._filePromiseWrapper?e:null):Promise.resolve(null)}get data(){return this._reader.data}read(){if(this.status!="idle")throw new c("filerepository-read-wrong-status",this);return this.status="reading",this.file.then(e=>this._reader.read(e)).then(e=>{if(this.status!=="reading")throw this.status;return this.status="idle",e}).catch(e=>{throw e==="aborted"?(this.status="aborted","aborted"):(this.status="error",this._reader.error?this._reader.error:e)})}upload(){if(this.status!="idle")throw new c("filerepository-upload-wrong-status",this);return this.status="uploading",this.file.then(()=>this._adapter.upload()).then(e=>(this.uploadResponse=e,this.status="idle",e)).catch(e=>{throw this.status==="aborted"?"aborted":(this.status="error",e)})}abort(){const e=this.status;this.status="aborted",this._filePromiseWrapper.isFulfilled?e=="reading"?this._reader.abort():e=="uploading"&&this._adapter.abort&&this._adapter.abort():(this._filePromiseWrapper.promise.catch(()=>{}),this._filePromiseWrapper.rejecter("aborted")),this._destroy()}_destroy(){this._filePromiseWrapper=void 0,this._reader=void 0,this._adapter=void 0,this.uploadResponse=void 0}_createFilePromiseWrapper(e){const t={};return t.promise=new Promise((s,r)=>{t.rejecter=r,t.isFulfilled=!1,e.then(a=>{t.isFulfilled=!0,s(a)}).catch(a=>{t.isFulfilled=!0,r(a)})}),t}}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class P extends n{static get requires(){return[d]}static get pluginName(){return"Base64UploadAdapter"}static get licenseFeatureCode(){return"B64A"}static get isOfficialPlugin(){return!0}static get isPremiumPlugin(){return!0}init(){this.editor.plugins.get(d).createUploadAdapter=e=>new b(e)}}let b=class{loader;reader;constructor(e){this.loader=e}upload(){return new Promise((e,t)=>{const s=this.reader=new window.FileReader;s.addEventListener("load",()=>{e({default:s.result})}),s.addEventListener("error",r=>{t(r)}),s.addEventListener("abort",()=>{t()}),this.loader.file.then(r=>{s.readAsDataURL(r)})})}abort(){this.reader.abort()}};/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class A extends n{static get requires(){return[d]}static get pluginName(){return"SimpleUploadAdapter"}static get licenseFeatureCode(){return"SUA"}static get isOfficialPlugin(){return!0}static get isPremiumPlugin(){return!0}init(){const e=this.editor.config.get("simpleUpload");if(e){if(!e.uploadUrl){p("simple-upload-adapter-missing-uploadurl");return}this.editor.plugins.get(d).createUploadAdapter=t=>new x(t,e)}}}class x{loader;options;xhr;constructor(e,t){this.loader=e,this.options=t}upload(){return this.loader.file.then(e=>new Promise((t,s)=>{this._initRequest(),this._initListeners(t,s,e),this._sendRequest(e)}))}abort(){this.xhr&&this.xhr.abort()}_initRequest(){const e=this.xhr=new XMLHttpRequest;e.open("POST",this.options.uploadUrl,!0),e.responseType="json"}_initListeners(e,t,s){const r=this.xhr,a=this.loader,l=`Couldn't upload file: ${s.name}.`;r.addEventListener("error",()=>t(l)),r.addEventListener("abort",()=>t()),r.addEventListener("load",()=>{const i=r.response;if(!i||i.error)return t(i&&i.error&&i.error.message?i.error.message:l);const _=i.url?{default:i.url}:i.urls;e({...i,urls:_})});/* istanbul ignore else -- @preserve */r.upload&&r.upload.addEventListener("progress",i=>{i.lengthComputable&&(a.uploadTotal=i.total,a.uploaded=i.loaded)})}_sendRequest(e){let t=this.options.headers||{};typeof t=="function"&&(t=t(e));const s=this.options.withCredentials||!1;for(const a of Object.keys(t))this.xhr.setRequestHeader(a,t[a]);this.xhr.withCredentials=s;const r=new FormData;r.append("upload",e),this.xhr.send(r)}}export{P as Base64UploadAdapter,g as FileReader,d as FileRepository,A as SimpleUploadAdapter};
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import{Plugin as b}from"@ckeditor/ckeditor5-core";import{Template as y,View as x}from"@ckeditor/ckeditor5-ui";import{env as T}from"@ckeditor/ckeditor5-utils";/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/function p(e){if(e.is("$text")||e.is("$textProxy"))return e.data;const t=e;let n="",i=null;for(const r of t.getChildren()){const o=p(r);i&&i.is("element")&&(n+=`
|
||||||
|
`),n+=o,i=r}return n}function C(e){return typeof e=="object"&&e!==null}function O(e,t,{signal:n,edges:i}={}){let r,o=null;const u=i!=null&&i.includes("leading"),c=i==null||i.includes("trailing"),l=()=>{o!==null&&(e.apply(r,o),r=void 0,o=null)},a=()=>{c&&l(),g()};let s=null;const h=()=>{s!=null&&clearTimeout(s),s=setTimeout(()=>{s=null,a()},t)},d=()=>{s!==null&&(clearTimeout(s),s=null)},g=()=>{d(),r=void 0,o=null},_=()=>{d(),l()},f=function(...w){if(n?.aborted)return;r=this,o=w;const m=s==null;h(),u&&m&&l()};return f.schedule=h,f.cancel=g,f.flush=_,n?.addEventListener("abort",g,{once:!0}),f}function P(e,t=0,n={}){typeof n!="object"&&(n={});const{leading:i=!1,trailing:r=!0,maxWait:o}=n,u=Array(2);i&&(u[0]="leading"),r&&(u[1]="trailing");let c,l=null;const a=O(function(...d){c=e.apply(this,d),l=null},t,{edges:u}),s=function(...d){return o!=null&&(l===null&&(l=Date.now()),Date.now()-l>=o)?(c=e.apply(this,d),l=Date.now(),a.cancel(),a.schedule(),c):(a.apply(this,d),c)},h=()=>(a.flush(),c);return s.cancel=a.cancel,s.flush=h,s}function W(e,t=0,n={}){typeof n!="object"&&(n={});const{leading:i=!0,trailing:r=!0}=n;return P(e,t,{leading:i,trailing:r,maxWait:t})}function S(e){if(typeof e!="object"||e==null)return!1;if(Object.getPrototypeOf(e)===null)return!0;if(Object.prototype.toString.call(e)!=="[object Object]"){const n=e[Symbol.toStringTag];return n==null||!Object.getOwnPropertyDescriptor(e,Symbol.toStringTag)?.writable?!1:e.toString()===`[object ${n}]`}let t=e;for(;Object.getPrototypeOf(t)!==null;)t=Object.getPrototypeOf(t);return Object.getPrototypeOf(e)===t}function V(e){return C(e)&&e.nodeType===1&&!S(e)}/**
|
||||||
|
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
||||||
|
*/class E extends b{_config;_outputView;_wordsMatchRegExp;constructor(t){super(t),this.set("characters",0),this.set("words",0),Object.defineProperties(this,{characters:{get(){return this.characters=this._getCharacters(this._getText())}},words:{get(){return this.words=this._getWords(this._getText())}}}),this.set("_wordsLabel",void 0),this.set("_charactersLabel",void 0),this._config=t.config.get("wordCount")||{},this._outputView=void 0,this._wordsMatchRegExp=T.features.isRegExpUnicodePropertySupported?new RegExp("([\\p{L}\\p{N}]+\\S?)+","gu"):/([a-zA-Z0-9À-ž]+\S?)+/gu}static get pluginName(){return"WordCount"}static get licenseFeatureCode(){return"WC"}static get isOfficialPlugin(){return!0}static get isPremiumPlugin(){return!0}init(){this.editor.model.document.on("change:data",W(this._refreshStats.bind(this),250)),typeof this._config.onUpdate=="function"&&this.on("update",(n,i)=>{this._config.onUpdate(i)}),V(this._config.container)&&this._config.container.appendChild(this.wordCountContainer)}destroy(){this._outputView&&(this._outputView.element.remove(),this._outputView.destroy()),super.destroy()}get wordCountContainer(){const t=this.editor,n=t.t,i=t.config.get("wordCount.displayWords"),r=t.config.get("wordCount.displayCharacters"),o=y.bind(this,this),u=[];return this._outputView||(this._outputView=new x,(i||i===void 0)&&(this.bind("_wordsLabel").to(this,"words",c=>n("Words: %0",c)),u.push({tag:"div",children:[{text:[o.to("_wordsLabel")]}],attributes:{class:"ck-word-count__words"}})),(r||r===void 0)&&(this.bind("_charactersLabel").to(this,"characters",c=>n("Characters: %0",c)),u.push({tag:"div",children:[{text:[o.to("_charactersLabel")]}],attributes:{class:"ck-word-count__characters"}})),this._outputView.setTemplate({tag:"div",attributes:{class:["ck","ck-word-count"]},children:u}),this._outputView.render()),this._outputView.element}_getText(){let t="";for(const n of this.editor.model.document.getRoots())t!==""&&(t+=`
|
||||||
|
`),t+=p(n);return t}_getCharacters(t){return t.replace(/\n/g,"").length}_getWords(t){return(t.match(this._wordsMatchRegExp)||[]).length}_refreshStats(){const t=this._getText(),n=this.words=this._getWords(t),i=this.characters=this._getCharacters(t);this.fire("update",{words:n,characters:i})}}export{E as WordCount,p as _modelElementToPlainText};
|
||||||